diff --git a/src/config/default.ts b/src/config/default.ts index 7f88912..8806f5d 100755 --- a/src/config/default.ts +++ b/src/config/default.ts @@ -15,5 +15,5 @@ export default { }, ], }, - JWT_SECRET: "RblQqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!", + DEV_JWT_SECRET: "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!", } diff --git a/src/server/index.ts b/src/server/index.ts index d316e52..0b58b3c 100755 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -16,7 +16,7 @@ import devServer from "./devServer" import ssr from "./ssr" import certbotRouter from "../routes/certbot" -import { secure } from "./secure" +import { hasSecret, secure } from "./secure" import { javGameListGet } from "./gsheets/javGames" import { preVolunteerAdd, preVolunteerCountGet } from "./gsheets/preVolunteers" import { teamListGet } from "./gsheets/teams" @@ -34,6 +34,7 @@ import config from "../config" import notificationsSubscribe from "./notificationsSubscribe" import checkAccess from "./checkAccess" import { hasGSheetsAccess } from "./gsheets/accessors" +import { addStatus, showStatusAt } from "./status" checkAccess() @@ -122,6 +123,10 @@ if (validCertPath) { const httpsOptions = _.mapValues(validCertPath, (pemPath: string) => fs.readFileSync(pemPath)) servers.push({ protocol: "https", server: https.createServer(httpsOptions, app) }) + + showStatusAt(6) +} else { + showStatusAt(5) } /** @@ -139,7 +144,7 @@ servers.forEach(({ protocol, server }) => { function onError(error: any) { if (error) { - console.error(chalk.red(`==> 😭 OMG!!! ${error}`)) + addStatus("Server listening:", chalk.red(`==> 😭 OMG!!! ${error}`)) } } @@ -150,13 +155,35 @@ function onError(error: any) { function onListening(server: any) { const addr = server.address() const bind = typeof addr === "string" ? `pipe ${addr}` : `port ${addr.port}` - console.error(chalk.green(`\nServer listening on ${bind}`)) + addStatus("Server listening:", chalk.green(`✅ ${bind}`)) } hasGSheetsAccess().then((hasApiAccess: boolean) => { if (hasApiAccess) { - console.error(chalk.green(`Database: remote Google Sheet`)) + addStatus("Database:", chalk.green(`✅ online from Google Sheet`)) } else { - console.error(chalk.green(`Database: local db`)) + addStatus("Database:", chalk.blue(`🚧 offline, simulated from local db file`)) + } +}) + +const hasSendGridApiAccess = !!process.env.SENDGRID_API_KEY +if (hasSendGridApiAccess) { + addStatus("Emailing:", chalk.green(`✅ online through SendGrid`)) +} else { + addStatus("Emailing:", chalk.blue(`🚧 offline, simulated`)) +} + +const hasPushNotifAccess = !!process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY +if (hasPushNotifAccess) { + addStatus("Push notif:", chalk.green(`✅ online with a Vapid key`)) +} else { + addStatus("Push notif:", chalk.blue(`🚧 offline, simulated`)) +} + +hasSecret().then((has: boolean) => { + if (has) { + addStatus("JWT secret:", chalk.green(`✅ prod private one from file`)) + } else { + addStatus("JWT secret:", chalk.blue(`🚧 dev public fake one from config`)) } }) diff --git a/src/server/notificationsSubscribe.ts b/src/server/notificationsSubscribe.ts index c3894cc..bd276e7 100644 --- a/src/server/notificationsSubscribe.ts +++ b/src/server/notificationsSubscribe.ts @@ -3,7 +3,8 @@ import webpush from "web-push" const publicKey = process.env.FORCE_ORANGE_PUBLIC_VAPID_KEY const privateKey = process.env.FORCE_ORANGE_PRIVATE_VAPID_KEY -if (publicKey && privateKey) { +const hasPushAccess = publicKey && privateKey +if (hasPushAccess) { webpush.setVapidDetails("mailto: contact@parisestludique.fr", publicKey, privateKey) } @@ -18,10 +19,19 @@ export default function notificationsSubscribe( title: "Hello!", body: "It works.", }) - webpush - .sendNotification(subscription, payload) - .then((result) => console.log(result)) - .catch((e) => console.log(e.stack)) + + if (hasPushAccess) { + webpush + .sendNotification(subscription, payload) + .then((result) => console.log(result)) + .catch((e) => console.log(e.stack)) + } else { + console.error( + `Fake sending push notif to ${JSON.stringify(subscription)} of ${JSON.stringify( + payload + )})}` + ) + } response.status(200).json({ success: true }) } diff --git a/src/server/secure.ts b/src/server/secure.ts index 04210c5..e4a141c 100644 --- a/src/server/secure.ts +++ b/src/server/secure.ts @@ -1,6 +1,6 @@ import { NextFunction, Request, Response } from "express" import path from "path" -import { promises as fs } from "fs" +import { constants, promises as fs } from "fs" import { verify, sign } from "jsonwebtoken" import config from "../config" @@ -8,6 +8,7 @@ import config from "../config" type AuthorizedRequest = Request & { headers: { authorization: string } } let cachedSecret: string +const SECRET_PATH = path.resolve(process.cwd(), "access/jwt_secret.json") getSecret() // Necessary until we can make async express middleware export function secure(request: AuthorizedRequest, response: Response, next: NextFunction): void { @@ -35,15 +36,28 @@ export function secure(request: AuthorizedRequest, response: Response, next: Nex }) } +let hasSecretReturn: boolean | undefined +export async function hasSecret(): Promise { + if (hasSecretReturn !== undefined) { + return hasSecretReturn + } + try { + // eslint-disable-next-line no-bitwise + await fs.access(SECRET_PATH, constants.R_OK | constants.W_OK) + hasSecretReturn = true + } catch { + hasSecretReturn = false + } + return hasSecretReturn +} + async function getSecret() { if (!cachedSecret) { - const SECRET_PATH = path.resolve(process.cwd(), "access/jwt_secret.json") - try { const secretContent = await fs.readFile(SECRET_PATH) cachedSecret = secretContent && JSON.parse(secretContent.toString()).secret } catch (e: any) { - cachedSecret = config.JWT_SECRET + cachedSecret = config.DEV_JWT_SECRET } } diff --git a/src/server/status.ts b/src/server/status.ts new file mode 100644 index 0000000..11e9d1b --- /dev/null +++ b/src/server/status.ts @@ -0,0 +1,20 @@ +const messages: string[][] = [] +let _messageCount = 0 + +export function addStatus(...messageParams: string[]): void { + messages.push(messageParams) + if (messages.length === _messageCount) { + showStatus() + } else if (messages.length > _messageCount) { + console.error(...messageParams) + } +} + +function showStatus(): void { + console.error("") + messages.forEach((messageParams) => console.error(...messageParams)) +} + +export function showStatusAt(messageCount: number): void { + _messageCount = messageCount +}