Author SHA1 Message Date
asepharyana 5319971bfd fix: update llm-api MODEL_PATH for new MiniCPM5 GGUF 2026-07-26 15:39:58 +07:00
18 changed files with 14 additions and 606 deletions
@@ -1,20 +0,0 @@
name: Publish to FlakeHub
on:
push:
branches: [main, master]
workflow_dispatch:
jobs:
flakehub-publish:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- uses: DeterminateSystems/determinate-nix-action@main
- uses: DeterminateSystems/flakehub-push@main
with:
visibility: public
rolling: true
-84
View File
@@ -1,84 +0,0 @@
name: Nix Build & Deploy — All Services
on:
push:
branches: [main]
paths:
- 'apps/**'
- 'infra/**'
- 'flake.nix'
- 'flake.lock'
- '.github/workflows/nix-build.yml'
workflow_dispatch:
concurrency:
group: nix-deploy
cancel-in-progress: false
permissions:
contents: read
env:
VPS_HOST: ${{ secrets.VPS_HOST }}
VPS_USER: ${{ secrets.VPS_USER }}
jobs:
build-and-deploy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
service: [hub, scraper, tools-gateway, tools-workers, tools-frontend, llm-api]
steps:
- name: Checkout
uses: actions/checkout@v7
with:
submodules: recursive
fetch-depth: 0
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@v22
with:
determinate: false
extra-conf: |
sandbox = false
accept-flake-config = true
- name: Cache Nix
uses: DeterminateSystems/magic-nix-cache-action@v14
- name: Build ${{ matrix.service }}
id: build
run: |
nix build .#${{ matrix.service }} --impure --option sandbox false --print-build-logs
STORE_PATH=$(readlink result)
echo "store-path=$STORE_PATH" >> "$GITHUB_OUTPUT"
echo "✅ ${{ matrix.service }}: $STORE_PATH"
- name: Setup SSH key
if: github.ref == 'refs/heads/main'
env:
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
sed -i 's/\r$//' ~/.ssh/id_ed25519
ssh-keygen -y -f ~/.ssh/id_ed25519 >/dev/null 2>&1 || { echo "SSH key invalid"; exit 1; }
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts 2>/dev/null
- name: Deploy ${{ matrix.service }} to VPS
if: github.ref == 'refs/heads/main'
run: |
STORE_PATH="${{ steps.build.outputs.store-path }}"
echo "=== Copying ${{ matrix.service }}: $STORE_PATH ==="
nix copy --to "ssh://$VPS_USER@$VPS_HOST" "$STORE_PATH"
echo "=== Updating profile ==="
ssh "$VPS_USER@$VPS_HOST" "sudo /nix/var/nix/profiles/default/bin/nix-env --profile /nix/var/nix/profiles/${{ matrix.service }} --set '$STORE_PATH'"
echo "=== Restarting service ==="
ssh "$VPS_USER@$VPS_HOST" "sudo systemctl restart ${{ matrix.service }}" || echo " ⚠️ restart failed (may not be enabled yet)"
echo "✅ ${{ matrix.service }} deployed"
-1
View File
@@ -63,4 +63,3 @@ docs/todo.md
**/vendor/ **/vendor/
.codegraph/ .codegraph/
result
Generated
-61
View File
@@ -1,61 +0,0 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1785301185,
"narHash": "sha256-eoS3KQTO0aPWXZvIaRbRAzSSHW3l5wdMFXtT1ISfoKA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9bc02893134c733dd85de46ee4fb2fac696b5529",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
-208
View File
@@ -1,208 +0,0 @@
{
description = "Asepharyana Hub Nix builds for infrastructure and app services";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachSystem [ "x86_64-linux" ] (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
# ── mkApp generator ──
mkApp = { name, src, buildScript, installScript, nativeBuildInputs ? [], buildInputs ? [] }:
pkgs.stdenv.mkDerivation {
inherit name src;
nativeBuildInputs = with pkgs; [
cacert curl gcc gnumake openssl pkg-config python3 libclang
] ++ nativeBuildInputs;
buildInputs = with pkgs; [
nodejs openssl stdenv.cc.cc.lib libffi
] ++ buildInputs;
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
LD_LIBRARY_PATH = "${pkgs.libclang.lib}/lib:${pkgs.stdenv.cc.cc.lib}/lib:${pkgs.libffi}/lib";
NIX_ENFORCE_PURITY = "0";
SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
NODE_EXTRA_CA_CERTS = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
NODE_ENV = "production";
phases = [ "unpackPhase" "buildPhase" "installPhase" ];
buildPhase = ''
export HOME="$TMPDIR" CARGO_HOME="$TMPDIR/.cargo-${name}"
SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
'' + buildScript;
installPhase = installScript;
};
# ── Node.js ──
nodejs = pkgs.nodejs-slim_22;
pnpm = pkgs.pnpm.override { inherit nodejs; };
# ── Common Rust build deps ──
cargoDeps = with pkgs; [ rustc cargo clang cmake pkg-config openssl.dev zlib ];
# ── Submodule repos — URLs from .gitmodules ──
submoduleRepos = {
hub = "https://github.com/asepharyana/asepharyana-hub-hub.git";
scraper = "https://github.com/asepharyana/asepharyana-hub-scraper.git";
tools = "https://github.com/asepharyana/asepharyana-hub-tools.git";
llm-api = "https://github.com/asepharyana/asepharyana-hub-llm-api.git";
};
# ── Fetch submodule source ──
submoduleSrc = name: builtins.fetchGit {
url = submoduleRepos.${name};
rev = if name == "hub" then "b007ced615dcc78392fc938350a23e8f022d83b7"
else if name == "scraper" then "62aa5b0e52859afe3ba9de1c7b11cfe2dacf6c2c"
else if name == "tools" then "3956b90c3ce39ffa7ffba8084937f20e11364d6b"
else if name == "llm-api" then "67861f384bd4f64d5236a9608b33c965fbebbc7f"
else "HEAD";
submodules = true;
};
# ─── App Derivations ───
hub = mkApp {
name = "hub-0.1.0";
src = submoduleSrc "hub";
nativeBuildInputs = with pkgs; [ bun ];
buildScript = ''
echo "=== Installing dependencies ==="
bun install 2>&1
echo "=== Building Next.js ==="
bun run build 2>&1
'';
installScript = ''
mkdir -p $out/share/hub $out/bin
cp -r .next $out/share/hub/
cp -r public $out/share/hub/ 2>/dev/null || true
cp package.json $out/share/hub/
cp -r node_modules $out/share/hub/
cat > $out/bin/hub << WRAPPER
#!${pkgs.runtimeShell}
exec ${pkgs.bun}/bin/bun run --cwd $out/share/hub start
WRAPPER
chmod +x $out/bin/hub
'';
};
scraper = mkApp {
name = "scraper-0.1.0";
src = submoduleSrc "scraper";
nativeBuildInputs = cargoDeps;
buildScript = ''
echo "=== Building scraper ==="
cargo build --release 2>&1
'';
installScript = ''
mkdir -p $out/bin
cp target/release/scraper $out/bin/scraper
'';
};
tools-gateway = mkApp {
name = "tools-gateway-0.1.0";
src = submoduleSrc "tools";
nativeBuildInputs = cargoDeps ++ [ pkgs.tesseract ];
buildScript = ''
cd backend
echo "=== Building tools-gateway ==="
cargo build --release --features tesseract --bin tools-gateway 2>&1
'';
installScript = ''
mkdir -p $out/bin
cp target/release/tools-gateway $out/bin/tools-gateway
'';
};
tools-workers = mkApp {
name = "tools-workers-0.1.0";
src = submoduleSrc "tools";
nativeBuildInputs = cargoDeps ++ [ pkgs.tesseract pkgs.leptonica ];
buildScript = ''
cd backend
echo "=== Building tools-workers ==="
cargo build --release --features tesseract --bin tools-workers 2>&1
'';
installScript = ''
mkdir -p $out/bin
cp target/release/tools-workers $out/bin/tools-workers
'';
};
tools-frontend = mkApp {
name = "tools-frontend-0.1.0";
src = submoduleSrc "tools";
nativeBuildInputs = with pkgs; [ bun ];
buildScript = ''
cd frontend
echo "=== Installing dependencies ==="
bun install 2>&1
echo "=== Building Next.js ==="
bun run build 2>&1
'';
installScript = ''
mkdir -p $out/share/tools-frontend $out/bin
cp -r .next $out/share/tools-frontend/
cp -r public $out/share/tools-frontend/ 2>/dev/null || true
cp package.json $out/share/tools-frontend/
cp -r node_modules $out/share/tools-frontend/
cat > $out/bin/tools-frontend << WRAPPER
#!${pkgs.runtimeShell}
exec ${pkgs.bun}/bin/bun run --cwd $out/share/tools-frontend start
WRAPPER
chmod +x $out/bin/tools-frontend
'';
};
llm-api = mkApp {
name = "llm-api-0.1.0";
src = submoduleSrc "llm-api";
nativeBuildInputs = cargoDeps ++ [ pkgs.cmake pkgs.gcc ];
buildScript = ''
echo "=== Building llm-api ==="
cargo build --release 2>&1
'';
installScript = ''
mkdir -p $out/bin
cp target/release/llm-api $out/bin/llm-api
'';
};
in
{
packages = {
inherit hub scraper tools-gateway tools-workers tools-frontend llm-api;
default = hub;
};
apps.hub = {
type = "app";
program = "${hub}/bin/hub";
};
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [ nodejs-slim_22 bun pnpm rustc cargo ];
};
});
}
@@ -1,7 +1,7 @@
services: services:
hub: hub:
container_name: hub container_name: hub
image: ghcr.io/asepharyana/asepharyana-hub/hub:sha-ac25a51 image: ghcr.io/asepharyana/asepharyana-hub/hub:sha-22e8a87
restart: always restart: always
networks: networks:
app-shared-net: app-shared-net:
@@ -1,7 +1,7 @@
services: services:
llm-api: llm-api:
container_name: llm-api container_name: llm-api
image: ghcr.io/asepharyana/asepharyana-hub/llm-api:sha-43f0df4 image: ghcr.io/asepharyana/asepharyana-hub/llm-api:sha-e953c3c
restart: always restart: always
networks: networks:
app-shared-net: app-shared-net:
@@ -1,7 +1,7 @@
services: services:
scraper-api: scraper-api:
container_name: scraper-api container_name: scraper-api
image: ghcr.io/asepharyana/asepharyana-hub/scraper-api:sha-2459541 image: ghcr.io/asepharyana/asepharyana-hub/scraper-api:sha-e953c3c
restart: always restart: always
depends_on: depends_on:
nats: nats:
@@ -1,7 +1,7 @@
services: services:
tools: tools:
container_name: tools container_name: tools
image: ghcr.io/asepharyana/asepharyana-hub/tools:sha-ac25a51 image: ghcr.io/asepharyana/asepharyana-hub/tools:sha-e953c3c
restart: always restart: always
networks: networks:
app-shared-net: app-shared-net:
+1 -9
View File
@@ -13,7 +13,6 @@ services:
ports: ports:
- '80:80' - '80:80'
- '443:443' - '443:443'
- '443:443/udp'
networks: networks:
- app-shared-net - app-shared-net
extra_hosts: extra_hosts:
@@ -46,13 +45,6 @@ services:
- '--entryPoints.websecure.transport.lifeCycle.requestAcceptGraceTimeout=15s' - '--entryPoints.websecure.transport.lifeCycle.requestAcceptGraceTimeout=15s'
- '--entryPoints.websecure.transport.lifeCycle.graceTimeOut=10s' - '--entryPoints.websecure.transport.lifeCycle.graceTimeOut=10s'
- '--entryPoints.websecure.address=:443' - '--entryPoints.websecure.address=:443'
- '--entryPoints.websecure.http3=true'
# ── Response speed tuning ──
- '--serversTransport.maxIdleConnsPerHost=100'
- '--serversTransport.forwardingTimeouts.dialTimeout=3s'
- '--serversTransport.forwardingTimeouts.idleConnTimeout=180s'
- '--global.checkNewVersion=false'
- '--global.sendAnonymousUsage=false'
- '--experimental.plugins.real-ip.moduleName=github.com/soulbalz/traefik-real-ip' - '--experimental.plugins.real-ip.moduleName=github.com/soulbalz/traefik-real-ip'
- '--experimental.plugins.real-ip.version=v1.0.3' - '--experimental.plugins.real-ip.version=v1.0.3'
- '--experimental.plugins.blockpath.moduleName=github.com/traefik/plugin-blockpath' - '--experimental.plugins.blockpath.moduleName=github.com/traefik/plugin-blockpath'
@@ -76,7 +68,7 @@ services:
- GOGC=200 - GOGC=200
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- ${TRAEFIK_CONFIG_PATH:-/home/code/asepharyana-hub/infra/traefik/dynamic}:/etc/traefik/dynamic:ro - ${TRAEFIK_CONFIG_PATH:-/root/asepharyana-hub/infra/traefik/dynamic}:/etc/traefik/dynamic:ro
- ${TRAEFIK_CERT_MY_ID_PEM:-/root/asepharyana.my.id.pem}:/etc/traefik/certs/asepharyana.my.id.pem:ro - ${TRAEFIK_CERT_MY_ID_PEM:-/root/asepharyana.my.id.pem}:/etc/traefik/certs/asepharyana.my.id.pem:ro
- ${TRAEFIK_CERT_MY_ID_KEY:-/root/asepharyana.my.id.key}:/etc/traefik/certs/asepharyana.my.id.key:ro - ${TRAEFIK_CERT_MY_ID_KEY:-/root/asepharyana.my.id.key}:/etc/traefik/certs/asepharyana.my.id.key:ro
- ${TRAEFIK_CERT_WEB_ID_PEM:-/root/asepharyana.web.id.pem}:/etc/traefik/certs/asepharyana.web.id.pem:ro - ${TRAEFIK_CERT_WEB_ID_PEM:-/root/asepharyana.web.id.pem}:/etc/traefik/certs/asepharyana.web.id.pem:ro
-4
View File
@@ -54,7 +54,3 @@ service:
receivers: [otlp] receivers: [otlp]
processors: [memory_limiter, batch, attributes] processors: [memory_limiter, batch, attributes]
exporters: [prometheus, debug] exporters: [prometheus, debug]
logs:
receivers: [otlp]
processors: [memory_limiter, batch, attributes]
exporters: [debug]
+5 -155
View File
@@ -27,7 +27,7 @@ http:
- websecure - websecure
tls: {} tls: {}
middlewares: middlewares:
- llm-chain@file - common-chain@file
service: llm-api-service service: llm-api-service
# ── Tools (Document Scanner & Media Processing) ── # ── Tools (Document Scanner & Media Processing) ──
@@ -50,178 +50,28 @@ http:
- common-chain@file - common-chain@file
service: jaeger-service service: jaeger-service
# ── 9Router (AI routing gateway) ──
9router:
rule: 'Host(`9router.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: 9router-service
# ── PR-Agent (GitHub App webhook) ──
pr-agent:
rule: 'Host(`pr-agent.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: pr-agent-service
# ── LIDM Frontend ──
lidm-frontend:
rule: 'Host(`lidm.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: lidm-frontend-service
# ── LIDM Backend API ──
lidm-backend:
rule: 'Host(`lidm-api.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: lidm-backend-service
# ── ZeaVis Edu Frontend ──
zeavisedu:
rule: 'Host(`zeavisedu.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: zeavisedu-service
# ── ZeaVis Edu API ──
api-zeavisedu:
rule: 'Host(`api-zeavisedu.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: api-zeavisedu-service
# ── ZeaVis Edu ML Service ──
ml-zeavisedu:
rule: 'Host(`ml-zeavisedu.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: ml-zeavisedu-service
# ── Hermes Dashboard ──
hermes-dashboard:
rule: 'Host(`dashboard.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: hermes-dashboard-service
# ── GMW Discord Automod Dashboard (Nix: gmw-proxy on 8080) ──
gmw:
rule: 'Host(`imphnen.asepharyana.my.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- common-chain@file
service: gmw-service
# ── TeleUploader (S3 to Telegram Bridge, Nix: bun on 3000) ──
teleuploader:
rule: 'Host(`upload.asepharyana.my.id`) || Host(`upload.asepharyana.web.id`)'
entryPoints:
- websecure
tls: {}
middlewares:
- upload-chain@file
service: teleuploader-service
services: services:
hub-service: hub-service:
loadBalancer: loadBalancer:
servers: servers:
- url: 'http://host.docker.internal:3099' - url: 'http://hub:3000'
scraper-service: scraper-service:
loadBalancer: loadBalancer:
servers: servers:
- url: 'http://host.docker.internal:4091' - url: 'http://scraper-api:4091'
tools-service: tools-service:
loadBalancer: loadBalancer:
servers: servers:
- url: 'http://host.docker.internal:3500' - url: 'http://tools:3000'
llm-api-service: llm-api-service:
loadBalancer: loadBalancer:
servers: servers:
- url: 'http://host.docker.internal:8082' - url: 'http://llm-api:8080'
jaeger-service: jaeger-service:
loadBalancer: loadBalancer:
servers: servers:
- url: 'http://jaeger:16686' - url: 'http://jaeger:16686'
9router-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:20128'
pr-agent-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:3002'
lidm-frontend-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:3100'
lidm-backend-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:3101'
zeavisedu-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:8088'
api-zeavisedu-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:3200'
ml-zeavisedu-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:8200'
hermes-dashboard-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:9119'
gmw-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:8080'
teleuploader-service:
loadBalancer:
servers:
- url: 'http://host.docker.internal:3000'
-28
View File
@@ -61,14 +61,6 @@ http:
- "^/wp-login\\.php" - "^/wp-login\\.php"
- "^/config\\.php" - "^/config\\.php"
# ── LLM Stream Chain (no buffer/compress — SSE needs real-time) ──
llm-chain:
chain:
middlewares:
- secure-headers
- retry
- rate-limit
# ── Common Chain ── # ── Common Chain ──
common-chain: common-chain:
chain: chain:
@@ -78,23 +70,3 @@ http:
- retry - retry
- rate-limit - rate-limit
- buffer - buffer
# ── TeleUploader Chain (2GB body buffer — large file uploads) ──
upload-buffer:
buffering:
maxRequestBodyBytes: 2147483648
maxResponseBodyBytes: 2147483648
memRequestBodyBytes: 1048576
memResponseBodyBytes: 1048576
upload-rate-limit:
rateLimit:
average: 300
burst: 100
period: 1m
upload-chain:
chain:
middlewares:
- secure-headers
- retry
- upload-rate-limit
- upload-buffer
-28
View File
@@ -1,28 +0,0 @@
#!/usr/bin/env bash
# ── Nix Deploy — VPS-side script ──
# Runs after `nix copy --to ssh://VPS ./result` from CI
# Usage: sudo ./deploy.sh <service-name>
set -euo pipefail
SERVICE="$1"
PROFILE="/nix/var/nix/profiles/${SERVICE}"
# Find the latest store path for this service
LATEST=$(ls -1d /nix/store/*-"${SERVICE}"-* 2>/dev/null | tail -1)
if [ -z "$LATEST" ]; then
echo "ERROR: No store path found for ${SERVICE}"
exit 1
fi
# Update profile
/nix/var/nix/profiles/default/bin/nix-env --profile "$PROFILE" --set "$LATEST"
# Restart service
systemctl daemon-reload
systemctl enable --now "${SERVICE}" 2>/dev/null || systemctl restart "${SERVICE}"
echo "Deployed ${SERVICE}: ${LATEST}"
systemctl is-active "${SERVICE}"
# Collect garbage (safe: only removes unreachable paths)
# nix-collect-garbage -d 2>/dev/null || true