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