amber-backend/pb_hooks/web.pb.js

31 lines
1.5 KiB
JavaScript
Raw Permalink Normal View History

Accounts are owner-created, and one page manages the whole account Registration is closed. The instance was gated by a single shared invite code in AMBER_INVITE_CODE -- a bearer secret, one string for everyone, forever, no record of who used it, and known to every family member who ever onboarded, so it leaked by design. An email whitelist was the obvious replacement and does not survive contact with the facts: it binds to a person only if the address is verified, and all five accounts on this instance are `verified = false`. For a household this size the owner creating each account in the admin UI is stronger than either and has nothing to leak. `users.createRule` is superuser-only now, and the hook refuses with a sentence a family member can read rather than a bare 403. The family code survives with a narrower job. It is no longer a key to the front door; it is what unlocks the *shared family credentials* during setup. Close friends get accounts too and bring their own (BYOC), so "has an account" must not mean "may have the family's sources" -- the template route now wants auth AND the code, keeping its rate limiter because the code is still a shared secret. pb_public/account.html replaces index/settings/onboarding with one page behind a persisted session: profiles (name, child flag, age ceiling, PIN), the 13 synced playback preferences per profile, sources, devices, downloads, account. The token persists in localStorage; the password never does, and anything needing it asks in the moment -- that is the honest split between "stay signed in" and "hold the key to your credentials in a browser tab". Sources can now be set up from your own service credentials. The page posts them straight to the addon's own /encode and builds TorBox's config token locally -- both addons send permissive CORS, so the credentials go from the family member's browser to the addon and never touch our server. prehraj.to is required because the addon's /encode returns 400 without it and decodeConfig rejects such a token, so "webshare only" is not currently possible; the copy says so rather than pretending. TorBox is optional and the copy is emphatic about what it is for. Two hazards handled rather than hoped over. Changing the password re-keys the addon vault, and the re-encrypt happens BEFORE the password PATCH: if it were after, a failure would leave a blob no device can read. And prefs writes carry `updatedAt`, the clock the app's LWW actually compares -- the server's `updated` is only the pull cursor -- and merge into the existing data rather than replacing it, because the app writes fields this page does not show yet. Also: `users.ratingDefault`, so the account-wide age ceiling stops being the one half of a parental control that does not sync. The app side ships with the next release. The other three device-local settings stay local on purpose, and `adult_enabled` most deliberately of all. Plan and reasoning: docs/plan-accounts-and-web.md in amber-app.
2026-08-06 23:54:25 +00:00
/// <reference path="../pb_data/types.d.ts" />
// Pretty URLs for the family-facing web surfaces.
//
The unified page becomes the site; retire the two it replaced index.html was the old landing page with a signup form, and that form can now only fail — registration is owner-only. A dead button is worse than no button, so the unified account page takes its place rather than sitting beside it. settings.html goes too: it is a strict subset of the sources section, and onboarding.html was the signup wizard. Verified against the live backend before the swap, signed in as a throwaway account created the way the owner now creates them (and deleted afterwards — the instance is back to its five real accounts): registration 403 "Only superusers can perform this action", with and without the old invite header template route 401 anonymously session survives a full page reload; the password does NOT, so the sources section re-asks for it — the intended split prefs one row, updatedAt distinct from the server cursor, and all 13 fields present including the three the page does not render, so the merge did not clobber what the app writes sources (BYOC) the browser reached the addon's /encode cross-origin, minted the prehraj.to token, built TorBox's config locally, encrypted and saved; the stored blob contains no plaintext and carries kdf=pbkdf2-sha256-210000 downloads all three platforms resolve to 1.1.6 · clean with a file token Checked rather than assumed: the Czech URL is stored with /manifest.json (matching the existing template) and the app strips it on both read and write via _stripManifest, so minting it that way is correct and not a latent 404.
2026-08-07 00:10:48 +00:00
// One page now does what three did: `index.html` is sign-in, profiles,
Accounts are owner-created, and one page manages the whole account Registration is closed. The instance was gated by a single shared invite code in AMBER_INVITE_CODE -- a bearer secret, one string for everyone, forever, no record of who used it, and known to every family member who ever onboarded, so it leaked by design. An email whitelist was the obvious replacement and does not survive contact with the facts: it binds to a person only if the address is verified, and all five accounts on this instance are `verified = false`. For a household this size the owner creating each account in the admin UI is stronger than either and has nothing to leak. `users.createRule` is superuser-only now, and the hook refuses with a sentence a family member can read rather than a bare 403. The family code survives with a narrower job. It is no longer a key to the front door; it is what unlocks the *shared family credentials* during setup. Close friends get accounts too and bring their own (BYOC), so "has an account" must not mean "may have the family's sources" -- the template route now wants auth AND the code, keeping its rate limiter because the code is still a shared secret. pb_public/account.html replaces index/settings/onboarding with one page behind a persisted session: profiles (name, child flag, age ceiling, PIN), the 13 synced playback preferences per profile, sources, devices, downloads, account. The token persists in localStorage; the password never does, and anything needing it asks in the moment -- that is the honest split between "stay signed in" and "hold the key to your credentials in a browser tab". Sources can now be set up from your own service credentials. The page posts them straight to the addon's own /encode and builds TorBox's config token locally -- both addons send permissive CORS, so the credentials go from the family member's browser to the addon and never touch our server. prehraj.to is required because the addon's /encode returns 400 without it and decodeConfig rejects such a token, so "webshare only" is not currently possible; the copy says so rather than pretending. TorBox is optional and the copy is emphatic about what it is for. Two hazards handled rather than hoped over. Changing the password re-keys the addon vault, and the re-encrypt happens BEFORE the password PATCH: if it were after, a failure would leave a blob no device can read. And prefs writes carry `updatedAt`, the clock the app's LWW actually compares -- the server's `updated` is only the pull cursor -- and merge into the existing data rather than replacing it, because the app writes fields this page does not show yet. Also: `users.ratingDefault`, so the account-wide age ceiling stops being the one half of a parental control that does not sync. The app side ships with the next release. The other three device-local settings stay local on purpose, and `adult_enabled` most deliberately of all. Plan and reasoning: docs/plan-accounts-and-web.md in amber-app.
2026-08-06 23:54:25 +00:00
// playback preferences, sources, devices and downloads behind a persisted
// session. `/settings` keeps working because it is in people's muscle memory and
// in older messages — it lands on the sources section of the new page.
//
// `/tv` is the short form of `/get/tv`, because that URL gets typed on a
// television remote inside the Downloader app, one character at a time.
The unified page becomes the site; retire the two it replaced index.html was the old landing page with a signup form, and that form can now only fail — registration is owner-only. A dead button is worse than no button, so the unified account page takes its place rather than sitting beside it. settings.html goes too: it is a strict subset of the sources section, and onboarding.html was the signup wizard. Verified against the live backend before the swap, signed in as a throwaway account created the way the owner now creates them (and deleted afterwards — the instance is back to its five real accounts): registration 403 "Only superusers can perform this action", with and without the old invite header template route 401 anonymously session survives a full page reload; the password does NOT, so the sources section re-asks for it — the intended split prefs one row, updatedAt distinct from the server cursor, and all 13 fields present including the three the page does not render, so the merge did not clobber what the app writes sources (BYOC) the browser reached the addon's /encode cross-origin, minted the prehraj.to token, built TorBox's config locally, encrypted and saved; the stored blob contains no plaintext and carries kdf=pbkdf2-sha256-210000 downloads all three platforms resolve to 1.1.6 · clean with a file token Checked rather than assumed: the Czech URL is stored with /manifest.json (matching the existing template) and the app strips it on both read and write via _stripManifest, so minting it that way is correct and not a latent 404.
2026-08-07 00:10:48 +00:00
//
// `onboarding.html` and `settings.html` are gone from the repo but NOT from the
// server: the deploy copies files into `pb_public` and never deletes the ones a
// commit removed, so both stale pages kept answering 200 after the swap. The old
// wizard still showed a signup form that can now only fail, which is worse than
// no page at all. Explicit routes shadow the leftovers — and keep working as
// bookmarks even once someone clears the directory by hand.
Accounts are owner-created, and one page manages the whole account Registration is closed. The instance was gated by a single shared invite code in AMBER_INVITE_CODE -- a bearer secret, one string for everyone, forever, no record of who used it, and known to every family member who ever onboarded, so it leaked by design. An email whitelist was the obvious replacement and does not survive contact with the facts: it binds to a person only if the address is verified, and all five accounts on this instance are `verified = false`. For a household this size the owner creating each account in the admin UI is stronger than either and has nothing to leak. `users.createRule` is superuser-only now, and the hook refuses with a sentence a family member can read rather than a bare 403. The family code survives with a narrower job. It is no longer a key to the front door; it is what unlocks the *shared family credentials* during setup. Close friends get accounts too and bring their own (BYOC), so "has an account" must not mean "may have the family's sources" -- the template route now wants auth AND the code, keeping its rate limiter because the code is still a shared secret. pb_public/account.html replaces index/settings/onboarding with one page behind a persisted session: profiles (name, child flag, age ceiling, PIN), the 13 synced playback preferences per profile, sources, devices, downloads, account. The token persists in localStorage; the password never does, and anything needing it asks in the moment -- that is the honest split between "stay signed in" and "hold the key to your credentials in a browser tab". Sources can now be set up from your own service credentials. The page posts them straight to the addon's own /encode and builds TorBox's config token locally -- both addons send permissive CORS, so the credentials go from the family member's browser to the addon and never touch our server. prehraj.to is required because the addon's /encode returns 400 without it and decodeConfig rejects such a token, so "webshare only" is not currently possible; the copy says so rather than pretending. TorBox is optional and the copy is emphatic about what it is for. Two hazards handled rather than hoped over. Changing the password re-keys the addon vault, and the re-encrypt happens BEFORE the password PATCH: if it were after, a failure would leave a blob no device can read. And prefs writes carry `updatedAt`, the clock the app's LWW actually compares -- the server's `updated` is only the pull cursor -- and merge into the existing data rather than replacing it, because the app writes fields this page does not show yet. Also: `users.ratingDefault`, so the account-wide age ceiling stops being the one half of a parental control that does not sync. The app side ships with the next release. The other three device-local settings stay local on purpose, and `adult_enabled` most deliberately of all. Plan and reasoning: docs/plan-accounts-and-web.md in amber-app.
2026-08-06 23:54:25 +00:00
The unified page becomes the site; retire the two it replaced index.html was the old landing page with a signup form, and that form can now only fail — registration is owner-only. A dead button is worse than no button, so the unified account page takes its place rather than sitting beside it. settings.html goes too: it is a strict subset of the sources section, and onboarding.html was the signup wizard. Verified against the live backend before the swap, signed in as a throwaway account created the way the owner now creates them (and deleted afterwards — the instance is back to its five real accounts): registration 403 "Only superusers can perform this action", with and without the old invite header template route 401 anonymously session survives a full page reload; the password does NOT, so the sources section re-asks for it — the intended split prefs one row, updatedAt distinct from the server cursor, and all 13 fields present including the three the page does not render, so the merge did not clobber what the app writes sources (BYOC) the browser reached the addon's /encode cross-origin, minted the prehraj.to token, built TorBox's config locally, encrypted and saved; the stored blob contains no plaintext and carries kdf=pbkdf2-sha256-210000 downloads all three platforms resolve to 1.1.6 · clean with a file token Checked rather than assumed: the Czech URL is stored with /manifest.json (matching the existing template) and the app strips it on both read and write via _stripManifest, so minting it that way is correct and not a latent 404.
2026-08-07 00:10:48 +00:00
routerAdd("GET", "/account", (e) => e.redirect(302, "/"))
Accounts are owner-created, and one page manages the whole account Registration is closed. The instance was gated by a single shared invite code in AMBER_INVITE_CODE -- a bearer secret, one string for everyone, forever, no record of who used it, and known to every family member who ever onboarded, so it leaked by design. An email whitelist was the obvious replacement and does not survive contact with the facts: it binds to a person only if the address is verified, and all five accounts on this instance are `verified = false`. For a household this size the owner creating each account in the admin UI is stronger than either and has nothing to leak. `users.createRule` is superuser-only now, and the hook refuses with a sentence a family member can read rather than a bare 403. The family code survives with a narrower job. It is no longer a key to the front door; it is what unlocks the *shared family credentials* during setup. Close friends get accounts too and bring their own (BYOC), so "has an account" must not mean "may have the family's sources" -- the template route now wants auth AND the code, keeping its rate limiter because the code is still a shared secret. pb_public/account.html replaces index/settings/onboarding with one page behind a persisted session: profiles (name, child flag, age ceiling, PIN), the 13 synced playback preferences per profile, sources, devices, downloads, account. The token persists in localStorage; the password never does, and anything needing it asks in the moment -- that is the honest split between "stay signed in" and "hold the key to your credentials in a browser tab". Sources can now be set up from your own service credentials. The page posts them straight to the addon's own /encode and builds TorBox's config token locally -- both addons send permissive CORS, so the credentials go from the family member's browser to the addon and never touch our server. prehraj.to is required because the addon's /encode returns 400 without it and decodeConfig rejects such a token, so "webshare only" is not currently possible; the copy says so rather than pretending. TorBox is optional and the copy is emphatic about what it is for. Two hazards handled rather than hoped over. Changing the password re-keys the addon vault, and the re-encrypt happens BEFORE the password PATCH: if it were after, a failure would leave a blob no device can read. And prefs writes carry `updatedAt`, the clock the app's LWW actually compares -- the server's `updated` is only the pull cursor -- and merge into the existing data rather than replacing it, because the app writes fields this page does not show yet. Also: `users.ratingDefault`, so the account-wide age ceiling stops being the one half of a parental control that does not sync. The app side ships with the next release. The other three device-local settings stay local on purpose, and `adult_enabled` most deliberately of all. Plan and reasoning: docs/plan-accounts-and-web.md in amber-app.
2026-08-06 23:54:25 +00:00
// Deep-link straight to sources; the page reads the hash to pick its tab.
The unified page becomes the site; retire the two it replaced index.html was the old landing page with a signup form, and that form can now only fail — registration is owner-only. A dead button is worse than no button, so the unified account page takes its place rather than sitting beside it. settings.html goes too: it is a strict subset of the sources section, and onboarding.html was the signup wizard. Verified against the live backend before the swap, signed in as a throwaway account created the way the owner now creates them (and deleted afterwards — the instance is back to its five real accounts): registration 403 "Only superusers can perform this action", with and without the old invite header template route 401 anonymously session survives a full page reload; the password does NOT, so the sources section re-asks for it — the intended split prefs one row, updatedAt distinct from the server cursor, and all 13 fields present including the three the page does not render, so the merge did not clobber what the app writes sources (BYOC) the browser reached the addon's /encode cross-origin, minted the prehraj.to token, built TorBox's config locally, encrypted and saved; the stored blob contains no plaintext and carries kdf=pbkdf2-sha256-210000 downloads all three platforms resolve to 1.1.6 · clean with a file token Checked rather than assumed: the Czech URL is stored with /manifest.json (matching the existing template) and the app strips it on both read and write via _stripManifest, so minting it that way is correct and not a latent 404.
2026-08-07 00:10:48 +00:00
routerAdd("GET", "/settings", (e) => e.redirect(302, "/#addons"))
Accounts are owner-created, and one page manages the whole account Registration is closed. The instance was gated by a single shared invite code in AMBER_INVITE_CODE -- a bearer secret, one string for everyone, forever, no record of who used it, and known to every family member who ever onboarded, so it leaked by design. An email whitelist was the obvious replacement and does not survive contact with the facts: it binds to a person only if the address is verified, and all five accounts on this instance are `verified = false`. For a household this size the owner creating each account in the admin UI is stronger than either and has nothing to leak. `users.createRule` is superuser-only now, and the hook refuses with a sentence a family member can read rather than a bare 403. The family code survives with a narrower job. It is no longer a key to the front door; it is what unlocks the *shared family credentials* during setup. Close friends get accounts too and bring their own (BYOC), so "has an account" must not mean "may have the family's sources" -- the template route now wants auth AND the code, keeping its rate limiter because the code is still a shared secret. pb_public/account.html replaces index/settings/onboarding with one page behind a persisted session: profiles (name, child flag, age ceiling, PIN), the 13 synced playback preferences per profile, sources, devices, downloads, account. The token persists in localStorage; the password never does, and anything needing it asks in the moment -- that is the honest split between "stay signed in" and "hold the key to your credentials in a browser tab". Sources can now be set up from your own service credentials. The page posts them straight to the addon's own /encode and builds TorBox's config token locally -- both addons send permissive CORS, so the credentials go from the family member's browser to the addon and never touch our server. prehraj.to is required because the addon's /encode returns 400 without it and decodeConfig rejects such a token, so "webshare only" is not currently possible; the copy says so rather than pretending. TorBox is optional and the copy is emphatic about what it is for. Two hazards handled rather than hoped over. Changing the password re-keys the addon vault, and the re-encrypt happens BEFORE the password PATCH: if it were after, a failure would leave a blob no device can read. And prefs writes carry `updatedAt`, the clock the app's LWW actually compares -- the server's `updated` is only the pull cursor -- and merge into the existing data rather than replacing it, because the app writes fields this page does not show yet. Also: `users.ratingDefault`, so the account-wide age ceiling stops being the one half of a parental control that does not sync. The app side ships with the next release. The other three device-local settings stay local on purpose, and `adult_enabled` most deliberately of all. Plan and reasoning: docs/plan-accounts-and-web.md in amber-app.
2026-08-06 23:54:25 +00:00
// Typing this on a remote is the actual cost being optimised.
routerAdd("GET", "/tv", (e) => e.redirect(302, "/get/tv"))
// Shadow the stale files the deploy left behind (see above).
routerAdd("GET", "/settings.html", (e) => e.redirect(302, "/#addons"))
routerAdd("GET", "/onboarding.html", (e) => e.redirect(302, "/"))