Files
vnctalk-avatar/README.md
T
macanhhuy 8493d8b09b
Build + push avatar image / build-and-push (push) Failing after 20s
Add Gitea Actions build workflow and README for vnctalk-avatar
2026-08-22 00:05:04 +07:00

3.8 KiB

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/<jid> it receives raw image bytes, resizes them to a fixed set of resolutions, and writes them to outputDir keyed by md5(jid):

<outputDir>/<md5(jid)>.jpg            # default resolution (80)
<outputDir>/<md5(jid)>-<res>.jpg      # 46, 47, 57, 80, 420, 800

The frontend reads these back via avatarServiceUrl + "/" + md5(jid) + "-<size>.jpg". Prosody's vnc_vcard_avatar module is what PUTs the photo here on every vCard avatar change (avatar_upload_url<avatar-host>/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 /<md5>.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

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://<avatar-host>/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}.