parent
f8ba5b32c9
commit
d495d7b222
3 changed files with 89 additions and 19 deletions
|
@ -20,8 +20,8 @@ app.use("/admin", admin)
|
||||||
app.use("/health", health)
|
app.use("/health", health)
|
||||||
|
|
||||||
app.get("/welcome", secretKey, (req, res) => {
|
app.get("/welcome", secretKey, (req, res) => {
|
||||||
const introContent = process.env.WELCOME_CONTENT
|
const introContent = config.welcome_content
|
||||||
const introTitle = process.env.WELCOME_TITLE
|
const introTitle = config.welcome_title
|
||||||
if (!introContent || !introTitle) {
|
if (!introContent || !introTitle) {
|
||||||
return res.status(500).json({ error: "Missing welcome content" })
|
return res.status(500).json({ error: "Missing welcome content" })
|
||||||
}
|
}
|
||||||
|
|
62
server/src/lib/__tests__/config.ts
Normal file
62
server/src/lib/__tests__/config.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { config } from "../config"
|
||||||
|
|
||||||
|
describe("Config", () => {
|
||||||
|
it("should build a valid development config when no environment is set", () => {
|
||||||
|
const emptyEnv = {};
|
||||||
|
const result = config(emptyEnv);
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("is_production", false)
|
||||||
|
expect(result).toHaveProperty("port")
|
||||||
|
expect(result).toHaveProperty("jwt_secret")
|
||||||
|
expect(result).toHaveProperty("drift_home")
|
||||||
|
expect(result).toHaveProperty("memory_db")
|
||||||
|
expect(result).toHaveProperty("enable_admin")
|
||||||
|
expect(result).toHaveProperty("secret_key")
|
||||||
|
expect(result).toHaveProperty("registration_password")
|
||||||
|
expect(result).toHaveProperty("welcome_content")
|
||||||
|
expect(result).toHaveProperty("welcome_title")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail when building a prod environment without SECRET_KEY", () => {
|
||||||
|
expect(() => config({ NODE_ENV: "production" }))
|
||||||
|
.toThrow(new Error("Missing environment variable: SECRET_KEY"))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should build a prod config with a SECRET_KEY", () => {
|
||||||
|
const result = config({ NODE_ENV: "production", SECRET_KEY: "secret" })
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("is_production", true)
|
||||||
|
expect(result).toHaveProperty("secret_key", "secret")
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("jwt_secret", () => {
|
||||||
|
it("should use default jwt_secret when environment is blank string", () => {
|
||||||
|
const result = config({ JWT_SECRET: "" })
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("is_production", false)
|
||||||
|
expect(result).toHaveProperty("jwt_secret", "myjwtsecret")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("booleans", () => {
|
||||||
|
it("should parse 'true' as true", () => {
|
||||||
|
const result = config({ MEMORY_DB: "true" })
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("memory_db", true)
|
||||||
|
})
|
||||||
|
it("should parse 'false' as false", () => {
|
||||||
|
const result = config({ MEMORY_DB: "false" })
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("memory_db", false)
|
||||||
|
})
|
||||||
|
it("should fail when it is not parseable", () => {
|
||||||
|
expect(() => config({ MEMORY_DB: "foo" }))
|
||||||
|
.toThrow(new Error("Invalid boolean value: foo"))
|
||||||
|
})
|
||||||
|
it("should default to false when the string is empty", () => {
|
||||||
|
const result = config({ MEMORY_DB: "" })
|
||||||
|
|
||||||
|
expect(result).toHaveProperty("memory_db", false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -6,11 +6,16 @@ type Config = {
|
||||||
memory_db: boolean
|
memory_db: boolean
|
||||||
enable_admin: boolean
|
enable_admin: boolean
|
||||||
secret_key: string
|
secret_key: string
|
||||||
registration_password: string
|
registration_password: string,
|
||||||
|
welcome_content: string | undefined,
|
||||||
|
welcome_title: string | undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = (): Config => {
|
type EnvironmentValue = string | undefined;
|
||||||
const stringToBoolean = (str: string | undefined): boolean => {
|
type Environment = { [key: string]: EnvironmentValue }
|
||||||
|
|
||||||
|
export const config = (env: Environment): Config => {
|
||||||
|
const stringToBoolean = (str: EnvironmentValue): boolean => {
|
||||||
if (str === "true") {
|
if (str === "true") {
|
||||||
return true
|
return true
|
||||||
} else if (str === "false") {
|
} else if (str === "false") {
|
||||||
|
@ -22,21 +27,21 @@ const config = (): Config => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const throwIfUndefined = (str: string | undefined, name: string): string => {
|
const throwIfUndefined = (str: EnvironmentValue, name: string): string => {
|
||||||
if (str === undefined) {
|
if (str === undefined) {
|
||||||
throw new Error(`Missing environment variable: ${name}`)
|
throw new Error(`Missing environment variable: ${name}`)
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultIfUndefined = (str: string | undefined, defaultValue: string): string => {
|
const defaultIfUndefined = (str: EnvironmentValue, defaultValue: string): string => {
|
||||||
if (str === undefined) {
|
if (str === undefined) {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
const validNodeEnvs = (str: string | undefined) => {
|
const validNodeEnvs = (str: EnvironmentValue) => {
|
||||||
const valid = ["development", "production", "test"]
|
const valid = ["development", "production", "test"]
|
||||||
if (str && !valid.includes(str)) {
|
if (str && !valid.includes(str)) {
|
||||||
throw new Error(`Invalid NODE_ENV set: ${str}`)
|
throw new Error(`Invalid NODE_ENV set: ${str}`)
|
||||||
|
@ -47,26 +52,29 @@ const config = (): Config => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_production = process.env.NODE_ENV === "production";
|
const is_production = env.NODE_ENV === "production";
|
||||||
|
|
||||||
const developmentDefault = (str: string | undefined, name: string, defaultValue: string): string => {
|
const developmentDefault = (str: EnvironmentValue, name: string, defaultValue: string): string => {
|
||||||
if (is_production) return throwIfUndefined(str, name);
|
if (is_production) return throwIfUndefined(str, name);
|
||||||
return defaultIfUndefined(str, defaultValue);
|
return defaultIfUndefined(str, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
validNodeEnvs(process.env.NODE_ENV)
|
validNodeEnvs(env.NODE_ENV)
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
|
port: env.PORT ? parseInt(env.PORT) : 3000,
|
||||||
jwt_secret: process.env.JWT_SECRET || "myjwtsecret",
|
jwt_secret: env.JWT_SECRET || "myjwtsecret",
|
||||||
drift_home: process.env.DRIFT_HOME || "~/.drift",
|
drift_home: env.DRIFT_HOME || "~/.drift",
|
||||||
is_production,
|
is_production,
|
||||||
memory_db: stringToBoolean(process.env.MEMORY_DB),
|
memory_db: stringToBoolean(env.MEMORY_DB),
|
||||||
enable_admin: stringToBoolean(process.env.ENABLE_ADMIN),
|
enable_admin: stringToBoolean(env.ENABLE_ADMIN),
|
||||||
secret_key: developmentDefault(process.env.SECRET_KEY, "SECRET_KEY", "secret"),
|
secret_key: developmentDefault(env.SECRET_KEY, "SECRET_KEY", "secret"),
|
||||||
registration_password: process.env.REGISTRATION_PASSWORD || ""
|
registration_password: env.REGISTRATION_PASSWORD ?? "",
|
||||||
|
welcome_content: env.WELCOME_CONTENT,
|
||||||
|
welcome_title: env.WELCOME_TITLE
|
||||||
|
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
export default config()
|
export default config(process.env)
|
||||||
|
|
Loading…
Reference in a new issue