From 8493d8b09b7417c55899f260faef6fcd6a815f37 Mon Sep 17 00:00:00 2001 From: macanhhuy Date: Sat, 22 Aug 2026 00:05:04 +0700 Subject: [PATCH] Add Gitea Actions build workflow and README for vnctalk-avatar --- .gitea/workflows/build.yml | 56 +++++++++++++++++++++++ README.md | 94 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 .gitea/workflows/build.yml create mode 100644 README.md diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..a6e0a16 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,56 @@ +name: Build + push avatar image +on: + push: + branches: [main] + workflow_dispatch: + +env: + REGISTRY: gitea.saas.vnc.biz + IMAGE: gitea.saas.vnc.biz/vnclagoon/vnctalk-avatar + +jobs: + build-and-push: + runs-on: ubuntu-latest + env: + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: Compute image tag + run: | + SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) + echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV + + # kaniko: build + push without a Docker daemon (runner job container has + # no /var/run/docker.sock). All deps are public (npm/yarn), no NPM_TOKEN. + - name: Fetch kaniko (daemon-less image builder) + run: | + set -euo pipefail + curl -fsSL -o /tmp/crane.tgz \ + https://github.com/google/go-containerregistry/releases/download/v0.21.9/go-containerregistry_Linux_x86_64.tar.gz + tar -xzf /tmp/crane.tgz -C /usr/local/bin crane + mkdir -p /tmp/kaniko-root + crane export gcr.io/kaniko-project/executor:debug /tmp/kaniko-fs.tar + tar -xf /tmp/kaniko-fs.tar -C /tmp/kaniko-root + cp /tmp/kaniko-root/kaniko/executor /usr/local/bin/kaniko-executor + chmod +x /usr/local/bin/kaniko-executor + /usr/local/bin/kaniko-executor version + + - name: Configure registry auth for push + run: | + AUTH=$(printf '%s:%s' "${REGISTRY_USER}" "${REGISTRY_TOKEN}" | base64 -w0) + CFG=$(printf '{"auths":{"%s":{"auth":"%s"}}}' "${REGISTRY}" "${AUTH}") + mkdir -p /kaniko/.docker "$HOME/.docker" + printf '%s' "$CFG" > /kaniko/.docker/config.json + printf '%s' "$CFG" > "$HOME/.docker/config.json" + echo "DOCKER_CONFIG=/kaniko/.docker" >> "$GITHUB_ENV" + + - name: Build + push avatar image + run: | + /usr/local/bin/kaniko-executor \ + --context "dir://${GITHUB_WORKSPACE}" \ + --dockerfile Dockerfile \ + --skip-tls-verify-registry="${REGISTRY}" \ + --destination "${IMAGE}:sha-${SHORT_SHA}" \ + --destination "${IMAGE}:latest" diff --git a/README.md b/README.md new file mode 100644 index 0000000..52c792f --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# vnctalk-avatar + +The VNCtalk **avatar service** — a small Node/nginx service that preprocesses +(GraphicsMagick resize) and serves user avatars. This is the code that backs +`avatar.vnc.biz`; deployed in-cluster it becomes the dev cluster's avatar store. + +## What it does + +On `PUT /avatarupload/` it receives raw image bytes, resizes them to a +fixed set of resolutions, and writes them to `outputDir` keyed by `md5(jid)`: + +``` +/.jpg # default resolution (80) +/-.jpg # 46, 47, 57, 80, 420, 800 +``` + +The frontend reads these back via `avatarServiceUrl + "/" + md5(jid) + "-.jpg"`. +Prosody's `vnc_vcard_avatar` module is what `PUT`s the photo here on every vCard +avatar change (`avatar_upload_url` → `/avatarupload/`). + +## API + +| Method | Path | Purpose | +|---|---|---| +| `PUT` | `/avatarupload/:jid` | Upload avatar bytes (`Content-Type: image/*`); returns `md5(jid)`. | +| `DELETE` | `/avatarupload/:jid` | Remove all of a jid's avatar files. | +| `GET` | `/avatarupload/health` | `{"status":"OK"}`. | +| `POST` | `/avatarupload/info` | Batch `ctime` lookup for a list of md5 ids. | + +Static image serving (`GET /.jpg`) is **not** handled by `app.js` — it is +served by an nginx that shares the `outputDir` volume. The container installs +nginx but the static-serving server config must be supplied at deploy time (see +Deploy below). + +## Auth + +`PUT`/`DELETE` require either: + +- the legacy global Basic credentials (`avatar` / the shared avatar-server + password), or +- a JWT (Bearer/basic-password slot) signed with `config.jwtsecret` whose + `vncdomain` claim matches the jid's domain. + +> **Open item:** both the global Basic credential hash and `jwtsecret` are +> hardcoded in `app/app.js` / `config/vnc-avatarservice.js`. These are shared +> service credentials already committed elsewhere (vncdirectory/vncproject). +> For a clean in-cluster deploy they should move to env vars / Infisical — +> tracked separately. + +## Config (`config/vnc-avatarservice.js`) + +| Key | Default | Purpose | +|---|---|---| +| `servicePort` | `3896` | Node upload API port. | +| `outputDir` | `/var/opt/out` | Avatar filesystem store (needs a persistent volume). | +| `defaultresolution` | `80` | `.jpg` (no suffix) resolution. | +| `resolutions` | `[46,47,57,80,420,800]` | All sizes written on upload. | +| `jwtsecret` | hardcoded | JWT verification secret (move to env). | + +`NODE_ENV` selects the config block (`development` / `development2` / `gr13`). + +## Build + +```bash +docker build -t vnctalk-avatar . +# CI: push to main → .gitea/workflows/build.yml → kaniko +``` + +## Deploy + +Not yet declared in `vnc-iac-env`. Two prerequisites for a working in-cluster +deploy (tracked separately): + +1. **Persistent `outputDir`** (`/var/opt/out`) — otherwise avatars are lost on + pod restart. +2. **nginx static serving** — a server block (or ingress) that serves the + `outputDir` files at `avatarServiceUrl`, plus the CORS headers the browser + needs. + +Once deployed, point `vnctalk-prosody`'s `avatar_upload_url` at it +(`https:///avatarupload/`) and set `avatar_upload_user` / +`avatar_upload_pass` (from Infisical `AVATAR_PASSWORD`, not git). + +## Layout + +| Path | Purpose | +|---|---| +| `app/app.js` | Express upload API (`PUT /avatarupload/:jid`, health, info). | +| `config/vnc-avatarservice.js` | Env-selected config (port, outputDir, resolutions, jwtsecret). | +| `Dockerfile` | Alpine multi-stage, non-root (`vnc`, uid 1001), gm + nginx + node. | +| `Dockerfile.ubuntu` | Legacy GCP base-image variant (pm2 + deb `vnc-avatarservice`). | +| `debian/` | Legacy Debian packaging (used by the GCP-era image). | +| `conf.template/` | Legacy passenger/nginx templates (mostly notification-proxy leftovers). | +| `.gitea/workflows/build.yml` | kaniko build → `vnclagoon/vnctalk-avatar:{sha-…,latest}`. |