From d9acd72d7bf2c76e2d0aa45ba439f212c226d9dd Mon Sep 17 00:00:00 2001 From: Katarina Date: Tue, 15 Jul 2025 14:09:42 +0200 Subject: [PATCH] Fix Dockerization --- .dockerignore | 6 +++++ Dockerfile | 33 ------------------------- docker/Dockerfile | 36 +++++++++++++++++++++++++++ docker/entrypoint.sh | 13 ++++++++++ index.html | 1 + nginx.conf | 51 +++++++++++++++++++++++++++++++++++++++ package-lock.json | 6 ++--- package.json | 2 +- public/config.template.js | 3 +++ src/api/index.ts | 4 +-- src/main.ts | 22 ++++++++++++----- src/util/assets.ts | 19 --------------- src/util/config.ts | 24 ++++++++++++++++++ 13 files changed, 154 insertions(+), 66 deletions(-) create mode 100644 .dockerignore delete mode 100644 Dockerfile create mode 100644 docker/Dockerfile create mode 100644 docker/entrypoint.sh create mode 100644 nginx.conf create mode 100644 public/config.template.js delete mode 100644 src/util/assets.ts create mode 100644 src/util/config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6953254 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.gitignore +openapitools.json +README.md +.vscode +.idea +.gitea \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c4b6a66..0000000 --- a/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Stage 1: Install dependencies (including build tools) -FROM node:22-bookworm-slim AS deps -WORKDIR /app -COPY package*.json ./ -RUN npm ci - -# Stage 2: Build application -FROM node:22-bookworm-slim AS builder -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY . . -RUN npm run build - -# Stage 3: Prepare static web server -FROM alpine:3.19 AS server-prep -RUN wget -O /tmp/sws.tar.gz \ - https://github.com/static-web-server/static-web-server/releases/download/v2.17.0/static-web-server-v2.17.0-x86_64-unknown-linux-musl.tar.gz -RUN tar -xzf /tmp/sws.tar.gz -C /tmp \ - --strip-components=1 - -# Stage 4: Create runtime image -FROM gcr.io/distroless/static-debian12:nonroot - -COPY --from=builder --chown=nonroot:nonroot /app/dist /app - -COPY --from=server-prep --chown=nonroot:nonroot /tmp/static-web-server /usr/local/bin/ - -USER nonroot -WORKDIR /app -EXPOSE 8080 - -ENTRYPOINT ["/usr/local/bin/static-web-server"] -CMD ["--port", "8080", "--root", "/app", "--log-level", "warn"] \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..541c917 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,36 @@ +# Stage 1: Build application +FROM node:22-bookworm-slim AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npm run build + +# Stage 2: Create runtime image +FROM nginxinc/nginx-unprivileged:1.25-alpine + +# Create writable directories +USER root +RUN mkdir -p /runtime-config && \ + chown nginx:nginx /runtime-config && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/run +USER nginx + +# Copy built assets +COPY --from=builder --chown=nginx:nginx /app/dist /usr/share/nginx/html + +# Copy our custom nginx config +COPY nginx.conf /etc/nginx/nginx.conf + +# Copy entrypoint script +COPY docker/entrypoint.sh /entrypoint.sh + +# Make entrypoint executable +USER root +RUN chmod +x /entrypoint.sh +USER nginx + +EXPOSE 8080 + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..4799161 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -e + +# Create runtime config file in writable location +cat > /runtime-config/config.js < + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..c4686ed --- /dev/null +++ b/nginx.conf @@ -0,0 +1,51 @@ +# Main nginx configuration +worker_processes auto; +error_log /dev/stderr warn; +pid /tmp/nginx.pid; # Use writable location for PID + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + access_log /dev/stdout; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + server { + listen 8080; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Handle client-side routing + location / { + try_files $uri $uri/ /index.html; + } + + # Serve config.js from the writable location + location = /config.js { + alias /runtime-config/config.js; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + access_log off; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index feaba17..66f66db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,11 @@ "packages": { "": { "name": "dex-ui-vue", - "version": "0.0.0", + "version": "0.0.1", "dependencies": { "@primeuix/themes": "^1.2.1", "@tailwindcss/vite": "^4.1.11", + "@types/node": "^24.0.14", "@vueuse/core": "^13.5.0", "axios": "^1.10.0", "oidc-client-ts": "^3.3.0", @@ -23,7 +24,6 @@ }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.20.2", - "@types/node": "^24.0.14", "@vitejs/plugin-vue": "^6.0.0", "@vue/tsconfig": "^0.7.0", "typescript": "~5.7.2", @@ -1386,7 +1386,6 @@ "version": "24.0.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.8.0" @@ -4546,7 +4545,6 @@ "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "devOptional": true, "license": "MIT" }, "node_modules/universalify": { diff --git a/package.json b/package.json index 80043ad..22dab74 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@primeuix/themes": "^1.2.1", "@tailwindcss/vite": "^4.1.11", + "@types/node": "^24.0.14", "@vueuse/core": "^13.5.0", "axios": "^1.10.0", "oidc-client-ts": "^3.3.0", @@ -25,7 +26,6 @@ }, "devDependencies": { "@openapitools/openapi-generator-cli": "^2.20.2", - "@types/node": "^24.0.14", "@vitejs/plugin-vue": "^6.0.0", "@vue/tsconfig": "^0.7.0", "typescript": "~5.7.2", diff --git a/public/config.template.js b/public/config.template.js new file mode 100644 index 0000000..5921eb4 --- /dev/null +++ b/public/config.template.js @@ -0,0 +1,3 @@ +window.__RUNTIME_CONFIG__ = { + API_URL: "__API_URL__" +}; \ No newline at end of file diff --git a/src/api/index.ts b/src/api/index.ts index 6625b53..0bc5ac8 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,9 +1,7 @@ import axios from "axios"; import {userManager} from "../stores/auth.ts"; -const axiosInstance = axios.create({ - baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8080' -}) +const axiosInstance = axios.create() axiosInstance.interceptors.request.use(async (config) => { const user = await userManager.getUser() diff --git a/src/main.ts b/src/main.ts index e0a5803..e768b8a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,8 @@ import { AccordionContent, AccordionHeader, AccordionPanel, - DatePicker, Fluid, + DatePicker, + Fluid, ToastService } from "primevue"; import {createPinia} from "pinia"; @@ -26,6 +27,15 @@ import axiosInstance from "./api"; import SetsView from "./views/set/SetsView.vue"; import JobsView from "./views/JobsView.vue"; import {definePreset} from "@primeuix/themes"; +import {getConfig, initConfig} from "@/util/config.ts"; + +// Initialize configuration from window object +initConfig((window as any).__APP_CONFIG__ || {}) + +// Use the config in your app +const config = getConfig() +console.log('API Base URL:', config.API_BASE_URL) + export const DeckServiceKey = Symbol("deckServiceKey") export const CardServiceKey = Symbol("cardServiceKey") @@ -134,11 +144,11 @@ router.beforeEach(async (to) => { app.use(router); app.use(ToastService) -const deckService: DeckService = new DeckService(undefined, "http://localhost:8080", axiosInstance) -const cardService: CardService = new CardService(undefined, "http://localhost:8080", axiosInstance) -const setService: SetService = new SetService(undefined, "http://localhost:8080", axiosInstance) -const cardPrintService: CardPrintService = new CardPrintService(undefined, "http://localhost:8080", axiosInstance) -const jobService: JobService = new JobService(undefined, "http://localhost:8080", axiosInstance) +const deckService: DeckService = new DeckService(undefined, config.API_BASE_URL, axiosInstance) +const cardService: CardService = new CardService(undefined, config.API_BASE_URL, axiosInstance) +const setService: SetService = new SetService(undefined, config.API_BASE_URL, axiosInstance) +const cardPrintService: CardPrintService = new CardPrintService(undefined, config.API_BASE_URL, axiosInstance) +const jobService: JobService = new JobService(undefined, config.API_BASE_URL, axiosInstance) app.provide(DeckServiceKey, deckService) app.provide(CardServiceKey, cardService) diff --git a/src/util/assets.ts b/src/util/assets.ts deleted file mode 100644 index 42170b6..0000000 --- a/src/util/assets.ts +++ /dev/null @@ -1,19 +0,0 @@ -import darkAttribute from "/src/assets/DARK.svg" -import divineAttribute from "/src/assets/DIVINE.svg" -import earthAttribute from "/src/assets/EARTH.svg" -import fireAttribute from "/src/assets/FIRE.svg" -import laughAttribute from "/src/assets/LAUGH.svg" -import lightAttribute from "/src/assets/LIGHT.svg" -import waterAttribute from "/src/assets/WATER.svg" -import windAttribute from "/src/assets/WIND.svg" - -export { - darkAttribute, - divineAttribute, - earthAttribute, - fireAttribute, - laughAttribute, - lightAttribute, - waterAttribute, - windAttribute -} diff --git a/src/util/config.ts b/src/util/config.ts new file mode 100644 index 0000000..89ff920 --- /dev/null +++ b/src/util/config.ts @@ -0,0 +1,24 @@ +// Define your config interface +export interface AppConfig { + API_BASE_URL: string; + OTHER_VAR: string; +} + +// Global variable that will be set at runtime +let runtimeConfig: AppConfig = { + API_BASE_URL: import.meta.env.VITE_API_BASE_URL || '', + OTHER_VAR: import.meta.env.VITE_OTHER_VAR || '' +}; + +// Function to initialize config at runtime +export function initConfig(config: Partial) { + runtimeConfig = { + ...runtimeConfig, + ...config + }; +} + +// Accessor function +export function getConfig(): AppConfig { + return runtimeConfig; +} \ No newline at end of file