Add Gitea Actions build workflow and README for vnctalk-avatar
Build + push avatar image / build-and-push (push) Failing after 20s
Build + push avatar image / build-and-push (push) Failing after 20s
This commit is contained in:
@@ -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"
|
||||||
@@ -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/<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 `PUT`s 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
|
||||||
|
|
||||||
|
```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://<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}`. |
|
||||||
Reference in New Issue
Block a user