From e3a8f4ef2220255c4a87f0653f1c8633bd970678 Mon Sep 17 00:00:00 2001 From: Auttomus <190161908+auttomus@users.noreply.github.com> Date: Sat, 6 Jun 2026 15:18:20 +0800 Subject: [PATCH 1/4] chore: standardize environment variable exclusion patterns across dockerignore files --- .dockerignore | 5 +++++ backend/.dockerignore | 7 +++++++ frontend/.dockerignore | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 backend/.dockerignore diff --git a/.dockerignore b/.dockerignore index ee408d8..f4dee15 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,8 @@ frontend/build/ .git/ .agents/ .vscode/ +**/.env +**/.env.docker +**/.env.prod +**/.env.* +!**/.env.example diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..b720b3b --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.env +.env.docker +.env.prod +.env.* +!.env.example diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 9b8d514..014e929 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,4 +1,9 @@ .react-router build node_modules -README.md \ No newline at end of file +README.md +.env +.env.docker +.env.prod +.env.* +!.env.example \ No newline at end of file From a07b9f4098dd3a407fa7a7a17a3d210147e0b573 Mon Sep 17 00:00:00 2001 From: Auttomus <190161908+auttomus@users.noreply.github.com> Date: Sun, 7 Jun 2026 08:20:21 +0800 Subject: [PATCH 2/4] feat: implement dynamic environment configuration and versioning. Add CI/CD pipeline for Docker releases --- .github/workflows/docker-release.yml | 68 +++++++++++++++++++ frontend/Dockerfile | 4 -- frontend/app/core/utils/resolveMediaUrl.ts | 5 +- .../settings/components/AboutCard.tsx | 2 +- frontend/app/root.tsx | 3 + frontend/package.json | 1 + 6 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/docker-release.yml diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 0000000..3cdf029 --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,68 @@ +name: Build and Release Docker Images + +on: + push: + tags: + - 'v*' # Memicu alur kerja setiap kali melakukan push tag seperti v1.0.0, v2.1.3, dll. + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + # Login ke GitHub Container Registry menggunakan token otomatis bawaan GitHub + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # ─── BUILD & PUSH BACKEND ────────────────────────────────────────────────── + - name: Extract Backend Metadata + id: meta-backend + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }}-backend + tags: | + type=semver,pattern={{version}} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') }} + + - name: Build and Push Backend Image + uses: docker/build-push-action@v5 + with: + context: ./backend + push: true + tags: ${{ id.meta-backend.outputs.tags }} + labels: ${{ id.meta-backend.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # ─── BUILD & PUSH FRONTEND ───────────────────────────────────────────────── + - name: Extract Frontend Metadata + id: meta-frontend + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }}-frontend + tags: | + type=semver,pattern={{version}} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') }} + + - name: Build and Push Frontend Image + uses: docker/build-push-action@v5 + with: + context: ./frontend + push: true + tags: ${{ id.meta-frontend.outputs.tags }} + labels: ${{ id.meta-frontend.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/frontend/Dockerfile b/frontend/Dockerfile index f887579..2f3655e 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -19,10 +19,6 @@ FROM base AS build WORKDIR /app COPY . . COPY --from=dev-deps /app/node_modules ./node_modules -ARG VITE_MINIO_PUBLIC_URL=https://cdn.sotto.auttomus.xyz -ARG VITE_MIDTRANS_CLIENT_KEY=Mid-client-aKAogCe7B_Nui3nc -ENV VITE_MINIO_PUBLIC_URL=$VITE_MINIO_PUBLIC_URL -ENV VITE_MIDTRANS_CLIENT_KEY=$VITE_MIDTRANS_CLIENT_KEY RUN ./node_modules/.bin/react-router build FROM base AS deploy diff --git a/frontend/app/core/utils/resolveMediaUrl.ts b/frontend/app/core/utils/resolveMediaUrl.ts index 5a3f4c4..5f1714d 100644 --- a/frontend/app/core/utils/resolveMediaUrl.ts +++ b/frontend/app/core/utils/resolveMediaUrl.ts @@ -13,7 +13,10 @@ export function resolveMediaUrl(objectKeyOrUrl: string | null | undefined): stri return objectKeyOrUrl; } - const base = import.meta.env.VITE_MINIO_PUBLIC_URL || 'http://localhost:9000'; + const base = + (typeof window !== 'undefined' && (window as any).ENV?.VITE_MINIO_PUBLIC_URL) || + import.meta.env.VITE_MINIO_PUBLIC_URL || + 'http://localhost:9000'; // Ensure no double slash between base and key const cleanBase = base.endsWith('/') ? base.slice(0, -1) : base; const cleanKey = objectKeyOrUrl.startsWith('/') ? objectKeyOrUrl : `/${objectKeyOrUrl}`; diff --git a/frontend/app/features/settings/components/AboutCard.tsx b/frontend/app/features/settings/components/AboutCard.tsx index 1c79373..cec33d2 100644 --- a/frontend/app/features/settings/components/AboutCard.tsx +++ b/frontend/app/features/settings/components/AboutCard.tsx @@ -27,7 +27,7 @@ export function AboutCard() { {/* Version Badge */}
- Versi 0.7.0 (beta) + Versi {(typeof window !== "undefined" && (window as any).ENV?.VITE_APP_VERSION) || import.meta.env.VITE_APP_VERSION}
diff --git a/frontend/app/root.tsx b/frontend/app/root.tsx index 77b2829..fc8da3c 100644 --- a/frontend/app/root.tsx +++ b/frontend/app/root.tsx @@ -12,6 +12,7 @@ import { apolloClient } from "~/core/apollo/client"; import type { Route } from "./+types/root"; import "./app.css"; +import pkg from "../package.json"; import { ToastProvider } from "~/components/ui/ToastProvider"; import { DialogProvider } from "~/components/ui/DialogProvider"; import { useRealtimeGateway } from "~/core/hooks/useRealtimeGateway"; @@ -64,6 +65,8 @@ export async function loader() { return { ENV: { VITE_MIDTRANS_CLIENT_KEY: process.env.MIDTRANS_CLIENT_KEY || process.env.VITE_MIDTRANS_CLIENT_KEY || "", + VITE_MINIO_PUBLIC_URL: process.env.MINIO_PUBLIC_URL || process.env.VITE_MINIO_PUBLIC_URL || "", + VITE_APP_VERSION: pkg.version || "0.7.0 (beta)", } }; } diff --git a/frontend/package.json b/frontend/package.json index 870a2ba..478077c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,6 @@ { "name": "frontend", + "version": "0.7.1", "private": true, "type": "module", "packageManager": "pnpm@11.1.2", From 5f23d709559f5d2d507bdf35aa1688980c38b234 Mon Sep 17 00:00:00 2001 From: Auttomus <190161908+auttomus@users.noreply.github.com> Date: Sun, 7 Jun 2026 08:27:09 +0800 Subject: [PATCH 3/4] git add .github/workflows/eslint.yml backend/package.json backend/pnpm-lock.yaml git commit -m "ci: fix eslint workflow for backend monorepo with sarif output" --- .github/workflows/eslint.yml | 28 +++-- backend/package.json | 3 +- backend/pnpm-lock.yaml | 220 ++++++++++++++++++++++++++++++++++- 3 files changed, 233 insertions(+), 18 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 222c372..2ff8b7c 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -30,24 +30,30 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install ESLint - run: | - npm install eslint@8.10.0 - npm install @microsoft/eslint-formatter-sarif@3.1.0 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 11 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + cache-dependency-path: backend/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: backend - name: Run ESLint env: SARIF_ESLINT_IGNORE_SUPPRESSED: "true" - # fix: Menambahkan backslash (\) untuk menyambung perintah ke baris berikutnya - run: npx eslint . \ - --config .eslintrc.js \ - --ext .js,.jsx,.ts,.tsx \ - --format @microsoft/eslint-formatter-sarif \ - --output-file eslint-results.sarif + run: pnpm exec eslint "{src,apps,libs,test}/**/*.ts" --format @microsoft/eslint-formatter-sarif --output-file ../eslint-results.sarif + working-directory: backend continue-on-error: true - name: Upload analysis results to GitHub - # fix: Mengubah versi v3 menjadi v4 sesuai instruksi peringatan uses: github/codeql-action/upload-sarif@v4 with: sarif_file: eslint-results.sarif diff --git a/backend/package.json b/backend/package.json index 6871a3e..596da7f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -36,7 +36,6 @@ "@nestjs/schedule": "^6.1.3", "@nestjs/websockets": "^11.1.19", "@prisma/client": "^6.19.3", - "prisma": "^6.19.3", "bcrypt": "^6.0.0", "bull": "^4.16.5", "cassandra-driver": "^4.9.0", @@ -47,6 +46,7 @@ "minio": "^8.0.7", "passport": "^0.7.0", "passport-jwt": "^4.0.1", + "prisma": "^6.19.3", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "socket.io": "^4.8.3", @@ -55,6 +55,7 @@ "devDependencies": { "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.18.0", + "@microsoft/eslint-formatter-sarif": "^3.1.0", "@nestjs/cli": "^11.0.0", "@nestjs/schematics": "^11.0.0", "@nestjs/testing": "^11.0.1", diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml index 0830e94..40205ea 100644 --- a/backend/pnpm-lock.yaml +++ b/backend/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: passport-jwt: specifier: ^4.0.1 version: 4.0.1 + prisma: + specifier: ^6.19.3 + version: 6.19.3(typescript@5.9.3) reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -92,6 +95,9 @@ importers: socket.io: specifier: ^4.8.3 version: 4.8.3 + tsx: + specifier: ^4.21.0 + version: 4.22.0 devDependencies: '@eslint/eslintrc': specifier: ^3.2.0 @@ -99,6 +105,9 @@ importers: '@eslint/js': specifier: ^9.18.0 version: 9.39.4 + '@microsoft/eslint-formatter-sarif': + specifier: ^3.1.0 + version: 3.1.0 '@nestjs/cli': specifier: ^11.0.0 version: 11.0.21(@types/node@24.12.2)(prettier@3.8.3) @@ -156,9 +165,6 @@ importers: prettier: specifier: ^3.4.2 version: 3.8.3 - prisma: - specifier: ^6.19.3 - version: 6.19.3(typescript@5.9.3) source-map-support: specifier: ^0.5.21 version: 0.5.21 @@ -177,9 +183,6 @@ importers: tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 - tsx: - specifier: ^4.21.0 - version: 4.22.0 typescript: specifier: ^5.7.3 version: 5.9.3 @@ -670,10 +673,18 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.3.5': resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.39.4': resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -721,10 +732,19 @@ packages: resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -995,6 +1015,10 @@ packages: resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@microsoft/eslint-formatter-sarif@3.1.0': + resolution: {integrity: sha512-/mn4UXziHzGXnKCg+r8HGgPy+w4RzpgdoqFuqaKOqUVBT5x2CygGefIrO4SusaY7t0C4gyIWMNu6YQT6Jw64Cw==} + engines: {node: '>= 14'} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} cpu: [arm64] @@ -2228,6 +2252,10 @@ packages: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dotenv-expand@12.0.3: resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} engines: {node: '>=12'} @@ -2359,6 +2387,10 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2375,6 +2407,12 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2389,6 +2427,10 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -2495,6 +2537,10 @@ packages: picomatch: optional: true + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -2523,6 +2569,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -2635,6 +2685,10 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -2650,6 +2704,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql-tag@2.12.6: resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} engines: {node: '>=10'} @@ -2789,6 +2846,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} @@ -2987,6 +3048,10 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jschardet@3.1.4: + resolution: {integrity: sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==} + engines: {node: '>=0.1.90'} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3570,6 +3635,11 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -3835,6 +3905,9 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + through2@4.0.2: resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} @@ -3943,6 +4016,10 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -4017,6 +4094,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + utf8@3.0.0: + resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -4638,6 +4718,11 @@ snapshots: '@esbuild/win32-x64@0.28.0': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': dependencies: eslint: 9.39.4(jiti@2.7.0) @@ -4661,6 +4746,20 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.15.0 @@ -4675,6 +4774,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/js@8.57.1': {} + '@eslint/js@9.39.4': {} '@eslint/object-schema@2.1.7': {} @@ -4721,8 +4822,18 @@ snapshots: '@humanfs/types@0.15.0': {} + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + '@humanwhocodes/module-importer@1.0.1': {} + '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} '@inquirer/ansi@1.0.2': {} @@ -5094,6 +5205,15 @@ snapshots: '@lukeed/csprng@1.1.0': {} + '@microsoft/eslint-formatter-sarif@3.1.0': + dependencies: + eslint: 8.57.1 + jschardet: 3.1.4 + lodash: 4.18.1 + utf8: 3.0.0 + transitivePeerDependencies: + - supports-color + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true @@ -6363,6 +6483,10 @@ snapshots: diff@4.0.4: {} + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dotenv-expand@12.0.3: dependencies: dotenv: 16.6.1 @@ -6505,6 +6629,11 @@ snapshots: esrecurse: 4.3.0 estraverse: 4.3.0 + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -6516,6 +6645,49 @@ snapshots: eslint-visitor-keys@5.0.1: {} + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + eslint@9.39.4(jiti@2.7.0): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) @@ -6563,6 +6735,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + esprima@4.0.1: {} esquery@1.7.0: @@ -6694,6 +6872,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -6734,6 +6916,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + flat-cache@3.2.0: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + rimraf: 3.0.2 + flat-cache@4.0.1: dependencies: flatted: 3.4.2 @@ -6871,6 +7059,10 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + globals@14.0.0: {} globals@17.6.0: {} @@ -6879,6 +7071,8 @@ snapshots: graceful-fs@4.2.11: {} + graphemer@1.4.0: {} + graphql-tag@2.12.6(graphql@16.14.0): dependencies: graphql: 16.14.0 @@ -6994,6 +7188,8 @@ snapshots: is-number@7.0.0: {} + is-path-inside@3.0.3: {} + is-promise@4.0.0: {} is-stream@2.0.1: {} @@ -7379,6 +7575,8 @@ snapshots: dependencies: argparse: 2.0.1 + jschardet@3.1.4: {} + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -7893,6 +8091,10 @@ snapshots: reusify@1.1.0: {} + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + router@2.2.0: dependencies: debug: 4.4.3 @@ -8204,6 +8406,8 @@ snapshots: glob: 7.2.3 minimatch: 3.1.5 + text-table@0.2.0: {} + through2@4.0.2: dependencies: readable-stream: 3.6.2 @@ -8314,6 +8518,8 @@ snapshots: type-detect@4.0.8: {} + type-fest@0.20.2: {} + type-fest@0.21.3: {} type-fest@4.41.0: {} @@ -8401,6 +8607,8 @@ snapshots: dependencies: punycode: 2.3.1 + utf8@3.0.0: {} + util-deprecate@1.0.2: {} utils-merge@1.0.1: {} From 4b8e70e993a1c44b99884098b873b6a1fa660d22 Mon Sep 17 00:00:00 2001 From: Auttomus <190161908+auttomus@users.noreply.github.com> Date: Sun, 7 Jun 2026 08:36:26 +0800 Subject: [PATCH 4/4] chore: setup dynamic config and ghcr release configuration --- docker-compose.prod.yml | 4 ++-- infrastructure/.env.prod.example | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index a6b09f0..f8dec02 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -51,7 +51,7 @@ services: # 4. BACKEND SERVICE (NestJS) backend: - image: ${DOCKER_REGISTRY_USER:-auttomus}/sotto-backend:latest + image: ghcr.io/${DOCKER_REGISTRY_USER:-untacorp}/sotto-backend:${IMAGE_TAG:-latest} container_name: sotto_backend restart: unless-stopped env_file: @@ -68,7 +68,7 @@ services: # 5. FRONTEND SERVICE (React Router) frontend: - image: ${DOCKER_REGISTRY_USER:-auttomus}/sotto-frontend:latest + image: ghcr.io/${DOCKER_REGISTRY_USER:-untacorp}/sotto-frontend:${IMAGE_TAG:-latest} container_name: sotto_frontend restart: unless-stopped env_file: diff --git a/infrastructure/.env.prod.example b/infrastructure/.env.prod.example index e210d30..028fea8 100644 --- a/infrastructure/.env.prod.example +++ b/infrastructure/.env.prod.example @@ -63,3 +63,8 @@ SYNERGY_FRESHNESS_DECAY=0.3466 # ─── CLOUDFLARE TUNNEL ────────────────────────────────────────────────────── # Token Cloudflare Tunnel jika Anda ingin mempublikasikan service secara live. TUNNEL_TOKEN= + +# ─── DOCKER DEPLOYMENT ────────────────────────────────────────────────────── +# Konfigurasi target repositori dan versi tag yang ingin dideploy di VPS. +DOCKER_REGISTRY_USER=untacorp +IMAGE_TAG=latest