From e60641960f37d2f238c9e1e181eede7f07d35728 Mon Sep 17 00:00:00 2001 From: Chris Anderson Date: Mon, 6 Mar 2023 20:44:12 -0600 Subject: [PATCH] Adds local storage provider --- .github/workflows/build.yml | 2 + apps/platform/.gitignore | 3 + apps/platform/Dockerfile | 1 + apps/platform/package.json | 2 + apps/platform/src/api.ts | 5 + apps/platform/src/config/env.ts | 25 +- .../src/storage/LocalStorageProvider.ts | 26 + .../platform/src/storage/S3StorageProvider.ts | 5 + apps/platform/src/storage/Storage.ts | 8 +- apps/platform/src/storage/StorageProvider.ts | 3 +- package-lock.json | 538 ++++++++++++------ 11 files changed, 441 insertions(+), 177 deletions(-) create mode 100644 apps/platform/src/storage/LocalStorageProvider.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a2eef9e..ac850aee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,8 @@ name: Create & Publish Docker Image on: push: + branches: + - "feat/local-storage" tags: - "v*.*.*" diff --git a/apps/platform/.gitignore b/apps/platform/.gitignore index c47c3096..dfe01e9b 100644 --- a/apps/platform/.gitignore +++ b/apps/platform/.gitignore @@ -107,3 +107,6 @@ build # VS Code .DS_Store + +# Uploads +/public/uploads diff --git a/apps/platform/Dockerfile b/apps/platform/Dockerfile index a9d14320..41daa82d 100644 --- a/apps/platform/Dockerfile +++ b/apps/platform/Dockerfile @@ -13,6 +13,7 @@ COPY --from=compile /usr/src/app/apps/platform/package*.json ./ COPY --from=compile /usr/src/app/apps/platform/build ./ COPY --from=compile /usr/src/app/apps/platform/db ./db COPY --from=compile /usr/src/app/apps/platform/scripts ./scripts +COPY --from=compile /usr/src/app/apps/platform/public ./public RUN npm ci --only=production # --------------> The production image diff --git a/apps/platform/package.json b/apps/platform/package.json index ab1a1bbf..f635aa30 100644 --- a/apps/platform/package.json +++ b/apps/platform/package.json @@ -30,6 +30,7 @@ "koa": "^2.13.4", "koa-body": "5.0.0", "koa-jwt": "^4.0.3", + "koa-static": "^5.0.0", "mysql2": "^2.3.3", "node-pushnotifications": "^2.0.3", "node-schedule": "^2.1.0", @@ -54,6 +55,7 @@ "@types/jsonwebtoken": "^8.5.9", "@types/koa__cors": "^3.3.0", "@types/koa__router": "^8.0.11", + "@types/koa-static": "^4.0.2", "@types/node": "^18.7.18", "@types/node-pushnotifications": "^1.0.4", "@types/node-schedule": "^2.1.0", diff --git a/apps/platform/src/api.ts b/apps/platform/src/api.ts index 0dcd1023..6686af85 100644 --- a/apps/platform/src/api.ts +++ b/apps/platform/src/api.ts @@ -1,6 +1,7 @@ import Koa from 'koa' import koaBody from 'koa-body' import cors from '@koa/cors' +import serve from 'koa-static' import controllers from './config/controllers' import { RequestError } from './core/errors' @@ -18,6 +19,10 @@ export default class Api extends Koa { ctx.state.app = this.app return next() }) + .use(serve('./public', { + hidden: true, + defer: true, + })) this.use(async (ctx, next) => { try { diff --git a/apps/platform/src/config/env.ts b/apps/platform/src/config/env.ts index 229ff1f3..b4b939c3 100644 --- a/apps/platform/src/config/env.ts +++ b/apps/platform/src/config/env.ts @@ -53,19 +53,20 @@ export default (type?: EnvType): Env => { }, }), }), - storage: { - baseUrl: process.env.STORAGE_BASE_URL!, - ...driver>(process.env.STORAGE_DRIVER, { - s3: () => ({ - bucket: process.env.AWS_S3_BUCKET!, - region: process.env.AWS_REGION!, - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID!, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, - }, - }), + storage: driver(process.env.STORAGE_DRIVER ?? 'local', { + s3: () => ({ + baseUrl: process.env.STORAGE_BASE_URL!, + bucket: process.env.AWS_S3_BUCKET!, + region: process.env.AWS_REGION!, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID!, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, + }, }), - }, + local: () => ({ + baseUrl: process.env.BASE_URL!, + }), + }), baseUrl: process.env.BASE_URL!, port: parseInt(process.env.PORT!), secret: process.env.APP_SECRET!, diff --git a/apps/platform/src/storage/LocalStorageProvider.ts b/apps/platform/src/storage/LocalStorageProvider.ts new file mode 100644 index 00000000..bf9ba508 --- /dev/null +++ b/apps/platform/src/storage/LocalStorageProvider.ts @@ -0,0 +1,26 @@ +import fs from 'fs/promises' +import path from 'path' +import { StorageTypeConfig } from './Storage' +import { ImageUploadTask, StorageProvider } from './StorageProvider' + +export interface LocalConfig extends StorageTypeConfig { + driver: 'local' +} + +export class LocalStorageProvider implements StorageProvider { + + path(filename: string) { + return path.join(process.cwd(), 'public', 'uploads', filename) + } + + async upload(task: ImageUploadTask) { + await fs.writeFile( + task.url, + task.stream, + ) + } + + async delete(filename: string): Promise { + await fs.unlink(filename) + } +} diff --git a/apps/platform/src/storage/S3StorageProvider.ts b/apps/platform/src/storage/S3StorageProvider.ts index f55cca55..d8ea9c12 100644 --- a/apps/platform/src/storage/S3StorageProvider.ts +++ b/apps/platform/src/storage/S3StorageProvider.ts @@ -6,6 +6,7 @@ import { StorageTypeConfig } from './Storage' import { ImageUploadTask, StorageProvider } from './StorageProvider' export interface S3Config extends StorageTypeConfig, AWSConfig { + driver: 's3' bucket: string } @@ -17,6 +18,10 @@ export class S3StorageProvider implements StorageProvider { this.config = config } + path(filename: string) { + return filename + } + async upload(task: ImageUploadTask) { const pass = new PassThrough() const s3 = new S3(this.config) diff --git a/apps/platform/src/storage/Storage.ts b/apps/platform/src/storage/Storage.ts index 860dc501..cc5994df 100644 --- a/apps/platform/src/storage/Storage.ts +++ b/apps/platform/src/storage/Storage.ts @@ -8,10 +8,12 @@ import { combineURLs, uuid } from '../utilities' import { InternalError } from '../core/errors' import StorageError from './StorageError' import App from '../app' +import { LocalConfig, LocalStorageProvider } from './LocalStorageProvider' -export type StorageConfig = S3Config & { baseUrl: string } +export type StorageConfig = S3Config | LocalConfig export interface StorageTypeConfig extends DriverConfig { driver: StorageProviderName + baseUrl: string } export interface ImageUpload { @@ -24,6 +26,8 @@ export default class Storage { constructor(config?: StorageConfig) { if (config?.driver === 's3') { this.provider = new S3StorageProvider(config) + } else if (config?.driver === 'local') { + this.provider = new LocalStorageProvider() } else { throw new InternalError(StorageError.UndefinedStorageMethod) } @@ -34,7 +38,7 @@ export default class Storage { const originalPath = path.parse(image.metadata.fileName) const extension = originalPath.ext const fileName = originalPath.name - const url = `${key}${extension}` + const url = this.provider.path(`${key}${extension}`) await this.upload({ stream: image.file, diff --git a/apps/platform/src/storage/StorageProvider.ts b/apps/platform/src/storage/StorageProvider.ts index 3ac1b555..ff7a6b86 100644 --- a/apps/platform/src/storage/StorageProvider.ts +++ b/apps/platform/src/storage/StorageProvider.ts @@ -1,6 +1,6 @@ import { Stream } from 'stream' -export type StorageProviderName = 's3' +export type StorageProviderName = 's3' | 'local' export interface ImageUploadTask { stream: Stream @@ -8,6 +8,7 @@ export interface ImageUploadTask { } export interface StorageProvider { + path(filename: string): string upload(task: ImageUploadTask): Promise delete(filename: string): Promise } diff --git a/package-lock.json b/package-lock.json index 0d0ef20d..c9d62b95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "koa": "^2.13.4", "koa-body": "5.0.0", "koa-jwt": "^4.0.3", + "koa-static": "^5.0.0", "mysql2": "^2.3.3", "node-pushnotifications": "^2.0.3", "node-schedule": "^2.1.0", @@ -58,6 +59,7 @@ "@types/jsonwebtoken": "^8.5.9", "@types/koa__cors": "^3.3.0", "@types/koa__router": "^8.0.11", + "@types/koa-static": "^4.0.2", "@types/node": "^18.7.18", "@types/node-pushnotifications": "^1.0.4", "@types/node-schedule": "^2.1.0", @@ -2531,14 +2533,6 @@ "dev": true, "license": "MIT" }, - "apps/platform/node_modules/@types/accepts": { - "version": "1.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "apps/platform/node_modules/@types/babel__core": { "version": "7.1.20", "dev": true, @@ -2584,22 +2578,6 @@ "@types/node": "*" } }, - "apps/platform/node_modules/@types/content-disposition": { - "version": "0.5.5", - "dev": true, - "license": "MIT" - }, - "apps/platform/node_modules/@types/cookies": { - "version": "0.7.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, "apps/platform/node_modules/@types/formidable": { "version": "2.0.5", "license": "MIT", @@ -2615,16 +2593,6 @@ "@types/node": "*" } }, - "apps/platform/node_modules/@types/http-assert": { - "version": "1.5.3", - "dev": true, - "license": "MIT" - }, - "apps/platform/node_modules/@types/http-errors": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, "apps/platform/node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true, @@ -2679,26 +2647,6 @@ "@types/node": "*" } }, - "apps/platform/node_modules/@types/keygrip": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "apps/platform/node_modules/@types/koa": { - "version": "2.13.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/accepts": "*", - "@types/content-disposition": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/http-errors": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" - } - }, "apps/platform/node_modules/@types/koa__cors": { "version": "3.3.0", "dev": true, @@ -2715,14 +2663,6 @@ "@types/koa": "*" } }, - "apps/platform/node_modules/@types/koa-compose": { - "version": "3.2.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/koa": "*" - } - }, "apps/platform/node_modules/@types/node-pushnotifications": { "version": "1.0.4", "dev": true, @@ -8621,10 +8561,6 @@ "node": ">=0.10.0" } }, - "apps/platform/node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, "apps/platform/node_modules/side-channel": { "version": "1.0.4", "license": "MIT", @@ -9294,13 +9230,6 @@ "node": ">=0.10.0" } }, - "apps/platform/node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "apps/platform/node_modules/touch": { "version": "3.1.0", "dev": true, @@ -9864,7 +9793,6 @@ "@types/uuid": "^9.0.0", "autoprefixer": "^10.4.12", "axios": "0.27.2", - "bootstrap-icons": "1.10.3", "clsx": "^1.2.1", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", @@ -14674,10 +14602,6 @@ "version": "1.0.0", "license": "ISC" }, - "apps/ui/node_modules/bootstrap-icons": { - "version": "1.10.3", - "license": "MIT" - }, "apps/ui/node_modules/browser-process-hrtime": { "version": "1.0.0", "license": "BSD-2-Clause" @@ -21468,10 +21392,6 @@ "node": ">= 0.8.0" } }, - "apps/ui/node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, "apps/ui/node_modules/shell-quote": { "version": "1.7.4", "license": "MIT", @@ -22096,13 +22016,6 @@ "version": "1.0.6", "license": "MIT" }, - "apps/ui/node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, "apps/ui/node_modules/tough-cookie": { "version": "4.1.2", "license": "BSD-3-Clause", @@ -24818,6 +24731,15 @@ "node": ">= 10" } }, + "node_modules/@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -24835,6 +24757,24 @@ "@types/node": "*" } }, + "node_modules/@types/content-disposition": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz", + "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==", + "dev": true + }, + "node_modules/@types/cookies": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", + "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -24864,6 +24804,68 @@ "@types/range-parser": "*" } }, + "node_modules/@types/http-assert": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", + "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==", + "dev": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "node_modules/@types/keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==", + "dev": true + }, + "node_modules/@types/koa": { + "version": "2.13.5", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz", + "integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==", + "dev": true, + "dependencies": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "node_modules/@types/koa-compose": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", + "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", + "dev": true, + "dependencies": { + "@types/koa": "*" + } + }, + "node_modules/@types/koa-send": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/koa-send/-/koa-send-4.1.3.tgz", + "integrity": "sha512-daaTqPZlgjIJycSTNjKpHYuKhXYP30atFc1pBcy6HHqB9+vcymDgYTguPdx9tO4HMOqNyz6bz/zqpxt5eLR+VA==", + "dev": true, + "dependencies": { + "@types/koa": "*" + } + }, + "node_modules/@types/koa-static": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/koa-static/-/koa-static-4.0.2.tgz", + "integrity": "sha512-ns/zHg+K6XVPMuohjpOlpkR1WLa4VJ9czgUP9bxkCDn0JZBtUWbD/wKDZzPGDclkQK1bpAEScufCHOy8cbfL0w==", + "dev": true, + "dependencies": { + "@types/koa": "*", + "@types/koa-send": "*" + } + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -25934,7 +25936,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -26733,6 +26734,21 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -27335,6 +27351,39 @@ "node": ">=0.10.0" } }, + "node_modules/koa-send": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz", + "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==", + "dependencies": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "resolve-path": "^1.4.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/koa-static": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz", + "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==", + "dependencies": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + }, + "engines": { + "node": ">= 7.6.0" + } + }, + "node_modules/koa-static/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/lerna": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.4.1.tgz", @@ -29485,6 +29534,42 @@ "node": ">=8" } }, + "node_modules/resolve-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", + "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==", + "dependencies": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/resolve-path/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/resolve-path/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/resolve-path/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -29657,6 +29742,11 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -29841,6 +29931,14 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -30037,6 +30135,14 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -31936,6 +32042,15 @@ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, + "@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", @@ -31953,6 +32068,24 @@ "@types/node": "*" } }, + "@types/content-disposition": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.5.tgz", + "integrity": "sha512-v6LCdKfK6BwcqMo+wYW05rLS12S0ZO0Fl4w1h4aaZMD7bqT3gVUns6FvLJKGZHQmYn3SX55JWGpziwJRwVgutA==", + "dev": true + }, + "@types/cookies": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", + "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -31982,6 +32115,68 @@ "@types/range-parser": "*" } }, + "@types/http-assert": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.3.tgz", + "integrity": "sha512-FyAOrDuQmBi8/or3ns4rwPno7/9tJTijVW6aQQjK02+kOQ8zmoNg2XJtAuQhvQcy1ASJq38wirX5//9J1EqoUA==", + "dev": true + }, + "@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "@types/keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==", + "dev": true + }, + "@types/koa": { + "version": "2.13.5", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.5.tgz", + "integrity": "sha512-HSUOdzKz3by4fnqagwthW/1w/yJspTgppyyalPVbgZf8jQWvdIXcVW5h2DGtw4zYntOaeRGx49r1hxoPWrD4aA==", + "dev": true, + "requires": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "@types/koa-compose": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", + "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", + "dev": true, + "requires": { + "@types/koa": "*" + } + }, + "@types/koa-send": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@types/koa-send/-/koa-send-4.1.3.tgz", + "integrity": "sha512-daaTqPZlgjIJycSTNjKpHYuKhXYP30atFc1pBcy6HHqB9+vcymDgYTguPdx9tO4HMOqNyz6bz/zqpxt5eLR+VA==", + "dev": true, + "requires": { + "@types/koa": "*" + } + }, + "@types/koa-static": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/koa-static/-/koa-static-4.0.2.tgz", + "integrity": "sha512-ns/zHg+K6XVPMuohjpOlpkR1WLa4VJ9czgUP9bxkCDn0JZBtUWbD/wKDZzPGDclkQK1bpAEScufCHOy8cbfL0w==", + "dev": true, + "requires": { + "@types/koa": "*", + "@types/koa-send": "*" + } + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -32823,8 +33018,7 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" }, "deprecation": { "version": "2.3.1", @@ -33438,6 +33632,18 @@ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, "http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -33883,6 +34089,35 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, + "koa-send": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.1.tgz", + "integrity": "sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==", + "requires": { + "debug": "^4.1.1", + "http-errors": "^1.7.3", + "resolve-path": "^1.4.0" + } + }, + "koa-static": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz", + "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==", + "requires": { + "debug": "^3.1.0", + "koa-send": "^5.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, "lerna": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.4.1.tgz", @@ -35149,6 +35384,7 @@ "@types/jsonwebtoken": "^8.5.9", "@types/koa__cors": "^3.3.0", "@types/koa__router": "^8.0.11", + "@types/koa-static": "^4.0.2", "@types/node": "^18.7.18", "@types/node-pushnotifications": "^1.0.4", "@types/node-schedule": "^2.1.0", @@ -35175,6 +35411,7 @@ "koa": "^2.13.4", "koa-body": "5.0.0", "koa-jwt": "^4.0.3", + "koa-static": "^5.0.0", "mysql2": "^2.3.3", "node-pushnotifications": "^2.0.3", "node-schedule": "^2.1.0", @@ -36969,13 +37206,6 @@ "version": "1.0.3", "dev": true }, - "@types/accepts": { - "version": "1.3.5", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/babel__core": { "version": "7.1.20", "dev": true, @@ -37016,20 +37246,6 @@ "@types/node": "*" } }, - "@types/content-disposition": { - "version": "0.5.5", - "dev": true - }, - "@types/cookies": { - "version": "0.7.7", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, "@types/formidable": { "version": "2.0.5", "requires": { @@ -37043,14 +37259,6 @@ "@types/node": "*" } }, - "@types/http-assert": { - "version": "1.5.3", - "dev": true - }, - "@types/http-errors": { - "version": "2.0.1", - "dev": true - }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true @@ -37097,24 +37305,6 @@ "@types/node": "*" } }, - "@types/keygrip": { - "version": "1.0.2", - "dev": true - }, - "@types/koa": { - "version": "2.13.5", - "dev": true, - "requires": { - "@types/accepts": "*", - "@types/content-disposition": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/http-errors": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" - } - }, "@types/koa__cors": { "version": "3.3.0", "dev": true, @@ -37129,13 +37319,6 @@ "@types/koa": "*" } }, - "@types/koa-compose": { - "version": "3.2.5", - "dev": true, - "requires": { - "@types/koa": "*" - } - }, "@types/node-pushnotifications": { "version": "1.0.4", "dev": true, @@ -40908,9 +41091,6 @@ "split-string": "^3.0.1" } }, - "setprototypeof": { - "version": "1.2.0" - }, "side-channel": { "version": "1.0.4", "requires": { @@ -41367,9 +41547,6 @@ } } }, - "toidentifier": { - "version": "1.0.1" - }, "touch": { "version": "3.1.0", "dev": true, @@ -42071,6 +42248,38 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, + "resolve-path": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz", + "integrity": "sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==", + "requires": { + "http-errors": "~1.6.2", + "path-is-absolute": "1.0.1" + }, + "dependencies": { + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -42185,6 +42394,11 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -42330,6 +42544,11 @@ "minipass": "^3.1.1" } }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -42477,6 +42696,11 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -42570,7 +42794,6 @@ "@typescript-eslint/eslint-plugin": "^5.43.0", "autoprefixer": "^10.4.12", "axios": "0.27.2", - "bootstrap-icons": "1.10.3", "clsx": "^1.2.1", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", @@ -45433,9 +45656,6 @@ "boolbase": { "version": "1.0.0" }, - "bootstrap-icons": { - "version": "1.10.3" - }, "browser-process-hrtime": { "version": "1.0.0" }, @@ -49415,9 +49635,6 @@ "send": "0.18.0" } }, - "setprototypeof": { - "version": "1.2.0" - }, "shell-quote": { "version": "1.7.4" }, @@ -49811,9 +50028,6 @@ "toggle-selection": { "version": "1.0.6" }, - "toidentifier": { - "version": "1.0.1" - }, "tough-cookie": { "version": "4.1.2", "requires": {