Distribution site served by PocketBase itself (pb_public/, same origin): - Landing (/): create-account CTA, TV sign-in steps, login-gated PC downloads via the existing update manifest + file tokens - Wizard (/onboarding.html): invite code -> account -> named default profile -> family template fetched and encrypted IN THE BROWSER under the new user's password (PBKDF2-210k + AES-GCM, byte-compatible with the app's AddonConfigCrypto - a wizard blob decrypts in Dart, verified) -> pushed as their addon_config ciphertext. Adult fields never included. - /get/tv: public direct download of the latest clean Android APK (Downloader-friendly; adult builds stay account-gated) Backend: - onboarding_template collection (admin-only, maintained via admin UI) - AMBER_INVITE_CODE env gates BOTH users creation (X-Amber-Invite header or ?invite=) and the template route (per-IP rate limit 10/5min); fail-closed when unset. Note: this closes the previously-open in-app registration too. - Dockerfile ships pb_public + --publicDir; compose passes the env var Contract + Coolify steps (second domain amber.petruzalekr.cz) in docs/onboarding-contract.md. Verified locally end-to-end against pocketbase 0.39.6 (real-browser wizard run + Dart decrypt interop). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
46 lines
1.8 KiB
JavaScript
46 lines
1.8 KiB
JavaScript
/// <reference path="../pb_data/types.d.ts" />
|
|
|
|
// Family onboarding template (distribution site).
|
|
//
|
|
// One admin-maintained record holding the *plaintext* addon config that new
|
|
// family accounts start from: the onboarding page (pb_public) fetches it
|
|
// through an invite-code-gated route, encrypts it in the browser under the new
|
|
// user's password, and pushes the result as their addon_config blob — so the
|
|
// server still only ever stores ciphertext per account, and family members get
|
|
// a working setup without typing addon URLs.
|
|
//
|
|
// Deliberately NO adult fields: the template is family config by definition
|
|
// (mirrors the #14/#20 child-profile stripping). Adult stays per-account,
|
|
// configured manually.
|
|
//
|
|
// All rules are null → superusers only. Clients never read this collection
|
|
// directly; the invite-gated hook (onboarding.pb.js) is the sole reader.
|
|
// Maintain the single record via the PocketBase admin UI.
|
|
|
|
migrate((app) => {
|
|
const tpl = new Collection({
|
|
type: "base",
|
|
name: "onboarding_template",
|
|
listRule: null,
|
|
viewRule: null,
|
|
createRule: null,
|
|
updateRule: null,
|
|
deleteRule: null,
|
|
fields: [
|
|
// Same value shapes SettingsRepository stores (base URLs, no trailing
|
|
// /manifest.json). All optional so the record can be filled gradually.
|
|
{ type: "text", name: "addonUrl", max: 2000 },
|
|
{ type: "text", name: "czechAddonUrl", max: 2000 },
|
|
{ type: "text", name: "tmdbKey", max: 200 },
|
|
// Free-form admin note ("updated after TorBox key rotation" etc.) —
|
|
// never served to clients.
|
|
{ type: "text", name: "note", max: 1000 },
|
|
{ type: "autodate", name: "updated", onCreate: true, onUpdate: true },
|
|
],
|
|
})
|
|
app.save(tpl)
|
|
}, (app) => {
|
|
try {
|
|
app.delete(app.findCollectionByNameOrId("onboarding_template"))
|
|
} catch (_) { /* already gone */ }
|
|
})
|