From 041ec069cd658dd0d62e5048203205e55a4a575d Mon Sep 17 00:00:00 2001 From: Myrotvorets Date: Tue, 26 Sep 2023 10:47:12 +0300 Subject: [PATCH] Add Dev Container --- .../.docker/identigraf-uploader/.dockerignore | 1 + .../.docker/identigraf-uploader/Dockerfile | 11 ++++ .../rootfs/etc/nginx/http.d/default.conf | 41 ++++++++++++++ .../rootfs/etc/service/identigraf-uploader | 1 + .../rootfs/etc/service/nginx | 1 + .../rootfs/etc/sv/identigraf-uploader/down | 0 .../rootfs/etc/sv/identigraf-uploader/run | 10 ++++ .../rootfs/etc/sv/nginx/run | 16 ++++++ .../rootfs/usr/local/bin/post-create.sh | 21 ++++++++ .../rootfs/usr/local/bin/start-service.sh | 25 +++++++++ .../rootfs/usr/src/service/.gitkeep | 0 .devcontainer/devcontainer.json | 54 +++++++++++++++++++ .devcontainer/docker-compose.yml | 36 +++++++++++++ src/server.mts | 18 ++++++- 14 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/.docker/identigraf-uploader/.dockerignore create mode 100644 .devcontainer/.docker/identigraf-uploader/Dockerfile create mode 100644 .devcontainer/.docker/identigraf-uploader/rootfs/etc/nginx/http.d/default.conf create mode 120000 .devcontainer/.docker/identigraf-uploader/rootfs/etc/service/identigraf-uploader create mode 120000 .devcontainer/.docker/identigraf-uploader/rootfs/etc/service/nginx create mode 100644 .devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/down create mode 100755 .devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/run create mode 100755 .devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/nginx/run create mode 100755 .devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/post-create.sh create mode 100755 .devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/start-service.sh create mode 100644 .devcontainer/.docker/identigraf-uploader/rootfs/usr/src/service/.gitkeep create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/.docker/identigraf-uploader/.dockerignore b/.devcontainer/.docker/identigraf-uploader/.dockerignore new file mode 100644 index 00000000..e1664421 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/.dockerignore @@ -0,0 +1 @@ +.gitkeep diff --git a/.devcontainer/.docker/identigraf-uploader/Dockerfile b/.devcontainer/.docker/identigraf-uploader/Dockerfile new file mode 100644 index 00000000..b5ee4ce5 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/Dockerfile @@ -0,0 +1,11 @@ +FROM ghcr.io/sjinks/codespaces/nodejs:latest + +RUN \ + apk add --no-cache nginx && \ + sed -i "s/user nginx;/user vscode;/" /etc/nginx/nginx.conf && \ + chown -R vscode:vscode /run/nginx /var/log/nginx /var/lib/nginx + +COPY rootfs / +WORKDIR /usr/src/service + +RUN chown -R vscode:vscode /usr/src/service diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/nginx/http.d/default.conf b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/nginx/http.d/default.conf new file mode 100644 index 00000000..4bcd2a0d --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/nginx/http.d/default.conf @@ -0,0 +1,41 @@ +server { + listen 80; + server_name localhost; + + client_body_buffer_size 10m; + + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header Accept-Encoding ""; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_read_timeout 300s; + + location / { + proxy_set_header traceparent ""; + proxy_set_header tracestate ""; + proxy_pass http://localhost:3000; + } + + location /identigraf-upload/v1/ { + proxy_set_header traceparent ""; + proxy_set_header tracestate ""; + proxy_pass http://localhost:3000/; + } + + location /swagger { + proxy_pass http://swagger:8080; + } +} + +server { + listen 9411; + server_name localhost; + + location / { + proxy_pass http://zipkin:9411; + } +} diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/identigraf-uploader b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/identigraf-uploader new file mode 120000 index 00000000..170843ea --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/identigraf-uploader @@ -0,0 +1 @@ +../sv/identigraf-uploader/ \ No newline at end of file diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/nginx b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/nginx new file mode 120000 index 00000000..c363cc37 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/service/nginx @@ -0,0 +1 @@ +../sv/nginx \ No newline at end of file diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/down b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/down new file mode 100644 index 00000000..e69de29b diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/run b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/run new file mode 100755 index 00000000..75802953 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/identigraf-uploader/run @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e +exec 2>&1 + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +SERVICE=identigraf-uploader + +install -d -o vscode -g vscode -m 0755 "/var/log/${SERVICE}" +exec sudo -u vscode -H /usr/local/bin/start-service.sh > "/var/log/${SERVICE}/start.log" 2>&1 diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/nginx/run b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/nginx/run new file mode 100755 index 00000000..f894c0e4 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/etc/sv/nginx/run @@ -0,0 +1,16 @@ +#!/bin/sh + +set -eu + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +exec 2>&1 + +NGINX_USER="${CONTAINER_USER:-vscode}" + +COMMAND=/usr/sbin/nginx +PID_FILE=/run/nginx/nginx.pid + +/usr/bin/install -d -o "${NGINX_USER}" -g "${NGINX_USER}" "${PID_FILE%/*}" /var/log/nginx +/usr/bin/install -d -o "${NGINX_USER}" -g "${NGINX_USER}" -m 0750 /var/lib/nginx +exec "${COMMAND}" -c /etc/nginx/nginx.conf -g "pid ${PID_FILE}; daemon off;" diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/post-create.sh b/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/post-create.sh new file mode 100755 index 00000000..ce7dddcb --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/post-create.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +cd /usr/src/service || exit 1 + +if [ ! -f .npmrc.local ] || ! grep -q '//npm.pkg.github.com/:_authToken=' .npmrc.local; then + if [ -n "${GITHUB_TOKEN}" ]; then + echo "Using GITHUB_TOKEN to authenticate to npm.pkg.github.com" + echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> .npmrc.local + elif [ -n "${READ_PACKAGES_TOKEN}" ]; then + echo "Using READ_PACKAGES_TOKEN to authenticate to npm.pkg.github.com" + echo "//npm.pkg.github.com/:_authToken=${READ_PACKAGES_TOKEN}" >> .npmrc.local + else + echo "WARNING: Neither GITHUB_TOKEN nor READ_PACKAGES_TOKEN is available; package download may fail." + fi +else + echo ".npmrc.local already exists and has an authentication token for npm.pkg.github.com" +fi + +exec sudo sv start identigraf-uploader diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/start-service.sh b/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/start-service.sh new file mode 100755 index 00000000..daa009d3 --- /dev/null +++ b/.devcontainer/.docker/identigraf-uploader/rootfs/usr/local/bin/start-service.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +SERVICE=identigraf-uploader + +cd /usr/src/service || exit 1 + +if [ ! -f src/specs/identigraf-uploader.yaml ] && [ -d .git ]; then + git submodule update --init --recursive +fi + +export NPM_CONFIG_AUDIT=0 + +if [ ! -d node_modules ]; then + npm ci --ignore-scripts --userconfig .npmrc.local && npm rebuild && npm run prepare --if-present +else + npm install --ignore-scripts --userconfig .npmrc.local && npm rebuild && npm run prepare --if-present +fi + +if [ ! -d node_modules ]; then + echo "FATAL: Failed to install dependencies" + exit 1 +fi + +npm run start:dev 2>&1 | tee -a "/var/log/${SERVICE}/${SERVICE}.log" diff --git a/.devcontainer/.docker/identigraf-uploader/rootfs/usr/src/service/.gitkeep b/.devcontainer/.docker/identigraf-uploader/rootfs/usr/src/service/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..c3e8f921 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,54 @@ +{ + "name": "IDentigraF Uploader", + "dockerComposeFile": "docker-compose.yml", + "service": "identigraf-uploader", + "workspaceFolder": "/usr/src/service", + "forwardPorts": [80, 9411], + "portsAttributes": { + "80": { + "label": "Application (Frontend)", + "onAutoForward": "notify" + }, + "3000": { + "label": "Application (Backend)", + "onAutoForward": "ignore" + }, + "9411": { + "label": "Zipkin", + "onAutoForward": "notify" + } + }, + "remoteUser": "vscode", + "postCreateCommand": "/usr/local/bin/post-create.sh", + "secrets": { + "READ_PACKAGES_TOKEN": { + "description": "Personal access token to install packages from ghcr.io" + } + }, + "customizations": { + "codespaces": { + "repositories": { + "myrotvorets/psb-api-identigraf-uploader": { + "permissions": { + "packages": "read" + } + } + } + }, + "vscode": { + "extensions": [ + "dlech.chmod", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "timonwong.shellcheck", + "ms-azuretools.vscode-docker", + "zhiayang.tabindentspacealign", + "42Crunch.vscode-openapi", + "natqe.reload" + ], + "settings": { + "files.autoSave": "onWindowChange" + } + } + } +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..0e77861b --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,36 @@ +services: + identigraf-uploader: + build: + context: .docker/identigraf-uploader + dockerfile: Dockerfile + environment: + - NODE_ENV=development + - NO_UPDATE_NOTIFIER=true + - NPM_CONFIG_FUND=0 + - SUPPRESS_SUPPORT=1 + - HTTPS=0 + - PORT=3000 + - ENABLE_TRACING=1 + - OTEL_EXPORTER_ZIPKIN_ENDPOINT=http://zipkin:9411/api/v2/spans + - IDENTIGRAF_UPLOAD_FOLDER=/var/lib/identigraf-uploader/uploads + - IDENTIGRAF_MAX_FILE_SIZE=5242880 + - HAVE_SWAGGER=true + - npm_config_userconfig=/usr/src/service/.npmrc.local + restart: always + volumes: + - "../:/usr/src/service" + - uploads:/var/lib/identigraf-uploader/uploads + working_dir: /usr/src/service + + zipkin: + image: openzipkin/zipkin:latest@sha256:5fd55e6a109233b36d419d7fd2449588d17a6e4da7ed7a3fd0d09c86f1c75a15 + restart: always + + swagger: + image: swaggerapi/swagger-ui:latest@sha256:3b4f51470fed56b1ce5a064f57ce7a0d585f50192731f458e7b376713b286d57 + environment: + - SWAGGER_JSON_URL=/specs/identigraf-uploader.yaml + - BASE_URL=/swagger + +volumes: + uploads: diff --git a/src/server.mts b/src/server.mts index 08d21826..e7f6f3b1 100644 --- a/src/server.mts +++ b/src/server.mts @@ -1,6 +1,6 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; -import express, { type Express, json } from 'express'; +import express, { type Express, json, static as staticMiddleware } from 'express'; import { installOpenApiValidator } from '@myrotvorets/oav-installer'; import { errorMiddleware, notFoundMiddleware } from '@myrotvorets/express-microservice-middlewares'; import { cleanUploadedFilesMiddleware } from '@myrotvorets/clean-up-after-multer'; @@ -17,6 +17,22 @@ import { uploadErrorHandlerMiddleware } from './middleware/upload.mjs'; export async function configureApp(app: express.Express): Promise { const env = environment(); + /* c8 ignore start */ + if (env.NODE_ENV !== 'production') { + app.use( + '/specs/', + staticMiddleware(join(dirname(fileURLToPath(import.meta.url)), 'specs'), { + acceptRanges: false, + index: false, + }), + ); + + if (process.env.HAVE_SWAGGER === 'true') { + app.get('/', (_req, res) => res.redirect('/swagger/')); + } + } + /* c8 ignore end */ + app.use(json()); await installOpenApiValidator(