diff --git a/.env.example b/.env.example index 677317f..aa2563a 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ WEB_PORT=5173 -API_PORT=3000 -DATABASE_URL=postgres://postgres:postgres@localhost:5432/zeavis_edu +API_PORT=4006 +DATABASE_URL=postgres://asephs:***@100.121.180.82:6432/zeavis_edu # ── Telemetry / ClickHouse ────────────────────────────────────────── # These credentials are used by the telemetry Docker Compose stack. diff --git a/CLAUDE.md b/CLAUDE.md index 72cce50..8f64936 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -162,7 +162,7 @@ Each ZeaVis Edu service exposes a `GET /metrics` endpoint: All three share the `zeavis_` metric prefix and are scraped by Prometheus via `file_sd_configs` (see `telemetry/prometheus/targets/zeavis-edu.json`). -**IMPORTANT — Production architecture:** ZeaVis Edu apps and the Telemetry stack run on **separate VPS instances** connected via **Tailscale** (mesh VPN). Prometheus scrapes the API and ML service through their **Tailscale IPs** (e.g. `100.x.x.a:3000`), not via Docker hostnames. The target file has `__CHANGE_ME__` placeholders — replace with actual Tailscale IPs before deploying. +**IMPORTANT — Production architecture:** ZeaVis Edu apps and the Telemetry stack run on **separate VPS instances** connected via **Tailscale** (mesh VPN). Prometheus scrapes the API and ML service through their **Tailscale IPs** (e.g. `100.x.x.a:4006`), not via Docker hostnames. The target file has `__CHANGE_ME__` placeholders — replace with actual Tailscale IPs before deploying. The telemetry stack is managed from the project root via `make telemetry-*` targets (see `Makefile`). Docker Compose defines 5 services (Prometheus, Node Exporter, Query Proxy, Grafana, Telemetry UI). diff --git a/METRICS.md b/METRICS.md index af8ba31..a803b26 100644 --- a/METRICS.md +++ b/METRICS.md @@ -10,15 +10,15 @@ application stack and the payload each service provides. | Service | Host (prod) | Metrics Endpoint | Port (local) | |-----------------------|-----------------------------------|----------------------------|--------------| | Web (Vite dev) | `zeavisedu.asepharyana.my.id` | `GET /metrics` | 5173 | -| API (Elysia) | `api-zeavisedu.asepharyana.my.id` | `GET /metrics` | 3000 | -| ML Service (Axum) | `ml-zeavisedu.asepharyana.my.id` | `GET /metrics` | 8000 | +| API (Elysia) | `api-zeavisedu.asepharyana.my.id` | `GET /metrics` | 4006 | +| ML Service (Axum) | `ml-zeavisedu.asepharyana.my.id` | `GET /metrics` | 4012 | | Prometheus Collector | — | `GET /metrics` (self) | 9090 | > In production all metrics are scraped by the Prometheus collector running in the > Telemetry stack on a **separate VPS** connected via **Tailscale**. > See [`telemetry/prometheus/targets/`](./telemetry/prometheus/targets/) > for the auto‑discovery configuration. Target files must use **Tailscale IPs** -> (e.g. `100.x.x.a:3000`), not Docker hostnames, because the services are on +> (e.g. `100.x.x.a:4006`), not Docker hostnames, because the services are on > different hosts. > > In production (nginx), the web app proxies `/metrics` to the API service: @@ -101,11 +101,11 @@ The Telemetry submodule includes a Prometheus instance that uses ```json [ { - "targets": ["100.x.x.a:3000"], + "targets": ["100.x.x.a:4006"], "labels": { "service": "zeavis-api", "component": "backend", "env": "production" } }, { - "targets": ["100.x.x.b:8000"], + "targets": ["100.x.x.b:4012"], "labels": { "service": "zeavis-ml", "component": "inference", "env": "production" } } ] @@ -113,7 +113,7 @@ The Telemetry submodule includes a Prometheus instance that uses > ⚠️ **Cross-VPS:** Gunakan **IP Tailscale** (bukan Docker hostname) karena > Prometheus dan ZeaVis Edu berjalan di VPS berbeda. Pastikan port service -> (`:3000`, `:8000`) terekspos di `0.0.0.0` atau diizinkan oleh aturan +> (`:4006`, `:4012`) terekspos di `0.0.0.0` atau diizinkan oleh aturan > `iptables`/`ufw` untuk interface Tailscale (`tailscale0`/`100.x.x.x/10`). The Prometheus config (in `telemetry/prometheus/prometheus.yml`) will diff --git a/README.md b/README.md index 17891da..43f29b1 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ bun install bun run dev # Semua service (web + api) cd apps/web && bun run dev # Hanya frontend cd apps/api && bun run start # Hanya backend API -cd apps/ml-service && cargo run # ML inference engine (port 8000) +cd apps/ml-service && cargo run # ML inference engine (port 4012) cd apps/tauri && bun run tauri dev # Tauri desktop dev cd apps/tauri && bun run tauri android dev # Tauri Android dev ``` diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 51648b3..4adbed8 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -11,7 +11,7 @@ RUN bun install --production FROM oven/bun:1.3.14 AS runner WORKDIR /app ENV NODE_ENV=production -ENV API_PORT=3000 +ENV API_PORT=4006 COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/apps/api/node_modules apps/api/node_modules @@ -20,5 +20,5 @@ COPY package.json bunfig.toml tsconfig.base.json ./ COPY apps/api apps/api COPY packages/shared packages/shared -EXPOSE 3000 +EXPOSE 4006 CMD ["bun", "apps/api/src/index.ts"] diff --git a/apps/api/drizzle.config.ts b/apps/api/drizzle.config.ts index 45a3f9e..4c636c4 100644 --- a/apps/api/drizzle.config.ts +++ b/apps/api/drizzle.config.ts @@ -5,6 +5,6 @@ export default defineConfig({ out: './drizzle', dialect: 'postgresql', dbCredentials: { - url: process.env.DATABASE_URL ?? 'postgres://postgres:postgres@localhost:5432/zeavis_edu', + url: process.env.DATABASE_URL ?? 'postgres://asephs:***@100.121.180.82:6432/zeavis_edu', }, }); diff --git a/apps/api/src/config/env.ts b/apps/api/src/config/env.ts index 3501d5a..af1ee3c 100644 --- a/apps/api/src/config/env.ts +++ b/apps/api/src/config/env.ts @@ -18,7 +18,7 @@ const allowedOrigins = [ const secureCookies = Bun.env.SECURE_COOKIES === 'true' || webAppUrl.startsWith('https://'); export const env = { - port: Number(Bun.env.API_PORT ?? 3000), + port: Number(Bun.env.API_PORT ?? 4006), databaseUrl: Bun.env.DATABASE_URL, sessionSecret: Bun.env.SESSION_SECRET, uploaderBaseUrl: Bun.env.UPLOADER_BASE_URL ?? 'https://upload.asepharyana.my.id', diff --git a/apps/ml-service/Dockerfile b/apps/ml-service/Dockerfile index 78e9fee..e9e36a0 100644 --- a/apps/ml-service/Dockerfile +++ b/apps/ml-service/Dockerfile @@ -11,7 +11,7 @@ WORKDIR /app ENV MODEL_PATH=/app/model/model.onnx ENV MODEL_INPUT_SIZE=224 ENV ML_SERVICE_HOST=0.0.0.0 -ENV ML_SERVICE_PORT=8000 +ENV ML_SERVICE_PORT=4012 ENV RUST_LOG=info RUN pacman -Syu --noconfirm ca-certificates 2>/dev/null @@ -19,5 +19,5 @@ RUN pacman -Syu --noconfirm ca-certificates 2>/dev/null COPY --from=builder /app/target/release/zeavis-ml-service /usr/local/bin/zeavis-ml-service COPY Machine_Learning/model/model.onnx /app/model/model.onnx -EXPOSE 8000 +EXPOSE 4012 CMD ["zeavis-ml-service"] diff --git a/apps/ml-service/README.md b/apps/ml-service/README.md index a375704..68648dd 100644 --- a/apps/ml-service/README.md +++ b/apps/ml-service/README.md @@ -48,7 +48,7 @@ Output build lokal berada di `target/` dan direktori tersebut diabaikan oleh Git Semua perintah di bawah dijalankan dari direktori `apps/ml-service`. -### Opsi 1: Default (Port 8000) +### Opsi 1: Default (Port 4012) ```bash cargo run @@ -91,7 +91,7 @@ ML_SERVICE_PORT=9000 MODEL_PATH=/path/to/model.onnx cargo run ### Health Check ```bash -curl http://localhost:8000/health +curl http://localhost:4012/health ``` ```json @@ -104,7 +104,7 @@ curl http://localhost:8000/health ### Metadata ```bash -curl http://localhost:8000/metadata +curl http://localhost:4012/metadata ``` ```json @@ -123,7 +123,7 @@ curl http://localhost:8000/metadata Upload gambar daun jagung untuk klasifikasi: ```bash -curl -X POST http://localhost:8000/predict \ +curl -X POST http://localhost:4012/predict \ -F "file=@/path/to/corn-leaf.jpg" ``` @@ -164,13 +164,13 @@ cargo test cargo run # 2. Health check -curl http://localhost:8000/health +curl http://localhost:4012/health # 3. Metadata -curl http://localhost:8000/metadata +curl http://localhost:4012/metadata # 4. Prediksi -curl -X POST http://localhost:8000/predict \ +curl -X POST http://localhost:4012/predict \ -F "file=@../../Machine_Learning/dataset/Daun\ Sehat/sample.jpg" ``` @@ -182,7 +182,7 @@ Service dapat di-deploy via Docker. Build dari root repository karena Dockerfile ```bash docker build -f apps/ml-service/Dockerfile -t zeavis-ml-service . -docker run -p 8000:8000 zeavis-ml-service +docker run -p 8000:4012 zeavis-ml-service ``` Pastikan `Machine_Learning/model/model.onnx` sudah dibuat sebelum build image. @@ -210,7 +210,7 @@ MODEL_PATH=/absolute/path/to/model.onnx cargo run ```bash ML_SERVICE_PORT=9000 cargo run # Cek port yang digunakan: -lsof -i :8000 +lsof -i :4012 ``` ### ONNX Runtime tidak kompatibel diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf index e316eec..c491fd7 100644 --- a/apps/web/nginx.conf +++ b/apps/web/nginx.conf @@ -5,7 +5,7 @@ server { index index.html; location /api/ { - proxy_pass http://zeavis-api:3000/api/; + proxy_pass http://zeavis-api:4006/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -14,7 +14,7 @@ server { # Expose API metrics through the web endpoint (Prometheus scrape target) location /metrics { - proxy_pass http://zeavis-api:3000/metrics; + proxy_pass http://zeavis-api:4006/metrics; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/apps/web/src/pages/telemetry-page.tsx b/apps/web/src/pages/telemetry-page.tsx index eade06f..4098725 100644 --- a/apps/web/src/pages/telemetry-page.tsx +++ b/apps/web/src/pages/telemetry-page.tsx @@ -225,25 +225,25 @@ export function TelemetryPage() { queryInstant(`rate(node_network_receive_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`), queryInstant(`rate(node_network_transmit_bytes_total{instance="${INST}:9100",device="eth0"}[5m])`), // API - queryInstant(`zeavis_api_http_requests_total{instance="${INST}:3000"}`), - queryInstant(`zeavis_api_http_requests_active{instance="${INST}:3000"}`), - queryInstant(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:3000"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:3000"}`), - queryRange(`zeavis_api_http_requests_total{instance="${INST}:3000"}`, 60), - queryRange(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:3000"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:3000"}`, 60), + queryInstant(`zeavis_api_http_requests_total{instance="${INST}:4006"}`), + queryInstant(`zeavis_api_http_requests_active{instance="${INST}:4006"}`), + queryInstant(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:4006"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:4006"}`), + queryRange(`zeavis_api_http_requests_total{instance="${INST}:4006"}`, 60), + queryRange(`zeavis_api_http_request_duration_seconds_sum{instance="${INST}:4006"} / zeavis_api_http_request_duration_seconds_count{instance="${INST}:4006"}`, 60), // ML queryInstant(`zeavis_ml_zeavis_ml_model_load_status{instance="${INST}:8000"}`), // NodeJS - queryInstant(`nodejs_heap_size_used_bytes{instance="${INST}:3000"}`), - queryInstant(`nodejs_heap_size_total_bytes{instance="${INST}:3000"}`), - queryInstant(`nodejs_eventloop_lag_seconds{instance="${INST}:3000"}`), - queryInstant(`nodejs_active_handles_total{instance="${INST}:3000"}`), - queryInstant(`nodejs_active_requests_total{instance="${INST}:3000"}`), - queryRange(`nodejs_heap_size_used_bytes{instance="${INST}:3000"}`, 60), - queryRange(`nodejs_eventloop_lag_seconds{instance="${INST}:3000"}`, 60), + queryInstant(`nodejs_heap_size_used_bytes{instance="${INST}:4006"}`), + queryInstant(`nodejs_heap_size_total_bytes{instance="${INST}:4006"}`), + queryInstant(`nodejs_eventloop_lag_seconds{instance="${INST}:4006"}`), + queryInstant(`nodejs_active_handles_total{instance="${INST}:4006"}`), + queryInstant(`nodejs_active_requests_total{instance="${INST}:4006"}`), + queryRange(`nodejs_heap_size_used_bytes{instance="${INST}:4006"}`, 60), + queryRange(`nodejs_eventloop_lag_seconds{instance="${INST}:4006"}`, 60), // Process - queryInstant(`rate(process_cpu_seconds_total{instance="${INST}:3000"}[5m])`), - queryInstant(`process_resident_memory_bytes{instance="${INST}:3000"}`), - queryInstant(`process_open_fds{instance="${INST}:3000"}`), + queryInstant(`rate(process_cpu_seconds_total{instance="${INST}:4006"}[5m])`), + queryInstant(`process_resident_memory_bytes{instance="${INST}:4006"}`), + queryInstant(`process_open_fds{instance="${INST}:4006"}`), ]); setCpuData(cpuR); setMemData(memR); setDiskData(diskR); diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 5a566c7..6d7acf6 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -6,7 +6,7 @@ import { metricsPlugin } from './vite-plugin-metrics'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ''); - const apiProxyTarget = env.VITE_API_PROXY_TARGET || 'http://localhost:3000'; + const apiProxyTarget = env.VITE_API_PROXY_TARGET || 'http://localhost:4006'; return { plugins: [ diff --git a/docker-compose.yml b/docker-compose.yml index 3d80651..4826e4c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,21 +32,21 @@ services: - app-shared-net - telemetry-net ports: - - "${TS_IP:-0.0.0.0}:3000:3000" + - "${TS_IP:-0.0.0.0}:4006:4006" env_file: - .env environment: NODE_ENV: production - API_PORT: "3000" + API_PORT: "4006" WEB_APP_URL: https://zeavisedu.asepharyana.my.id - ML_SERVICE_URL: ${ML_SERVICE_URL:-http://zeavis-ml:8000} + ML_SERVICE_URL: ${ML_SERVICE_URL:-http://zeavis-ml:4012} labels: traefik.enable: "true" traefik.http.routers.zeavis-api.rule: Host(`api-zeavisedu.asepharyana.my.id`) traefik.http.routers.zeavis-api.entrypoints: websecure traefik.http.routers.zeavis-api.tls: "true" traefik.http.routers.zeavis-api.tls.certresolver: cloudflare - traefik.http.services.zeavis-api.loadbalancer.server.port: "3000" + traefik.http.services.zeavis-api.loadbalancer.server.port: "4006" # Node Exporter — expose system metrics (CPU, RAM, disk) for Prometheus scraping node_exporter: @@ -73,7 +73,7 @@ services: - app-shared-net - telemetry-net ports: - - "${TS_IP:-0.0.0.0}:8000:8000" + - "${TS_IP:-0.0.0.0}:4012:4012" env_file: - .env environment: @@ -85,4 +85,4 @@ services: traefik.http.routers.zeavis-ml.entrypoints: websecure traefik.http.routers.zeavis-ml.tls: "true" traefik.http.routers.zeavis-ml.tls.certresolver: cloudflare - traefik.http.services.zeavis-ml.loadbalancer.server.port: "8000" + traefik.http.services.zeavis-ml.loadbalancer.server.port: "4012" diff --git a/flake.nix b/flake.nix index 3c35de1..4a3bfa1 100644 --- a/flake.nix +++ b/flake.nix @@ -66,7 +66,7 @@ WRAPPER export MODEL_PATH="$out/share/zeavis-ml/model.onnx" export MODEL_INPUT_SIZE="224" export ML_SERVICE_HOST="0.0.0.0" -export ML_SERVICE_PORT="8200" +export ML_SERVICE_PORT="4012" export RUST_LOG="info" exec $out/bin/.zeavis-ml-service WRAPPER @@ -99,13 +99,13 @@ http { include ${pkgs.nginx}/conf/mime.types; access_log /var/lib/zeavis-web/nginx-access.log; server { - listen 8088; + listen 4011; server_name _; root $out/share/zeavis-web/html; index index.html; location /api/ { - proxy_pass http://127.0.0.1:3200/api/; + proxy_pass http://127.0.0.1:4006/api/; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; @@ -113,7 +113,7 @@ http { } location /metrics { - proxy_pass http://127.0.0.1:3200/metrics; + proxy_pass http://127.0.0.1:4006/metrics; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; diff --git a/infra/README.md b/infra/README.md index 6a4992c..d37bed4 100644 --- a/infra/README.md +++ b/infra/README.md @@ -29,7 +29,7 @@ ZeaVis Edu berjalan di **dua VPS terpisah** yang terhubung melalui **Tailscale** │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ ┌──────────┐ ┌──────────────┐ │ │ │ Web │ │ API │ │ ML │ │ │ │Prometheus│ │Metric │ │ -│ │:80 │ │:3000 │ │:8000 │ │ │ │:9090 │ │Ingester │ │ +│ │:80 │ │:4006 │ │:4012 │ │ │ │:9090 │ │Ingester │ │ │ │/metrics │ │/metrics │ │/metrics │ │ │ │ │ │:9091 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ └────┬─────┘ └──────┬───────┘ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │ @@ -65,7 +65,7 @@ ZeaVis Edu berjalan di **dua VPS terpisah** yang terhubung melalui **Tailscale** | VPS | Hostname | OS | Peran | |---|---|---|---| -| **App VPS** | `imrnes` | Arch Linux | Web (:80), API (:3000), ML Service (:8000) | +| **App VPS** | `imrnes` | Arch Linux | Web (:80), API (:4006), ML Service (:4012) | | **Telemetry VPS** | `orange` | Ubuntu | Prometheus, ClickHouse, Telemetry UI | --- @@ -128,8 +128,8 @@ bash clickhouse/init.sh | Port | Service | Akses | |---|---|---| | 80/443 | Web (via Traefik/Coolify) | Public | -| 3000 | API metrics | Tailscale-only | -| 8000 | ML service metrics | Tailscale-only | +| 4006 | API metrics | Tailscale-only | +| 4012 | ML service metrics | Tailscale-only | | 9100 | Node Exporter | Tailscale-only | ### Telemetry VPS (orange)