Configuration pedantry
This commit is contained in:
parent
e5fca7a476
commit
773439d2e0
4 changed files with 62 additions and 21 deletions
56
config.ts
56
config.ts
|
@ -9,6 +9,7 @@ export interface LogConfig {
|
|||
|
||||
export interface PostgresConfig {
|
||||
url: string;
|
||||
poolSize: number;
|
||||
}
|
||||
|
||||
export interface MailgunConfig {
|
||||
|
@ -28,8 +29,12 @@ function truthyEnv(key: string): boolean {
|
|||
["true", "1"];
|
||||
}
|
||||
|
||||
function isLogLevelName(s: string): s is LevelName {
|
||||
return s in LogLevels;
|
||||
function toLogLevelName(s: string): LevelName | undefined {
|
||||
if (s in LogLevels) {
|
||||
return s as LevelName;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export let config: Readonly<Config>;
|
||||
|
@ -45,7 +50,7 @@ export function reload(): [
|
|||
((logger: Logger) => void)[],
|
||||
] {
|
||||
// since we want configuration to be setup before any logging, lets save logs here until the logger is setup
|
||||
const logCalls = [];
|
||||
const logCalls: ((logger: Logger) => void)[] = [];
|
||||
|
||||
const envOrWarn = (key: string, fallback: string): string => {
|
||||
const val = Deno.env.get(key);
|
||||
|
@ -57,28 +62,49 @@ export function reload(): [
|
|||
return val || fallback;
|
||||
};
|
||||
|
||||
const desiredLogLevel = envOrWarn("LOG_LEVEL", "INFO").toUpperCase();
|
||||
if (!isLogLevelName(desiredLogLevel)) {
|
||||
logCalls.push((logger: Logger) =>
|
||||
logger.warning(
|
||||
`Specified LOG_LEVEL '${desiredLogLevel}' is invalid. Falling back to INFO`,
|
||||
)
|
||||
);
|
||||
}
|
||||
const envToIntOrWarn = (key: string, fallback: number): number => {
|
||||
const ev = envOrWarn(key, fallback.toString());
|
||||
let result = parseInt(ev);
|
||||
if (isNaN(result)) {
|
||||
logCalls.push((logger: Logger) =>
|
||||
logger.warning(
|
||||
`Specified ${key} '${ev}' is not a valid integer. Falling back to ${fallback}.`,
|
||||
)
|
||||
);
|
||||
result = fallback;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const logLevel: LevelName = isLogLevelName(desiredLogLevel)
|
||||
? desiredLogLevel
|
||||
: "INFO";
|
||||
const envToEnumOrWarn = <T extends { toString(self: T): string }>(
|
||||
key: string,
|
||||
converter: (s: string) => T | undefined,
|
||||
fallback: T,
|
||||
): T => {
|
||||
const ev = envOrWarn(key, `${fallback}`);
|
||||
const result = converter(ev);
|
||||
if (result) {
|
||||
return result;
|
||||
} else {
|
||||
logCalls.push((logger: Logger) =>
|
||||
logger.warning(
|
||||
`Specified ${key} '${ev}' is invalid. Falling back to ${fallback}`,
|
||||
)
|
||||
);
|
||||
return fallback;
|
||||
}
|
||||
};
|
||||
|
||||
const config: Config = {
|
||||
log: {
|
||||
consoleLevelName: logLevel,
|
||||
consoleLevelName: envToEnumOrWarn("LOG_LEVEL", toLogLevelName, "INFO"),
|
||||
},
|
||||
postgres: {
|
||||
url: envOrWarn(
|
||||
"POSTGRES_URL",
|
||||
"postgresql://postgres:@127.0.0.1:5432/lyricscreen",
|
||||
),
|
||||
poolSize: envToIntOrWarn("POSTGRES_POOL_SIZE", 3),
|
||||
},
|
||||
mailgun: {},
|
||||
isDevelopmentMode: truthyEnv("DEVELOPMENT_MODE"),
|
||||
|
|
14
db/mod.ts
14
db/mod.ts
|
@ -35,6 +35,18 @@ let pool: Pool;
|
|||
|
||||
export function initDatabaseConnectionPool({ url }: PostgresConfig) {
|
||||
pool = new Pool(url, 3, true);
|
||||
testDbConnection();
|
||||
}
|
||||
|
||||
export async function testDbConnection(): Promise<boolean> {
|
||||
try {
|
||||
await dbOp((conn) => conn.queryObject("select 1"));
|
||||
log.info("Successfully connected to database");
|
||||
return true;
|
||||
} catch (e) {
|
||||
log.critical("Failed to connect to database:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
type QueryResult<T> = { rows: T[] } | null;
|
||||
|
@ -383,6 +395,8 @@ export async function getUser(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: refresh token?
|
||||
|
||||
export async function getUserFromNonExpiredLoginToken(
|
||||
token: TokenDigest,
|
||||
): Promise<User> {
|
||||
|
|
12
log.ts
12
log.ts
|
@ -42,19 +42,21 @@ class CustomConsoleHandler extends log.handlers.ConsoleHandler {
|
|||
}
|
||||
}
|
||||
|
||||
export function setupLoggers(config: LogConfig) {
|
||||
// TODO: support for colors or nah?
|
||||
export function setupLoggers(config: LogConfig, redact?: RegExp) {
|
||||
// TODO: check support for colors and adjust as needed
|
||||
log.setup({
|
||||
handlers: {
|
||||
console: new CustomConsoleHandler(
|
||||
config.consoleLevelName,
|
||||
{
|
||||
formatter: ({ level, datetime, msg, args }: log.LogRecord) => {
|
||||
// TODO: use a replacer for redacting secrets?
|
||||
const fullMessage = format(
|
||||
let fullMessage = format(
|
||||
msg,
|
||||
args,
|
||||
...args,
|
||||
);
|
||||
if (redact) {
|
||||
fullMessage = fullMessage.replaceAll(redact, "<REDACTED>");
|
||||
}
|
||||
return `\x1b[m[\x1b[${levelColors[level] || ""}m${
|
||||
short[level] || "UNK"
|
||||
}\x1b[m] \x1b[90m${datetime.toISOString()}\x1b[m \x1b[${
|
||||
|
|
|
@ -13,7 +13,6 @@ export const handler: Handlers<UserId | RegistrationError | null> = {
|
|||
const formData = (await request.formData());
|
||||
const username = formData.get("username");
|
||||
const password = formData.get("password");
|
||||
// TODO: verify that username conforms to some regex? no spaces?
|
||||
if (!username) {
|
||||
return await context.render({ message: "no username provided" });
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue