From 7e2b47ce75c4a7ee6de2269dee4bff1b04b03219 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 14:46:59 -0400 Subject: [PATCH 01/12] chore: delete old and broken test user script --- create-test-user.js | 79 --------------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 create-test-user.js diff --git a/create-test-user.js b/create-test-user.js deleted file mode 100644 index 047ea68..0000000 --- a/create-test-user.js +++ /dev/null @@ -1,79 +0,0 @@ -const prompt = require('prompt'); -const crypto = require('crypto'); -const database = require('./src/database'); -const { PNID } = require('./src/models/pnid'); - -prompt.message = ''; - -const properties = [ - 'username', - 'email', - { - name: 'password', - hidden: true - } -]; - -prompt.get(properties, (error, { username, email, password }) => { - const date = new Date().toISOString(); - // Sample Mii data - const miiData = 'AwAAQOlVognnx0GC2X0LLQOzuI0n2QAAAUBiAGUAbABsAGEAAABFAAAAAAAAAEBAEgCBAQRoQxggNEYUgRIXaA0AACkDUkhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP6G'; - - const document = { - pid: 1, - creation_date: date.split('.')[0], - updated: date, - username: username, - password: password, - birthdate: '1990-01-01', - gender: 'M', - country: 'US', - language: 'en', - email: { - address: email, - primary: true, - parent: true, - reachable: true, - validated: true, - id: crypto.randomBytes(4).readUInt32LE() - }, - region: 0x310B0000, - timezone: { - name: 'America/New_York', - offset: -14400 - }, - mii: { - name: 'bella', - primary: true, - data: miiData, - id: crypto.randomBytes(4).readUInt32LE(), - hash: crypto.randomBytes(7).toString('hex'), - image_url: '', // Deprecated, will be removed - image_id: crypto.randomBytes(4).readUInt32LE() - }, - flags: { - active: true, - marketing: false, - off_device: true - }, - validation: { - // These values are temp and will be overwritten before the document saves - // These values are only being defined to get around the `E11000 duplicate key error collection` error - email_code: 1, - email_token: '' - } - }; - - const newUser = new PNID(document); - - newUser.save(async (error, newUser) => { - if (error) { - throw error; - } - - console.log(newUser); - console.log('New user created'); - }); -}); - -database.connect().then(prompt.start); \ No newline at end of file From 41977cf7afe27a35024fa0a81b4f8ac969b6730c Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:00:47 -0400 Subject: [PATCH 02/12] chore: update Dockerfile for production use This uses a standard Node.js Dockerfile template and removes the old, unused entrypoint script. --- .dockerignore | 10 +++---- Dockerfile | 64 +++++++++++++++++++++++++++++++++----------- docker/entrypoint.sh | 28 ------------------- 3 files changed, 52 insertions(+), 50 deletions(-) delete mode 100644 docker/entrypoint.sh diff --git a/.dockerignore b/.dockerignore index 7a2fc0a..ba3c084 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,4 @@ -.git -config.json -logs -certs -cdn -node_modules +.git +node_modules +dist +logs diff --git a/Dockerfile b/Dockerfile index a810a90..9390162 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,48 @@ -FROM node:18-alpine - -RUN apk add --no-cache python3 make gcc g++ -WORKDIR /app - -COPY "docker/entrypoint.sh" ./ - -COPY package*.json ./ -RUN npm install bcrypt && npm rebuild bcrypt --build-from-source -RUN npm install - -COPY . ./ - -VOLUME [ "/app/config.json", "/app/certs" ] - -CMD ["sh", "entrypoint.sh"] +# syntax=docker/dockerfile:1 + +ARG app_dir="/home/node/app" + + +# * Base Node.js image +FROM node:20-alpine AS base +ARG app_dir +WORKDIR ${app_dir} + + +# * Installing production dependencies +FROM base AS dependencies + +RUN --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=package-lock.json,target=package-lock.json \ + --mount=type=cache,target=/root/.npm \ + npm ci --omit=dev + + +# * Installing development dependencies and building the application +FROM base AS build + +RUN --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=package-lock.json,target=package-lock.json \ + --mount=type=cache,target=/root/.npm \ + npm ci + +COPY . . +RUN npm run build + + +# * Running the final application +FROM base AS final +ARG app_dir + +RUN mkdir -p ${app_dir}/logs +RUN chown node:node ${app_dir}/logs + +ENV NODE_ENV production +USER node + +COPY package.json . + +COPY --from=dependencies ${app_dir}/node_modules ${app_dir}/node_modules +COPY --from=build ${app_dir}/dist ${app_dir}/dist + +CMD ["node", "."] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100644 index 2c6aa02..0000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -# this doesnt check game server specific certs, only static file paths -files='config.json' - -for file in $files; do - if [ ! -f $file ]; then - echo "$PWD/$file file does not exist. Please mount and try again." - exit 1 - fi -done - -# check for keys -keys='certs/nex/datastore/secret.key certs/service/account/secret.key certs/service/account/aes.key certs/service/account/private.pem certs/service/account/public.pem' -for file in $keys; do - if [ ! -f "$file" ]; then - if [ x"${GENERATE_NEW_KEYS}" = "x" ]; then - echo "$PWD/$file file does not exist. Please mount and try again." - exit 1 - else - echo "$PWD/$file file does not exist. Generating a temporary one" - node generate-keys.js nex datastore - node generate-keys.js account - fi - fi -done - -exec node src/server.js From bdd8a916100a8eba4556989159b95ea089fb4ed8 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:32:48 -0400 Subject: [PATCH 03/12] fix: exit process on SIGTERM --- src/server.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server.ts b/src/server.ts index 797c60e..97638db 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,6 +3,9 @@ process.on('uncaughtException', (err, origin) => { console.log(err); console.log(origin); }); +process.on('SIGTERM', () => { + process.exit(0); +}); import express from 'express'; import morgan from 'morgan'; From 697eca73fb986e9a90ba74bcb1abd4c72705b76e Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:07:36 -0400 Subject: [PATCH 04/12] chore: load datastore secret key from environment variable --- SETUP.md | 1 + src/config-manager.ts | 18 ++++++++++++++++-- src/services/datastore/routes/upload.ts | 5 ++--- src/types/common/config.ts | 3 +++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/SETUP.md b/SETUP.md index 8bb83c2..5740b95 100644 --- a/SETUP.md +++ b/SETUP.md @@ -75,6 +75,7 @@ Configurations are loaded through environment variables. `.env` files are suppor | `PN_ACT_CONFIG_CDN_BASE_URL` | URL for serving CDN contents (usually the same as s3 endpoint) | No | | `PN_ACT_CONFIG_WEBSITE_BASE` | Website URL | Yes | | `PN_ACT_CONFIG_AES_KEY` | AES-256 key used for encrypting tokens | No | +| `PN_ACT_CONFIG_DATASTORE_SIGNATURE_SECRET` | HMAC secret key (16 bytes in hex format) used to sign uploaded DataStore files | No | | `PN_ACT_CONFIG_GRPC_MASTER_API_KEY_ACCOUNT` | Master API key to interact with the account gRPC service | No | | `PN_ACT_CONFIG_GRPC_MASTER_API_KEY_API` | Master API key to interact with the API gRPC service | No | | `PN_ACT_CONFIG_GRPC_PORT` | gRPC server port | No | diff --git a/src/config-manager.ts b/src/config-manager.ts index 28a081c..2d8f0e0 100644 --- a/src/config-manager.ts +++ b/src/config-manager.ts @@ -13,6 +13,8 @@ export const disabledFeatures = { s3: false }; +const hexadecimalStringRegex = /^[0-9a-f]+$/i; + LOG_INFO('Loading config'); let mongooseConnectOptions: mongoose.ConnectOptions = {}; @@ -71,7 +73,10 @@ export const config: Config = { }, port: Number(process.env.PN_ACT_CONFIG_GRPC_PORT || ''), }, - server_environment: process.env.PN_ACT_CONFIG_SERVER_ENVIRONMENT || '' + server_environment: process.env.PN_ACT_CONFIG_SERVER_ENVIRONMENT || '', + datastore: { + signature_secret: process.env.PN_ACT_CONFIG_DATASTORE_SIGNATURE_SECRET || '' + } }; if (process.env.PN_ACT_CONFIG_STRIPE_SECRET_KEY) { @@ -194,4 +199,13 @@ if (!config.grpc.port) { if (!config.stripe?.secret_key) { LOG_WARN('Failed to find Stripe api key! If a PNID is deleted with an active subscription, the subscription will *NOT* be canceled! Set the PN_ACT_CONFIG_STRIPE_SECRET_KEY environment variable to enable'); -} \ No newline at end of file +} + +if (!config.datastore.signature_secret) { + LOG_ERROR('Datastore signature secret key is not set. Set the PN_ACT_CONFIG_DATASTORE_SIGNATURE_SECRET environment variable'); + process.exit(0); +} +if (config.datastore.signature_secret.length !== 32 || !hexadecimalStringRegex.test(config.datastore.signature_secret)) { + LOG_ERROR('Datastore signature secret key must be a 32-character hexadecimal string.'); + process.exit(0); +} diff --git a/src/services/datastore/routes/upload.ts b/src/services/datastore/routes/upload.ts index 6e3585e..b5f35bc 100644 --- a/src/services/datastore/routes/upload.ts +++ b/src/services/datastore/routes/upload.ts @@ -3,11 +3,10 @@ import crypto from 'node:crypto'; import express from 'express'; import Dicer from 'dicer'; import { uploadCDNAsset } from '@/util'; +import { config } from '@/config-manager'; const router = express.Router(); -const signatureSecret = fs.readFileSync(`${__dirname}/../../../../certs/nex/datastore/secret.key`); - function multipartParser(request: express.Request, response: express.Response, next: express.NextFunction): void { const RE_BOUNDARY = /^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i; const RE_FILE_NAME = /name="(.*)"/; @@ -86,7 +85,7 @@ router.post('/upload', multipartParser, async (request: express.Request, respons const data = `${pid}${bucket}${key}${date}`; - const hmac = crypto.createHmac('sha256', signatureSecret).update(data).digest('hex'); + const hmac = crypto.createHmac('sha256', config.datastore.signature_secret).update(data).digest('hex'); console.log(hmac, signature); diff --git a/src/types/common/config.ts b/src/types/common/config.ts index 0b27a16..0ae43fb 100644 --- a/src/types/common/config.ts +++ b/src/types/common/config.ts @@ -47,4 +47,7 @@ export interface Config { secret_key: string; }; server_environment: string; + datastore: { + signature_secret: string; + }; } From ac80a9f60f2a44a06ed0bdf8863796c65458da1d Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:43:24 -0400 Subject: [PATCH 05/12] chore: delete old and unused key generation script --- generate-keys.js | 145 ----------------------------------------------- 1 file changed, 145 deletions(-) delete mode 100644 generate-keys.js diff --git a/generate-keys.js b/generate-keys.js deleted file mode 100644 index b1a311b..0000000 --- a/generate-keys.js +++ /dev/null @@ -1,145 +0,0 @@ -const NodeRSA = require('node-rsa'); -const crypto = require('crypto'); -const fs = require('fs-extra'); -const yesno = require('yesno'); -const logger = require('./logger'); -require('colors'); - -const ALLOWED_CHARS_REGEX = /[^a-zA-Z0-9_-]/g; - -async function main() { - const args = process.argv.slice(2); - - if (args.length < 1) { - logger.error('Must pass in type and optional name'); - usage(); - return; - } - - let [type, name] = args; - - type = type.toLowerCase().trim(); - - if (name) { - name = name.toLowerCase().trim(); - - if (ALLOWED_CHARS_REGEX.test(name)) { - logger.error(`Invalid name. Names must only contain [^a-zA-Z0-9_-]. Got ${name}`); - return; - } - } - - if (!['nex', 'service', 'account'].includes(type)) { - logger.error(`Invalid type. Expected nex, service, or account. Got ${type}`); - usage(); - return; - } - - if (type !== 'account' && (!name || name === '')) { - logger.error('If type is not account, a name MUST be passed'); - usage(); - return; - } - - if (type === 'service' && name === 'account') { - logger.error('Cannot use service name \'account\'. Reserved'); - usage(); - return; - } - - let path; - - if (type === 'account') { - path = `${__dirname}/certs/service/account`; - } else { - path = `${__dirname}/certs/${type}/${name}`; - } - - if (fs.pathExistsSync(path)) { - const overwrite = await yesno({ - question: 'Keys found for type name, overwrite existing keys?' - }); - - if (!overwrite) { - logger.info('Not overwriting existing keys. Exiting program'); - return; - } - } - - const publicKeyPath = `${path}/public.pem`; - const privateKeyPath = `${path}/private.pem`; - const aesKeyPath = `${path}/aes.key`; - const secretKeyPath = `${path}/secret.key`; - - // Ensure the output directories exist - logger.info('Creating output directories...'); - fs.ensureDirSync(path); - logger.success('Created output directories!'); - - const key = new NodeRSA({ b: 1024 }, null, { - environment: 'browser', - encryptionScheme: { - 'hash': 'sha256', - } - }); - - // Generate new key pair - logger.info('Generating RSA key pair...'); - logger.warn('(this may take a while)') - key.generateKeyPair(1024); - logger.success('Generated RSA key pair!'); - - // Export the keys - logger.info('Exporting public key...'); - const publicKey = key.exportKey('public'); - logger.success('Exported public key!'); - - // Saving public key - logger.info('Saving public key to disk...'); - fs.writeFileSync(publicKeyPath, publicKey); - logger.success(`Saved public key to ${publicKeyPath}!`); - - logger.info('Exporting private key...'); - const privateKey = key.exportKey('private'); - logger.success('Exported private key!'); - - // Saving private key - logger.info('Saving private key to disk...'); - fs.writeFileSync(privateKeyPath, privateKey); - logger.success(`Saved private key to ${privateKeyPath}!`); - - // Generate new AES key - logger.info('Generating AES key...'); - const aesKey = crypto.randomBytes(16); - logger.success('Generated AES key!'); - - // Saving AES key - logger.info('Saving AES key to disk...'); - fs.writeFileSync(aesKeyPath, aesKey.toString('hex')); - logger.success(`Saved AES key to ${aesKeyPath}!`); - - // Create HMAC secret key - logger.info('Generating HMAC secret...'); - const secret = crypto.randomBytes(16); - logger.success('Generated RSA key pair!'); - - logger.info('Saving HMAC secret to disk...'); - fs.writeFileSync(secretKeyPath, secret.toString('hex')); - logger.success(`Saved HMAC secret to ${secretKeyPath}!`); - - logger.success('Keys generated successfully'); -} - -// Display usage information -function usage() { - console.log('Usage: node generate-keys.js type [name]'); - - console.log('Types:'); - console.log(' - nex'); - console.log(' - service'); - console.log(' - account'); - - console.log('Name: Service or NEX server name. Not used in account type'); -} - -main().catch(logger.error); \ No newline at end of file From bd2b7de369d3b99a1dd7926a48455872864bb64a Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:46:21 -0400 Subject: [PATCH 06/12] chore: fix eslint error --- src/services/datastore/routes/upload.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/datastore/routes/upload.ts b/src/services/datastore/routes/upload.ts index b5f35bc..9827781 100644 --- a/src/services/datastore/routes/upload.ts +++ b/src/services/datastore/routes/upload.ts @@ -1,4 +1,3 @@ -import fs from 'node:fs'; import crypto from 'node:crypto'; import express from 'express'; import Dicer from 'dicer'; From 8292c6248fe2cb4d4164f1df105be8b9e58bb2d5 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:46:57 -0400 Subject: [PATCH 07/12] chore: fix Docker environment variable format --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9390162..949a998 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ ARG app_dir RUN mkdir -p ${app_dir}/logs RUN chown node:node ${app_dir}/logs -ENV NODE_ENV production +ENV NODE_ENV=production USER node COPY package.json . From 9712c4d95b9de0f7628bb0147dbcc57cec4ad97a Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:07:01 -0400 Subject: [PATCH 08/12] fix: imported redis is undefined --- src/cache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cache.ts b/src/cache.ts index e354732..7515446 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,5 +1,5 @@ import fs from 'fs-extra'; -import redis from 'redis'; +import * as redis from 'redis'; import { config, disabledFeatures } from '@/config-manager'; let client: redis.RedisClientType; From 78a9ebc345a6fea9d32891df63f200b50e837c46 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:31:49 -0400 Subject: [PATCH 09/12] chore: uninstall unused packages --- package-lock.json | 130 +--------------------------------------------- package.json | 4 +- 2 files changed, 2 insertions(+), 132 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0f634e..4ff53d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,9 +65,7 @@ "@typescript-eslint/parser": "^5.54.1", "eslint": "^8.35.0", "ndarray": "^1.0.19", - "prompt": "^1.0.0", - "typescript": "^4.9.5", - "yesno": "^0.4.0" + "typescript": "^4.9.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -769,15 +767,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3257,15 +3246,6 @@ "node": ">= 8" } }, - "node_modules/cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", @@ -4023,15 +4003,6 @@ "node >=0.6.0" ] }, - "node_modules/eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", - "dev": true, - "engines": { - "node": "> 0.1.90" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5136,12 +5107,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -5547,12 +5512,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5945,22 +5904,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/prompt": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", - "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", - "dev": true, - "dependencies": { - "@colors/colors": "1.5.0", - "async": "3.2.3", - "read": "1.0.x", - "revalidator": "0.1.x", - "winston": "2.x" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/protobufjs": { "version": "7.2.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", @@ -6107,18 +6050,6 @@ "node": ">= 0.8" } }, - "node_modules/read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", - "dev": true, - "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -6261,15 +6192,6 @@ "node": ">=0.10.0" } }, - "node_modules/revalidator": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6557,15 +6479,6 @@ "node": ">=0.10.0" } }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/static-eval": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", @@ -7160,41 +7073,6 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/winston": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", - "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", - "dev": true, - "dependencies": { - "async": "^2.6.4", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/winston/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/winston/node_modules/colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -7311,12 +7189,6 @@ "node": ">=12" } }, - "node_modules/yesno": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/yesno/-/yesno-0.4.0.tgz", - "integrity": "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==", - "dev": true - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 9e496e7..c404a65 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,6 @@ "@typescript-eslint/parser": "^5.54.1", "eslint": "^8.35.0", "ndarray": "^1.0.19", - "prompt": "^1.0.0", - "typescript": "^4.9.5", - "yesno": "^0.4.0" + "typescript": "^4.9.5" } } From 0e68fff5e53068c154e868deba40496cd984c884 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:51:43 -0400 Subject: [PATCH 10/12] chore: add .env to .dockerignore --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index ba3c084..665b496 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ .git +.env node_modules dist logs From d12c07cdee2da3be6e778d34981872a8bbbb1ae5 Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:42:07 -0400 Subject: [PATCH 11/12] feat: create Docker build and publish action --- .github/workflows/docker.yml | 47 ++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..9e1ef9c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,47 @@ +name: Build and Publish Docker Image + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + build-publish: + env: + SHOULD_PUSH_IMAGE: ${{ (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev')) || github.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + + steps: + - name: Set up QEMU for Docker + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log into the Docker container registry + if: ${{ env.SHOULD_PUSH_IMAGE == 'true' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ github.repository }} + tags: | + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=edge,enable=${{ github.ref == 'refs/heads/dev' }} + type=sha + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64,linux/arm64 + push: ${{ env.SHOULD_PUSH_IMAGE }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max From 01ce92d1f2fb204b84f4ba1c80b90b9ff4c217da Mon Sep 17 00:00:00 2001 From: Matthew Lopez <73856503+MatthewL246@users.noreply.github.com> Date: Mon, 1 Jul 2024 15:30:23 -0400 Subject: [PATCH 12/12] chore: mkdir and chown on one layer --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 949a998..53811ab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,8 +34,7 @@ RUN npm run build FROM base AS final ARG app_dir -RUN mkdir -p ${app_dir}/logs -RUN chown node:node ${app_dir}/logs +RUN mkdir -p ${app_dir}/logs && chown node:node ${app_dir}/logs ENV NODE_ENV=production USER node