diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0d15eb29c..1412d615b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,5 +5,5 @@ "service": "devenv", "remoteUser": "ubuntu", - "workspaceFolder": "/home/ubuntu/zeppelin" + "workspaceFolder": "/workspace/zeppelin" } diff --git a/.dockerignore b/.dockerignore index fa757ae23..00b225451 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,4 +8,97 @@ node_modules /backend/dist +/shared/dist /dashboard/dist + +# FROM GITIGNORE: + +# Created by .ignore support plugin (hsz.mobi) +### Node template +# Logs +/logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.clinic +.clinic-bot +.clinic-api + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +*.env +.env + +# windows folder options +desktop.ini + +# PHPStorm +.idea/ + +# Misc +/convert.js +/startscript.js +.cache +npm-ls.txt +npm-audit.txt +.vscode/launch.json + +# Debug files +*.debug.ts +*.debug.js + +.vscode/ + +config-errors.txt +/config-schema.json + +*.tsbuildinfo diff --git a/.env.example b/.env.example index aadba71bd..073d0b7d4 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,7 @@ +# ========================== +# GENERAL OPTIONS +# ========================== + # 32 character encryption key KEY= @@ -17,58 +21,63 @@ STAFF= # A comma-separated list of server IDs that should be allowed by default DEFAULT_ALLOWED_SERVERS= -# When using the Docker-based development environment, this is only used internally. The API will be available at localhost:DOCKER_DEV_WEB_PORT/api. -API_PORT=3000 - # Only required if relevant feature is used #PHISHERMAN_API_KEY= -# The user ID and group ID that should be used within the Docker containers -# This should match your own user ID and group ID. Run `id -u` and `id -g` to find them. -DOCKER_USER_UID= -DOCKER_USER_GID= -# -# DOCKER (DEVELOPMENT) -# NOTE: You only need to fill in these values for running the development environment. See production config further below. -# +# ========================== +# DEVELOPMENT +# NOTE: You only need to fill in these values for running the development environment +# ========================== -DOCKER_DEV_WEB_PORT=3300 +DEVELOPMENT_WEB_PORT=3300 # The MySQL database running in the container is exposed to the host on this port, # allowing access with database tools such as DBeaver -DOCKER_DEV_MYSQL_PORT=3001 +DEVELOPMENT_MYSQL_PORT=3356 # Password for the Zeppelin database user -DOCKER_DEV_MYSQL_PASSWORD= +DEVELOPMENT_MYSQL_PASSWORD=password # Password for the MySQL root user -DOCKER_DEV_MYSQL_ROOT_PASSWORD= +DEVELOPMENT_MYSQL_ROOT_PASSWORD=password # The development environment container has an SSH server that you can connect to. # This is the port that server is exposed to the host on. -DOCKER_DEV_SSH_PORT=3002 -DOCKER_DEV_SSH_PASSWORD=password +DEVELOPMENT_SSH_PORT=3022 +DEVELOPMENT_SSH_PASSWORD=password # If your user has a different UID than 1000, you might have to fill that in here to avoid permission issues -#DOCKER_DEV_UID=1000 +#DEVELOPMENT_UID=1000 + + +# ========================== +# PRODUCTION - STANDALONE +# NOTE: You only need to fill in these values for running the standalone production environment +# ========================== + +STANDALONE_DOMAIN= -# -# DOCKER (PRODUCTION) -# NOTE: You only need to fill in these values for running the production environment. See development config above. -# +STANDALONE_WEB_PORT=443 -DOCKER_PROD_DOMAIN= -DOCKER_PROD_WEB_PORT=443 # The MySQL database running in the container is exposed to the host on this port, # allowing access with database tools such as DBeaver -DOCKER_PROD_MYSQL_PORT=3001 +STANDALONE_MYSQL_PORT=3356 # Password for the Zeppelin database user -DOCKER_PROD_MYSQL_PASSWORD= +STANDALONE_MYSQL_PASSWORD= # Password for the MySQL root user -DOCKER_PROD_MYSQL_ROOT_PASSWORD= - -# You only need to set these if you're running an external database. -# In a standard setup, the database is run in a docker container. -#DB_HOST= -#DB_USER= -#DB_PASSWORD= -#DB_DATABASE= +STANDALONE_MYSQL_ROOT_PASSWORD= + + +# ========================== +# PRODUCTION - LIGHTWEIGHT +# NOTE: You only need to fill in these values for running the lightweight production environment +# ========================== + +# Ports where the API/dashboard are exposed on the host +LIGHTWEIGHT_API_PORT=3001 +LIGHTWEIGHT_DASHBOARD_PORT=3002 + +LIGHTWEIGHT_DB_HOST= +LIGHTWEIGHT_DB_PORT= +LIGHTWEIGHT_DB_USER= +LIGHTWEIGHT_DB_PASSWORD= +LIGHTWEIGHT_DB_DATABASE= diff --git a/.gitignore b/.gitignore index e2c7baa6c..fe2e0caea 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,7 @@ config-errors.txt /config-schema.json *.tsbuildinfo + +# Legacy data folders +/docker/development/data +/docker/production/data diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..42b88884b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM node:20 +USER node + +COPY --chown=node:node . /zeppelin + +# Install dependencies for all packages +WORKDIR /zeppelin +RUN npm ci + +# Build backend +WORKDIR /zeppelin/backend +RUN npm run build + +# Build dashboard +WORKDIR /zeppelin/dashboard +RUN npm run build + +# Prune dev dependencies +WORKDIR /zeppelin +RUN npm prune --production diff --git a/PRODUCTION.md b/PRODUCTION.md index 4110aa959..8c57494b3 100644 --- a/PRODUCTION.md +++ b/PRODUCTION.md @@ -1,34 +1,67 @@ # Zeppelin production environment -Zeppelin's production environment - that is, the **bot, API, and dashboard** - uses Docker. +Zeppelin's production environment uses Docker. There are a few different ways to run Zeppelin in production: -## Starting the production environment +1. **Standalone** + * The easiest way to get Zeppelin up and running. This setup comes with a built-in database and web server. +2. **Lightweight** + * In case you don't want to use the built-in database and web server. This setup only runs the bot, API, and dashboard themselves. You'll have to provide your own database connection options and set up a proxy server for the API and dashboard. +3. **Manual** + * If you only want to run a specific service, you can use Zeppelin's Dockerfile directly. + +## Standalone + +### Setup 1. Install Docker on the machine running the bot 2. Make a copy of `.env.example` called `.env` -3. Fill in the missing values in `.env` -4. Run `docker compose -f docker-compose.production.yml build` -5. Run `docker compose -f docker-compose.production.yml up -d` +3. Fill in the missing values in `.env` (including the "PRODUCTION - STANDALONE" section) + +**Note:** The dashboard and API are exposed with a self-signed certificate. It is recommended to set up a proxy with a proper certificate in front of them. A popular option for this is [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/). + +### Running the bot +`docker compose -f docker-compose.standalone.yml up -d` + +### Shutting the bot down +`docker compose -f docker-compose.standalone.yml down` + +### Updating the bot +1. Shut the bot down +2. Update the files (e.g. `git pull`) +3. Rebuild: `docker compose -f docker-compose.standalone.yml build` +4. Run the bot again + +### Viewing logs +`docker compose -f docker-compose.standalone.yml logs -t -f` + +## Lightweight -**Note:** The dashboard and API are exposed with a self-signed certificate. It is recommended to set up a proxy with a proper certificate in front of them. Cloudflare is a popular choice here. +### Setup +1. Install Docker on the machine running the bot +2. Make a copy of `.env.example` called `.env` +3. Fill in the missing values in `.env` (including the "PRODUCTION - LIGHTWEIGHT" section) -## Updating the bot +### Running the bot +`docker compose -f docker-compose.lightweight.yml up -d` -### One-click script -If you've downloaded the bot's files by cloning the git repository, you can use `update.sh` to update the bot. +### Shutting the bot down +`docker compose -f docker-compose.lightweight.yml down` -### Manual instructions -1. Shut the bot down: `docker compose -f docker-compose.production.yml down` +### Updating the bot +1. Shut the bot down 2. Update the files (e.g. `git pull`) -3. Build new images: `docker compose -f docker-compose.production.yml build` -3. Start the bot again: `docker compose -f docker-compose.production.yml up -d` +3. Rebuild: `docker compose -f docker-compose.lightweight.yml build` +4. Run the bot again + +### Viewing logs +`docker compose -f docker-compose.lightweight.yml logs -t -f` -### Ephemeral hotfixes -If you need to make a hotfix to the bot's source files directly on the server: -1. Shut the bot down: `docker compose -f docker-compose.production.yml down` -2. Make your edits -3. Build new images: `docker compose -f docker-compose.production.yml build` -4. Start the bot again: `docker compose -f docker-compose.production.yml up -d` +## Manual +1. Build the Zeppelin image: `docker build --tag 'zeppelin' .` +2. Run the service: + * Bot: `docker run zeppelin npm run start-bot` + * API: `docker run zeppelin npm run start-api` + * Dashboard: `docker run zeppelin npm run start-dashboard` -Make sure to revert any hotfixes before updating the bot normally. +If you're using an application platform such as Railway, you can simply point it to Zeppelin's repository and it should pick up the Dockerfile from there. +For the start command, you can use the same commands as above: `npm run start-bot`, `npm run start-api`, `npm run start-dashboard`. -## View logs -To view real-time logs, run `docker compose -f docker-compose.production.yml logs -t -f` +**Note:** You'll need to provide the necessary env variables. For example, `docker run --env-file .env zeppelin` diff --git a/backend/package.json b/backend/package.json index 9c5d47836..ff9132b7f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,28 +4,28 @@ "description": "", "private": true, "scripts": { - "watch": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node start-dev.js\"", - "watch-yaml-parse-test": "cross-env NODE_ENV=development tsc-watch --onSuccess \"node dist/backend/src/yamlParseTest.js\"", + "watch": "NODE_ENV=development HOST_MODE=development tsc-watch --onSuccess \"node start-dev.js\"", + "watch-yaml-parse-test": "NODE_ENV=development HOST_MODE=development tsc-watch --onSuccess \"node dist/backend/src/yamlParseTest.js\"", "build": "tsc --build", - "start-bot-dev": "cross-env NODE_ENV=development node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/backend/src/index.js", + "start-bot-dev": "NODE_ENV=development HOST_MODE=development node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/backend/src/index.js", "start-bot-dev-debug": "NODE_ENV=development DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/backend/src/index.js", "start-bot-prod": "cross-env NODE_ENV=production node --enable-source-maps --stack-trace-limit=30 dist/backend/src/index.js", "start-bot-prod-debug": "NODE_ENV=production DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 dist/backend/src/index.js", - "watch-bot": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-bot-dev\"", - "start-api-dev": "cross-env NODE_ENV=development node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/backend/src/api/index.js", + "watch-bot": "NODE_ENV=development HOST_MODE=development tsc-watch --onSuccess \"npm run start-bot-dev\"", + "start-api-dev": "NODE_ENV=development HOST_MODE=development node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/backend/src/api/index.js", "start-api-dev-debug": "NODE_ENV=development DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/backend/src/api/index.js", "start-api-prod": "cross-env NODE_ENV=production node --enable-source-maps --stack-trace-limit=30 dist/backend/src/api/index.js", "start-api-prod-debug": "NODE_ENV=production DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 dist/backend/src/api/index.js", - "watch-api": "cross-env NODE_ENV=development tsc-watch --onSuccess \"npm run start-api-dev\"", + "watch-api": "NODE_ENV=development HOST_MODE=development tsc-watch --onSuccess \"npm run start-api-dev\"", "typeorm": "node ./node_modules/typeorm/cli.js", "migrate": "npm run typeorm -- migration:run -d dist/backend/src/data/dataSource.js", "migrate-prod": "cross-env NODE_ENV=production npm run migrate", - "migrate-dev": "cross-env NODE_ENV=development npm run build && npm run migrate", + "migrate-dev": "NODE_ENV=development HOST_MODE=development npm run build && npm run migrate", "migrate-rollback": "npm run typeorm -- migration:revert -d dist/backend/src/data/dataSource.js", "migrate-rollback-prod": "cross-env NODE_ENV=production npm run migrate", - "migrate-rollback-dev": "cross-env NODE_ENV=development npm run build && npm run migrate", - "validate-active-configs": "cross-env NODE_ENV=development node --enable-source-maps dist/backend/src/validateActiveConfigs.js > ../config-errors.txt", - "export-config-json-schema": "cross-env NODE_ENV=development node --enable-source-maps dist/backend/src/exportSchemas.js > ../config-schema.json", + "migrate-rollback-dev": "NODE_ENV=development HOST_MODE=development npm run build && npm run migrate", + "validate-active-configs": "NODE_ENV=development HOST_MODE=development node --enable-source-maps dist/backend/src/validateActiveConfigs.js > ../config-errors.txt", + "export-config-json-schema": "NODE_ENV=development HOST_MODE=development node --enable-source-maps dist/backend/src/exportSchemas.js > ../config-schema.json", "test": "npm run build && npm run run-tests", "run-tests": "ava", "test-watch": "tsc-watch --onSuccess \"npx ava\"" diff --git a/backend/src/api/start.ts b/backend/src/api/start.ts index 9006959a7..4e8985b02 100644 --- a/backend/src/api/start.ts +++ b/backend/src/api/start.ts @@ -51,7 +51,7 @@ app.use((req, res, next) => { return notFound(res); }); -const port = env.API_PORT; +const port = 3001; app.listen(port, "0.0.0.0", () => console.log(`API server listening on port ${port}`)); // tslint:disable-line startBackgroundTasks(); diff --git a/backend/src/data/dataSource.ts b/backend/src/data/dataSource.ts index badbf1871..d763f082a 100644 --- a/backend/src/data/dataSource.ts +++ b/backend/src/data/dataSource.ts @@ -1,6 +1,7 @@ import moment from "moment-timezone"; import path from "path"; import { DataSource } from "typeorm"; +import { MysqlConnectionOptions } from "typeorm/driver/mysql/MysqlConnectionOptions.js"; import { env } from "../env"; import { backendDir } from "../paths"; @@ -9,13 +10,39 @@ moment.tz.setDefault("UTC"); const entities = path.relative(process.cwd(), path.resolve(backendDir, "dist/backend/src/data/entities/*.js")); const migrations = path.relative(process.cwd(), path.resolve(backendDir, "dist/backend/src/migrations/*.js")); +type DbOpts = Pick; +let dbOpts: DbOpts; +if (env.HOST_MODE === "development") { + dbOpts = { + host: "mysql", + port: 3306, + username: "zeppelin", + password: env.DEVELOPMENT_MYSQL_PASSWORD, + database: "zeppelin", + }; +} else if (env.HOST_MODE === "standalone") { + dbOpts = { + host: "mysql", + port: 3306, + username: "zeppelin", + password: env.STANDALONE_MYSQL_PASSWORD, + database: "zeppelin", + }; +} else if (env.HOST_MODE === "lightweight") { + dbOpts = { + host: env.LIGHTWEIGHT_DB_HOST, + port: env.LIGHTWEIGHT_DB_PORT, + username: env.LIGHTWEIGHT_DB_USER, + password: env.LIGHTWEIGHT_DB_PASSWORD, + database: env.LIGHTWEIGHT_DB_DATABASE, + }; +} else { + throw new Error(`Unknown host mode: ${env.HOST_MODE}`); +} + export const dataSource = new DataSource({ type: "mysql", - host: env.DB_HOST, - port: env.DB_PORT, - username: env.DB_USER, - password: env.DB_PASSWORD, - database: env.DB_DATABASE, + ...dbOpts, charset: "utf8mb4", supportBigNumbers: true, bigNumberStrings: true, diff --git a/backend/src/env.ts b/backend/src/env.ts index 0a65e3a42..4ec20e08e 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -13,7 +13,6 @@ const envType = z.object({ DASHBOARD_URL: z.string().url(), API_URL: z.string().url(), - API_PORT: z.preprocess((v) => Number(v), z.number().min(1).max(65535)).default(3000), STAFF: z .preprocess( @@ -39,18 +38,16 @@ const envType = z.object({ PHISHERMAN_API_KEY: z.string().optional(), - DOCKER_DEV_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in development - DOCKER_PROD_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in production + DEVELOPMENT_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in development + STANDALONE_MYSQL_PASSWORD: z.string().optional(), // Included here for the DB_PASSWORD default in production - DB_HOST: z.string().optional().default("mysql"), - DB_PORT: z - .preprocess((v) => Number(v), z.number()) - .optional() - .default(3306), - DB_USER: z.string().optional().default("zeppelin"), - DB_PASSWORD: z.string().optional(), // Default is set to DOCKER_MYSQL_PASSWORD further below - DB_DATABASE: z.string().optional().default("zeppelin"), + LIGHTWEIGHT_DB_HOST: z.string().optional(), + LIGHTWEIGHT_DB_PORT: z.preprocess((v) => Number(v), z.number()).optional(), + LIGHTWEIGHT_DB_USER: z.string().optional(), + LIGHTWEIGHT_DB_PASSWORD: z.string().optional(), + LIGHTWEIGHT_DB_DATABASE: z.string().optional(), + HOST_MODE: z.enum(["development", "standalone", "lightweight"]).optional().default("lightweight"), DEBUG: z .string() .optional() @@ -65,11 +62,3 @@ if (fs.existsSync(envPath)) { } export const env = envType.parse(toValidate); - -if (!env.DB_PASSWORD) { - if (process.env.NODE_ENV === "production" && env.DOCKER_PROD_MYSQL_PASSWORD) { - env.DB_PASSWORD = env.DOCKER_PROD_MYSQL_PASSWORD; - } else if (env.DOCKER_DEV_MYSQL_PASSWORD) { - env.DB_PASSWORD = env.DOCKER_DEV_MYSQL_PASSWORD; - } -} diff --git a/backend/src/index.ts b/backend/src/index.ts index 6dad14b14..f26f828fc 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -164,7 +164,7 @@ if (process.env.NODE_ENV === "production") { } // Verify required Node.js version -const REQUIRED_NODE_VERSION = "14.0.0"; +const REQUIRED_NODE_VERSION = "16.9.0"; const requiredParts = REQUIRED_NODE_VERSION.split(".").map((v) => parseInt(v, 10)); const actualVersionParts = process.versions.node.split(".").map((v) => parseInt(v, 10)); for (const [i, part] of actualVersionParts.entries()) { diff --git a/dashboard/package.json b/dashboard/package.json index 269e9d6d7..5b0c398b9 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -6,8 +6,7 @@ "scripts": { "build": "rimraf dist && cross-env NODE_ENV=production webpack --config webpack.config.js", "build-debug": "rimraf dist && cross-env NODE_ENV=development webpack --config webpack.config.js", - "watch": "cross-env NODE_ENV=development webpack-dev-server", - "watch-build": "rimraf dist && NODE_ENV=development webpack --config webpack.config.js --watch" + "watch": "cross-env NODE_ENV=development webpack-dev-server" }, "devDependencies": { "@babel/core": "^7.22.5", @@ -37,7 +36,9 @@ "webpack-merge": "^5.9.0" }, "dependencies": { + "@fastify/static": "^7.0.1", "@highlightjs/vue-plugin": "^1.0.2", + "fastify": "^4.26.2", "highlight.js": "^11.8.0", "humanize-duration": "^3.27.0", "js-yaml": "^4.1.0", diff --git a/dashboard/serve.js b/dashboard/serve.js new file mode 100644 index 000000000..be2054148 --- /dev/null +++ b/dashboard/serve.js @@ -0,0 +1,14 @@ +const fastify = require("fastify")({ logger: true }); +const fastifyStatic = require("@fastify/static"); +const path = require("path"); + +fastify.register(fastifyStatic, { + root: path.join(__dirname, "dist"), +}); + +fastify.listen({ port: 3002, host: '0.0.0.0' }, (err, address) => { + if (err) { + throw err; + } + console.log(`Server listening on ${address}`); +}); diff --git a/dashboard/webpack.config.js b/dashboard/webpack.config.js index 4af49e33f..9f21c8e9f 100644 --- a/dashboard/webpack.config.js +++ b/dashboard/webpack.config.js @@ -175,7 +175,7 @@ if (process.env.NODE_ENV === "production") { devServer: { ...(process.env.DEV_HOST ? { host: process.env.DEV_HOST } : undefined), historyApiFallback: true, - port: 1234, + port: 3002, }, }); } diff --git a/docker-compose.development.yml b/docker-compose.development.yml index d64aa3939..6d4baf6cd 100644 --- a/docker-compose.development.yml +++ b/docker-compose.development.yml @@ -1,30 +1,25 @@ version: '3' name: zeppelin-dev volumes: - vscode-remote: {} - vscode-server: {} - jetbrains-data: {} + home: {} services: nginx: build: context: ./docker/development/nginx args: - DOCKER_DEV_WEB_PORT: ${DOCKER_DEV_WEB_PORT:?Missing DOCKER_DEV_WEB_PORT} - API_PORT: ${API_PORT:?Missing API_PORT} + DEVELOPMENT_WEB_PORT: ${DEVELOPMENT_WEB_PORT:?Missing DEVELOPMENT_WEB_PORT} ports: - - "${DOCKER_DEV_WEB_PORT:?Missing DOCKER_DEV_WEB_PORT}:443" - volumes: - - ./:/zeppelin + - "${DEVELOPMENT_WEB_PORT:?Missing DEVELOPMENT_WEB_PORT}:443" mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD: ${DOCKER_DEV_MYSQL_ROOT_PASSWORD?:Missing DOCKER_DEV_MYSQL_ROOT_PASSWORD} + MYSQL_ROOT_PASSWORD: ${DEVELOPMENT_MYSQL_ROOT_PASSWORD?:Missing DEVELOPMENT_MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: zeppelin MYSQL_USER: zeppelin - MYSQL_PASSWORD: ${DOCKER_DEV_MYSQL_PASSWORD?:Missing DOCKER_DEV_MYSQL_PASSWORD} + MYSQL_PASSWORD: ${DEVELOPMENT_MYSQL_PASSWORD?:Missing DEVELOPMENT_MYSQL_PASSWORD} ports: - - ${DOCKER_DEV_MYSQL_PORT:?Missing DOCKER_DEV_MYSQL_PORT}:3306 + - ${DEVELOPMENT_MYSQL_PORT:?Missing DEVELOPMENT_MYSQL_PORT}:3306 volumes: - ./docker/development/data/mysql:/var/lib/mysql command: --authentication-policy=mysql_native_password @@ -33,13 +28,10 @@ services: build: context: ./docker/development/devenv args: - DOCKER_DEV_SSH_PASSWORD: ${DOCKER_DEV_SSH_PASSWORD:?Missing DOCKER_DEV_SSH_PASSWORD} - DOCKER_DEV_UID: ${DOCKER_DEV_UID:-1000} + DEVELOPMENT_SSH_PASSWORD: ${DEVELOPMENT_SSH_PASSWORD:?Missing DEVELOPMENT_SSH_PASSWORD} + DEVELOPMENT_UID: ${DEVELOPMENT_UID:-1000} ports: - - "${DOCKER_DEV_SSH_PORT:?Missing DOCKER_DEV_SSH_PORT}:22" + - "${DEVELOPMENT_SSH_PORT:?Missing DEVELOPMENT_SSH_PORT}:22" volumes: - - ./:/home/ubuntu/zeppelin - - ~/.ssh:/home/ubuntu/.ssh - - vscode-remote:/home/ubuntu/.vscode-remote - - vscode-server:/home/ubuntu/.vscode-server - - jetbrains-data:/home/ubuntu/.cache/JetBrains + - home:/home/ubuntu + - ./:/workspace/zeppelin diff --git a/docker-compose.lightweight.yml b/docker-compose.lightweight.yml new file mode 100644 index 000000000..fa0839e74 --- /dev/null +++ b/docker-compose.lightweight.yml @@ -0,0 +1,60 @@ +version: '3' +name: zeppelin-prod +services: + migrate: + depends_on: + mysql: + condition: service_healthy + build: + context: . + dockerfile: docker/production/zeppelin/Dockerfile + environment: + HOST_MODE: lightweight + working_dir: /zeppelin + command: ["npm", "run", "migrate-prod"] + + api: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + dockerfile: docker/production/zeppelin/Dockerfile + restart: on-failure + environment: + DEBUG: ${DEBUG-} + HOST_MODE: lightweight + ports: + - "${LIGHTWEIGHT_API_PORT}:3001" + working_dir: /zeppelin/backend + command: ["npm", "run", "start-api-prod"] + + bot: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + dockerfile: docker/production/zeppelin/Dockerfile + restart: on-failure + environment: + DEBUG: ${DEBUG-} + HOST_MODE: lightweight + working_dir: /zeppelin/backend + command: ["npm", "run", "start-bot-prod"] + + dashboard: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + dockerfile: docker/production/zeppelin/Dockerfile + restart: on-failure + environment: + DEBUG: ${DEBUG-} + HOST_MODE: lightweight + ports: + - "${LIGHTWEIGHT_DASHBOARD_PORT}:3002" + working_dir: /zeppelin/dashboard + command: ["node", "serve.js"] diff --git a/docker-compose.production.yml b/docker-compose.production.yml deleted file mode 100644 index 7dfc145b6..000000000 --- a/docker-compose.production.yml +++ /dev/null @@ -1,65 +0,0 @@ -version: '3' -name: zeppelin-prod -services: - nginx: - build: - context: . - dockerfile: docker/production/nginx/Dockerfile - args: - API_PORT: ${API_PORT:?Missing API_PORT} - DOCKER_PROD_DOMAIN: ${DOCKER_PROD_DOMAIN:?Missing DOCKER_PROD_DOMAIN} - ports: - - "${DOCKER_PROD_WEB_PORT:?Missing DOCKER_PROD_WEB_PORT}:443" - volumes: - - ./:/zeppelin - - mysql: - image: mysql:8.0 - environment: - MYSQL_ROOT_PASSWORD: ${DOCKER_PROD_MYSQL_ROOT_PASSWORD?:Missing DOCKER_PROD_MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE: zeppelin - MYSQL_USER: zeppelin - MYSQL_PASSWORD: ${DOCKER_PROD_MYSQL_PASSWORD?:Missing DOCKER_PROD_MYSQL_PASSWORD} - ports: - - 127.0.0.1:${DOCKER_PROD_MYSQL_PORT:?Missing DOCKER_PROD_MYSQL_PORT}:3306 - volumes: - - ./docker/production/data/mysql:/var/lib/mysql - command: --authentication-policy=mysql_native_password - healthcheck: - test: "/usr/bin/mysql --user=root --password=\"${DOCKER_PROD_MYSQL_ROOT_PASSWORD}\" --execute \"SHOW DATABASES;\"" - interval: 5s - timeout: 300s - retries: 60 - - migrate: - depends_on: - mysql: - condition: service_healthy - build: - context: . - dockerfile: docker/production/backend/Dockerfile - command: ["npm", "run", "migrate-prod"] - - api: - depends_on: - migrate: - condition: service_completed_successfully - build: - context: . - dockerfile: docker/production/backend/Dockerfile - restart: on-failure - environment: - DEBUG: ${DEBUG-} - command: ["npm", "run", "start-api-prod"] - - bot: - depends_on: - migrate: - condition: service_completed_successfully - build: - context: . - dockerfile: docker/production/backend/Dockerfile - restart: on-failure - environment: - DEBUG: ${DEBUG-} - command: ["/bin/bash", "/zeppelin/docker/production/start-bot.sh"] diff --git a/docker-compose.standalone.yml b/docker-compose.standalone.yml new file mode 100644 index 000000000..195c2507e --- /dev/null +++ b/docker-compose.standalone.yml @@ -0,0 +1,84 @@ +version: '3' +name: zeppelin-prod +volumes: + mysql-data: +services: + mysql: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: ${STANDALONE_MYSQL_ROOT_PASSWORD?:Missing STANDALONE_MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: zeppelin + MYSQL_USER: zeppelin + MYSQL_PASSWORD: ${STANDALONE_MYSQL_PASSWORD?:Missing STANDALONE_MYSQL_PASSWORD} + ports: + - 127.0.0.1:${STANDALONE_MYSQL_PORT:?Missing STANDALONE_MYSQL_PORT}:3306 + # If you're upgrading from an older version, you can load your old database by switching the volumes below. + # Then, take a database dump from the old database, switch the volumes back, and load the dump into the new database. + volumes: + - mysql-data:/var/lib/mysql + # - ./docker/production/data/mysql:/var/lib/mysql + command: --authentication-policy=mysql_native_password + healthcheck: + test: "/usr/bin/mysql --user=root --password=\"${STANDALONE_MYSQL_ROOT_PASSWORD}\" --execute \"SHOW DATABASES;\"" + interval: 5s + timeout: 300s + retries: 60 + + nginx: + build: + context: . + dockerfile: docker/production/nginx/Dockerfile + args: + STANDALONE_DOMAIN: ${STANDALONE_DOMAIN:?Missing STANDALONE_DOMAIN} + ports: + - "${STANDALONE_WEB_PORT:?Missing STANDALONE_WEB_PORT}:443" + + migrate: + depends_on: + mysql: + condition: service_healthy + build: + context: . + environment: + HOST_MODE: standalone + working_dir: /zeppelin + command: ["npm", "run", "migrate-prod"] + + api: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + restart: on-failure + environment: + HOST_MODE: standalone + DEBUG: ${DEBUG-} + working_dir: /zeppelin/backend + command: ["npm", "run", "start-api-prod"] + + bot: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + restart: on-failure + environment: + HOST_MODE: standalone + DEBUG: ${DEBUG-} + working_dir: /zeppelin/backend + command: ["npm", "run", "start-bot-prod"] + + dashboard: + depends_on: + migrate: + condition: service_completed_successfully + build: + context: . + restart: on-failure + environment: + HOST_MODE: standalone + DEBUG: ${DEBUG-} + working_dir: /zeppelin/dashboard + command: ["node", "serve.js"] diff --git a/docker/development/devenv/Dockerfile b/docker/development/devenv/Dockerfile index 3b0afd8e1..84d6fcf4a 100644 --- a/docker/development/devenv/Dockerfile +++ b/docker/development/devenv/Dockerfile @@ -1,7 +1,7 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 -ARG DOCKER_DEV_UID -ARG DOCKER_DEV_SSH_PASSWORD +ARG DEVELOPMENT_UID +ARG DEVELOPMENT_SSH_PASSWORD ENV DEBIAN_FRONTEND=noninteractive ENV TZ=UTC @@ -15,15 +15,11 @@ RUN add-apt-repository ppa:git-core/ppa && apt-get update && apt-get install -y # Set up SSH access RUN apt-get install -y openssh-server iptables RUN mkdir /var/run/sshd -RUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u $DOCKER_DEV_UID ubuntu -RUN echo "ubuntu:${DOCKER_DEV_SSH_PASSWORD}" | chpasswd +RUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u $DEVELOPMENT_UID ubuntu +RUN echo "ubuntu:${DEVELOPMENT_SSH_PASSWORD}" | chpasswd -# Set up proper permissions for volumes -RUN mkdir -p /home/ubuntu/zeppelin /home/ubuntu/.vscode-remote /home/ubuntu/.vscode-server /home/ubuntu/.cache/JetBrains -RUN chown -R ubuntu /home/ubuntu - -# Install Node.js 18 and packages needed to build native packages -RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - +# Install Node.js 20 and packages needed to build native packages +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - RUN apt-get install -y nodejs gcc g++ make python3 CMD ["/usr/sbin/sshd", "-D", "-e"] diff --git a/docker/development/nginx/Dockerfile b/docker/development/nginx/Dockerfile index 6df4e1409..602a70773 100644 --- a/docker/development/nginx/Dockerfile +++ b/docker/development/nginx/Dockerfile @@ -1,11 +1,6 @@ FROM nginx -ARG API_PORT -ARG DOCKER_DEV_API_PORT -ARG DOCKER_DEV_DASHBOARD_PORT - RUN apt-get update && apt-get install -y openssl RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/localhost-cert.key -out /etc/ssl/certs/localhost-cert.pem -days 3650 -subj '/CN=localhost' -nodes COPY ./default.conf /etc/nginx/conf.d/default.conf -RUN sed -ir "s/_API_PORT_/${API_PORT}/g" /etc/nginx/conf.d/default.conf diff --git a/docker/development/nginx/default.conf b/docker/development/nginx/default.conf index 9dc3db696..c0fb42401 100644 --- a/docker/development/nginx/default.conf +++ b/docker/development/nginx/default.conf @@ -3,24 +3,22 @@ server { listen [::]:443 ssl http2; server_name localhost; - root /zeppelin/dashboard/dist; + # Using a variable here stops nginx from crashing if the dev container is restarted or becomes otherwise unavailable + set $backend_upstream "http://devenv:3001"; + set $dashboard_upstream "http://devenv:3002"; location / { - index index.html; - try_files $uri $uri/ /index.html; + # Using a variable in proxy_pass also requires resolver to be set. + # This is the address of the internal docker compose DNS server. + resolver 127.0.0.11; + proxy_pass $dashboard_upstream$uri$is_args$args; } - # Using a variable here stops nginx from crashing if the dev container is restarted or becomes otherwise unavailable - set $backend_upstream "http://devenv:_API_PORT_"; - location /api { # Remove /api/ from the beginning when passing the path to the API process rewrite /api(/.*)$ $1 break; - # Using a variable in proxy_pass also requires resolver to be set. - # This is the address of the internal docker compose DNS server. resolver 127.0.0.11; - proxy_pass $backend_upstream$uri$is_args$args; proxy_redirect off; diff --git a/docker/production/backend/Dockerfile b/docker/production/backend/Dockerfile deleted file mode 100644 index 63da6d1bd..000000000 --- a/docker/production/backend/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM node:18 -USER node - -COPY --chown=node:node . /zeppelin - -WORKDIR /zeppelin/backend -RUN npm ci && npm run build diff --git a/docker/production/config/mysql.conf.d/.gitignore b/docker/production/config/mysql.conf.d/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/docker/production/config/mysql.conf.d/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/docker/production/data/mysql/.gitignore b/docker/production/data/mysql/.gitignore deleted file mode 100644 index d6b7ef32c..000000000 --- a/docker/production/data/mysql/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/docker/production/nginx/Dockerfile b/docker/production/nginx/Dockerfile index 32883f345..c49d9c9c5 100644 --- a/docker/production/nginx/Dockerfile +++ b/docker/production/nginx/Dockerfile @@ -1,23 +1,9 @@ -FROM node:18 AS builder -USER node - -COPY --chown=node:node . /zeppelin - -WORKDIR /zeppelin/dashboard -RUN ls -lah -RUN pwd -RUN npm ci && npm run build - FROM nginx -ARG API_PORT -ARG DOCKER_PROD_DOMAIN +ARG STANDALONE_DOMAIN RUN apt-get update && apt-get install -y openssl -RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/zeppelin-self-signed-cert.key -out /etc/ssl/certs/zeppelin-self-signed-cert.pem -days 3650 -subj "/CN=${DOCKER_PROD_DOMAIN}" -nodes +RUN openssl req -x509 -newkey rsa:4096 -keyout /etc/ssl/private/zeppelin-self-signed-cert.key -out /etc/ssl/certs/zeppelin-self-signed-cert.pem -days 3650 -subj "/CN=${STANDALONE_DOMAIN}" -nodes COPY ./docker/production/nginx/default.conf /etc/nginx/conf.d/default.conf -RUN sed -ir "s/_API_PORT_/${API_PORT}/g" /etc/nginx/conf.d/default.conf -RUN sed -ir "s/_DOCKER_PROD_DOMAIN_/${DOCKER_PROD_DOMAIN}/g" /etc/nginx/conf.d/default.conf - -COPY --from=builder /zeppelin/dashboard/dist /var/www +RUN sed -ir "s/_STANDALONE_DOMAIN_/${STANDALONE_DOMAIN}/g" /etc/nginx/conf.d/default.conf diff --git a/docker/production/nginx/default.conf b/docker/production/nginx/default.conf index 43cfe0f9b..52001d38f 100644 --- a/docker/production/nginx/default.conf +++ b/docker/production/nginx/default.conf @@ -1,26 +1,24 @@ server { listen 443 ssl http2; listen [::]:443 ssl http2; - server_name _DOCKER_PROD_DOMAIN_; + server_name _STANDALONE_DOMAIN_; - root /var/www; + # Using a variable here stops nginx from crashing if the dev container is restarted or becomes otherwise unavailable + set $backend_upstream "http://api:3001"; + set $dashboard_upstream "http://dashboard:3002"; location / { - index index.html; - try_files $uri $uri/ /index.html; + # Using a variable in proxy_pass also requires resolver to be set. + # This is the address of the internal docker compose DNS server. + resolver 127.0.0.11; + proxy_pass $dashboard_upstream$uri$is_args$args; } - # Using a variable here stops nginx from crashing if the dev container is restarted or becomes otherwise unavailable - set $backend_upstream "http://api:_API_PORT_"; - location /api { # Remove /api/ from the beginning when passing the path to the API process rewrite /api(/.*)$ $1 break; - # Using a variable in proxy_pass also requires resolver to be set. - # This is the address of the internal docker compose DNS server. resolver 127.0.0.11; - proxy_pass $backend_upstream$uri$is_args$args; proxy_redirect off; diff --git a/docker/production/start-bot.sh b/docker/production/start-bot.sh deleted file mode 100644 index f0213cb90..000000000 --- a/docker/production/start-bot.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# This wrapper script is used to run different things based on the DEBUG env variable -# Exec is used to forward signals: https://unix.stackexchange.com/a/196053 - -cd /zeppelin/backend -if [ "$DEBUG" == "true" ]; then - echo "DEBUG MODE: Starting bot container without starting the bot" - exec tail -f /dev/null -else - echo "Starting bot" - exec npm run start-bot-prod -fi diff --git a/package-lock.json b/package-lock.json index 7967c6275..101c528cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -153,7 +153,9 @@ "name": "@zeppelinbot/dashboard", "version": "1.0.0", "dependencies": { + "@fastify/static": "^7.0.1", "@highlightjs/vue-plugin": "^1.0.2", + "fastify": "^4.26.2", "highlight.js": "^11.8.0", "humanize-duration": "^3.27.0", "js-yaml": "^4.1.0", @@ -3247,6 +3249,44 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fastify/accept-negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", + "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", + "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@fastify/busboy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", @@ -3255,6 +3295,106 @@ "node": ">=14" } }, + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@fastify/send": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", + "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", + "dependencies": { + "@lukeed/ms": "^2.0.1", + "escape-html": "~1.0.3", + "fast-decode-uri-component": "^1.0.1", + "http-errors": "2.0.0", + "mime": "^3.0.0" + } + }, + "node_modules/@fastify/send/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@fastify/static": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.1.tgz", + "integrity": "sha512-i1p/nELMknAisNfnjo7yhfoUOdKzA+n92QaMirv2NkZrJ1Wl12v2nyTYlDwPN8XoStMBAnRK/Kx6zKmfrXUPXw==", + "dependencies": { + "@fastify/accept-negotiator": "^1.0.0", + "@fastify/send": "^2.0.0", + "content-disposition": "^0.5.3", + "fastify-plugin": "^4.0.0", + "fastq": "^1.17.0", + "glob": "^10.3.4" + } + }, + "node_modules/@fastify/static/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@fastify/static/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@fastify/static/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@fullhuman/postcss-purgecss": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", @@ -3591,6 +3731,14 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "engines": { + "node": ">=8" + } + }, "node_modules/@nearform/heap-profiler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@nearform/heap-profiler/-/heap-profiler-2.0.0.tgz", @@ -4822,6 +4970,11 @@ "node": ">=6.5" } }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5106,6 +5259,11 @@ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -5736,6 +5894,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/avvio": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz", + "integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==", + "dependencies": { + "@fastify/error": "^3.3.0", + "archy": "^1.0.0", + "debug": "^4.0.0", + "fastq": "^1.17.1" + } + }, "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -10104,6 +10273,16 @@ "node >=0.6.0" ] }, + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10148,16 +10327,71 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "node_modules/fast-json-stringify": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.13.0.tgz", + "integrity": "sha512-XjTDWKHP3GoMQUOfnjYUbqeHeEt+PvYgvBdG2fRSmYaORILbSr8xTJvZX+w1YSAP5pw2NwKrGRmQleYueZEoxw==", + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.0", + "ajv": "^8.10.0", + "ajv-formats": "^2.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.4.0.tgz", + "integrity": "sha512-2gwPvyna0zwBdxKnng1suu/dTL5s8XEy2ZqH8mwDUwJdDkV8w5kp+JV26mupdK68HmPMbm6yjW9m7/Ys/BHEHg==", + "engines": { + "node": ">=6" + } + }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -10167,11 +10401,48 @@ "node": ">= 4.9.1" } }, + "node_modules/fastify": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.2.tgz", + "integrity": "sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^8.17.0", + "process-warning": "^3.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" + } + }, + "node_modules/fastify-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" + }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dependencies": { "reusify": "^1.0.4" } @@ -10415,6 +10686,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-my-way": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.1.0.tgz", + "integrity": "sha512-41QwjCGcVTODUmLLqTMeoHeiozbMXYMAE1CKFiDyi9zVZ2Vjh0yz3MF0WQZoIb+cmzP/XlbFjlF2NtJmvZHznA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -12834,6 +13118,14 @@ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -13055,6 +13347,24 @@ "node": ">= 0.8.0" } }, + "node_modules/light-my-request": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.12.0.tgz", + "integrity": "sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==", + "dependencies": { + "cookie": "^0.6.0", + "process-warning": "^3.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/light-my-request/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -14981,6 +15291,14 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -15670,6 +15988,87 @@ "node": ">=0.10.0" } }, + "node_modules/pino": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.19.0.tgz", + "integrity": "sha512-oswmokxkav9bADfJ2ifrvfHUwad6MLp73Uat0IkQWY3iAw5xTRoznXbXksZs8oaOUMpmhVWD+PZogNzllWpJaA==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.1.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz", + "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==" + }, + "node_modules/pino/node_modules/sonic-boom": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", + "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/pkg-conf": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", @@ -18479,6 +18878,11 @@ "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/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -18813,6 +19217,11 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, "node_modules/quote-stream": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", @@ -18990,6 +19399,14 @@ "node": ">=8.10.0" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", @@ -19341,6 +19758,14 @@ "node": ">=4" } }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/retimer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/retimer/-/retimer-3.0.0.tgz", @@ -19359,12 +19784,16 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==" + }, "node_modules/rgb-regex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", @@ -19522,6 +19951,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-regex2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", + "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "dependencies": { + "ret": "~0.2.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -19600,6 +20045,11 @@ "get-assigned-identifiers": "^1.1.0" } }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" + }, "node_modules/seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", @@ -19837,6 +20287,11 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" + }, "node_modules/set-function-length": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", @@ -21540,6 +21995,14 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/threads": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", @@ -21679,6 +22142,14 @@ "node": ">=8.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "engines": { + "node": ">=12" + } + }, "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", diff --git a/package.json b/package.json index 51492b0b6..2851d76eb 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,11 @@ "scripts": { "format": "prettier --write \"./backend/src/**/*.{css,html,js,json,ts,tsx}\" \"./dashboard/src/**/*.{css,html,js,json,ts,tsx}\"", "lint": "eslint \"./backend/src/**/*.{js,ts,tsx}\" \"./dashboard/src/**/*.{js,ts,tsx}\"", - "codestyle-check": "prettier --check \"./backend/src/**/*.{css,html,js,json,ts,tsx}\" \"./dashboard/src/**/*.{css,html,js,json,ts,tsx}\"" + "codestyle-check": "prettier --check \"./backend/src/**/*.{css,html,js,json,ts,tsx}\" \"./dashboard/src/**/*.{css,html,js,json,ts,tsx}\"", + + "start-bot": "cd backend && npm run start-bot-prod", + "start-api": "cd backend && npm run start-api-prod", + "start-dashboard": "cd dashboard && node serve.js" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.59.5", diff --git a/update.sh b/update.sh deleted file mode 100755 index 1769c3c92..000000000 --- a/update.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -set -e - -echo Updating Zeppelin... - -docker compose -f docker-compose.production.yml stop -git pull -docker compose -f docker-compose.production.yml build -docker compose -f docker-compose.production.yml up -d - -echo Update finished!