/// // Auto-update manifest (epic #6, issue #16). // // GET /api/update/manifest?platform=windows|linux|android (auth: users) // // Returns the latest published build for the caller's platform. The VARIANT is // chosen server-side from the account's admin-set `nsfwEnabled` (adult) vs clean // — the client never asks for adult. This is belt-and-suspenders on top of the // `releases` collection rules, which already hide adult rows from non-flagged // accounts. The download is PocketBase's native protected file endpoint: the app // mints a short-lived file token (`POST /api/files/token`) and appends it to // `downloadPath`, and the token grant re-checks the same gating rule. // // NOTE: PocketBase runs each routerAdd handler in its own isolated JSVM, so the // small helpers live inside the handler. routerAdd("GET", "/api/update/manifest", (e) => { const ALLOWED = ["windows", "linux", "android"] const platform = (e.request.url.query().get("platform") || "").toLowerCase().trim() if (ALLOWED.indexOf(platform) === -1) throw new BadRequestError("bad or missing platform") // The account flag decides the channel; a client can't opt into adult. const variant = e.auth.getBool("nsfwEnabled") ? "adult" : "clean" let rec = null try { const rows = $app.findRecordsByFilter( "releases", "platform = {:p} && variant = {:v}", "-buildNumber", 1, 0, { p: platform, v: variant } ) if (rows && rows.length) rec = rows[0] } catch (_) { /* no matching release */ } if (!rec) return e.json(200, { available: false }) const filename = rec.getString("file") return e.json(200, { available: true, platform: platform, variant: variant, version: rec.getString("version"), buildNumber: rec.getInt("buildNumber"), notes: rec.getString("notes"), sha256: rec.getString("sha256"), size: rec.getInt("size"), filename: filename, // Native protected-file path; the app appends `?token=`. downloadPath: "/api/files/releases/" + rec.id + "/" + filename, }) }, $apis.requireAuth("users"))