feat: secret variables in environments (#3779)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
This commit is contained in:
@@ -2,22 +2,38 @@ import * as E from "fp-ts/Either"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import { InferredEntity, createVersionedEntity } from "verzod"
|
||||
|
||||
import { z } from "zod"
|
||||
|
||||
import V0_VERSION from "./v/0"
|
||||
import V1_VERSION from "./v/1"
|
||||
|
||||
const versionedObject = z.object({
|
||||
v: z.number(),
|
||||
})
|
||||
|
||||
export const Environment = createVersionedEntity({
|
||||
latestVersion: 0,
|
||||
latestVersion: 1,
|
||||
versionMap: {
|
||||
0: V0_VERSION
|
||||
0: V0_VERSION,
|
||||
1: V1_VERSION,
|
||||
},
|
||||
getVersion(data) {
|
||||
const versionCheck = versionedObject.safeParse(data)
|
||||
|
||||
if (versionCheck.success) return versionCheck.data.v
|
||||
|
||||
// For V0 we have to check the schema
|
||||
const result = V0_VERSION.schema.safeParse(data)
|
||||
return result.success ? 0 : null
|
||||
},
|
||||
getVersion(x) {
|
||||
return V0_VERSION.schema.safeParse(x).success
|
||||
? 0
|
||||
: null
|
||||
}
|
||||
})
|
||||
|
||||
export type Environment = InferredEntity<typeof Environment>
|
||||
|
||||
export type EnvironmentVariable = InferredEntity<
|
||||
typeof Environment
|
||||
>["variables"][number]
|
||||
|
||||
const REGEX_ENV_VAR = /<<([^>]*)>>/g // "<<myVariable>>"
|
||||
|
||||
/**
|
||||
@@ -31,6 +47,8 @@ const ENV_MAX_EXPAND_LIMIT = 10
|
||||
*/
|
||||
const ENV_EXPAND_LOOP = "ENV_EXPAND_LOOP" as const
|
||||
|
||||
export const EnvironmentSchemaVersion = 1
|
||||
|
||||
export function parseBodyEnvVariablesE(
|
||||
body: string,
|
||||
env: Environment["variables"]
|
||||
@@ -43,7 +61,11 @@ export function parseBodyEnvVariablesE(
|
||||
const found = env.find(
|
||||
(envVar) => envVar.key === key.replace(/[<>]/g, "")
|
||||
)
|
||||
return found ? found.value : key
|
||||
|
||||
if (found && "value" in found) {
|
||||
return found.value
|
||||
}
|
||||
return key
|
||||
})
|
||||
|
||||
depth++
|
||||
@@ -68,7 +90,10 @@ export const parseBodyEnvVariables = (
|
||||
|
||||
export function parseTemplateStringE(
|
||||
str: string,
|
||||
variables: Environment["variables"]
|
||||
variables:
|
||||
| Environment["variables"]
|
||||
| { secret: true; value: string; key: string }[],
|
||||
maskValue = false
|
||||
) {
|
||||
if (!variables || !str) {
|
||||
return E.right(str)
|
||||
@@ -78,10 +103,21 @@ export function parseTemplateStringE(
|
||||
let depth = 0
|
||||
|
||||
while (result.match(REGEX_ENV_VAR) != null && depth <= ENV_MAX_EXPAND_LIMIT) {
|
||||
result = decodeURI(encodeURI(result)).replace(
|
||||
REGEX_ENV_VAR,
|
||||
(_, p1) => variables.find((x) => x.key === p1)?.value || ""
|
||||
)
|
||||
result = decodeURI(encodeURI(result)).replace(REGEX_ENV_VAR, (_, p1) => {
|
||||
const variable = variables.find((x) => x && x.key === p1)
|
||||
if (variable && "value" in variable) {
|
||||
// Mask the value if it is a secret and explicitly specified
|
||||
if (variable.secret && maskValue) {
|
||||
return "*".repeat(
|
||||
(variable as { secret: true; value: string; key: string }).value
|
||||
.length
|
||||
)
|
||||
}
|
||||
return variable.value
|
||||
}
|
||||
|
||||
return ""
|
||||
})
|
||||
depth++
|
||||
}
|
||||
|
||||
@@ -90,14 +126,52 @@ export function parseTemplateStringE(
|
||||
: E.right(result)
|
||||
}
|
||||
|
||||
export type NonSecretEnvironmentVariable = Extract<
|
||||
EnvironmentVariable,
|
||||
{ secret: false }
|
||||
>
|
||||
|
||||
export type NonSecretEnvironment = Omit<Environment, "variables"> & {
|
||||
variables: NonSecretEnvironmentVariable[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `parseTemplateStringE` instead
|
||||
*/
|
||||
export const parseTemplateString = (
|
||||
str: string,
|
||||
variables: Environment["variables"]
|
||||
variables:
|
||||
| Environment["variables"]
|
||||
| { secret: true; value: string; key: string }[],
|
||||
maskValue = false
|
||||
) =>
|
||||
pipe(
|
||||
parseTemplateStringE(str, variables),
|
||||
parseTemplateStringE(str, variables, maskValue),
|
||||
E.getOrElse(() => str)
|
||||
)
|
||||
|
||||
export const translateToNewEnvironmentVariables = (
|
||||
x: any
|
||||
): Environment["variables"][number] => {
|
||||
return {
|
||||
key: x.key,
|
||||
value: x.value,
|
||||
secret: false,
|
||||
}
|
||||
}
|
||||
|
||||
export const translateToNewEnvironment = (x: any): Environment => {
|
||||
if (x.v && x.v === EnvironmentSchemaVersion) return x
|
||||
|
||||
// Legacy
|
||||
const id = x.id ?? ""
|
||||
const name = x.name ?? "Untitled"
|
||||
const variables = (x.variables ?? []).map(translateToNewEnvironmentVariables)
|
||||
|
||||
return {
|
||||
v: EnvironmentSchemaVersion,
|
||||
id,
|
||||
name,
|
||||
variables,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user