Commit Graph

9 Commits

Author SHA1 Message Date
Claude fd12f11cd7 Fix ADMIN_PANEL_ENABLED never being read from frontend/.env
process.env.ADMIN_PANEL_ENABLED was always undefined in the running
SvelteKit server process — Vite only injects VITE_-prefixed vars into
process.env for server-side code; plain vars in frontend/.env never reached
it, so the admin panel stayed disabled (cog hidden, /admin/* 404s) no matter
what the .env file said. Switched to SvelteKit's own $env/dynamic/private,
which reads it correctly in dev, preview, and adapter-based deployments.

Reproduced and verified the fix against a real frontend/.env file (not an
inline shell var, which is what masked this the first time).
2026-07-21 20:22:09 +00:00
Claude 619515db96 Replace admin username/password with a per-launch API key, and disable the admin panel by default
Two hardening changes beyond just a password:

- The admin panel no longer uses stored credentials at all. The backend
  generates a random API key on every startup and prints it to its own
  console (never through the DB-backed logger, since that's only reachable
  from inside the panel this key protects). Every /api/admin/* request must
  carry it as an X-Api-Key header, checked with a timing-safe comparison on
  every call — there's no session to create or steal, and restarting the
  backend invalidates the previous key immediately. The old admin_users and
  sessions tables, scrypt password hashing, and cookie-based session plumbing
  are removed entirely (dropped via migration for existing installs, not
  left behind unused). The login page keeps its existing layout but now asks
  for this key and explains where to find it, storing it in the browser's
  localStorage rather than relying on a server session.

- The admin panel (the masthead's cog icon and the /admin/* pages
  themselves) is now disabled by default on every deployment, gated by a new
  frontend-only ADMIN_PANEL_ENABLED env var. This is a separate, UI-only
  visibility control — the API key above is what actually protects the
  backend regardless of this flag.
2026-07-21 20:03:48 +00:00
Claude 64ef671c5e Add a per-source "Push to Top Stories?" opt-in, off by default
Every ingested article used to show up on the homepage regardless of its
source, which meant a handful of high-volume feeds could flood "Top Stories."
Sources now default to not appearing there; a source has to explicitly opt
in via a new checkbox (also toggleable inline with a star icon) for its
articles to show up on the homepage feed. An article shows there if any of
its contributing sources opted in — merged/clustered stories aren't held to
requiring all sources to agree. Category pages, Local, tags, and events are
unaffected; this only gates the bare, no-filter homepage query.

Schema: sources.push_to_top_stories and merged_articles.top_stories, both
backfilled for existing databases via ALTER TABLE.
2026-07-21 19:13:36 +00:00
Claude d469f00292 Resolve YouTube @handles and vanity URLs to a channel ID automatically
YouTube's public Atom feed only accepts a channel_id (or the legacy user
param) — it has no equivalent for the newer @handle format, so pasting a
handle URL straight into the source's url field wouldn't have worked. The
adapter now accepts a bare channel ID, a /channel/UC... URL, an @handle URL,
or a bare handle/username, resolving whichever was given to the actual
channel ID by reading it off the channel page when needed.
2026-07-21 18:54:43 +00:00
Claude e204c70e00 Fix source deletion, add content clearing, multi-category/editable sources, News category, wider layout, and a YouTube source module
- Fix "Body cannot be empty" error on DELETE by making the JSON content-type
  parser tolerate empty bodies, and by only sending Content-Type from the
  frontend when a request actually has one.
- Deleting a source now cascades: raw content items and any article composed
  entirely from that source are removed too, plus their media.
- Add admin endpoints/UI to clear all articles, all media, or a single
  source's content without deleting the source, so things can be repopulated
  fresh.
- Sources can now be assigned multiple categories via checkboxes (instead of
  free text) and edited in place, not just added/deleted.
- Add a "News" default category (seeded fresh, backfilled on existing DBs) so
  general news sources have a real home instead of the pseudo-category "Top
  stories", which is just the homepage's all-categories chronological view.
- Widen the site's content column 15% (1080px -> 1242px).
- Add YouTube as its own source type/ingestion module: pulls a channel's
  public Atom feed, and each video always publishes directly as its own
  article (title, embedded video, publish date, description) rather than
  going through the cross-source clustering/synthesis pipeline.
2026-07-21 18:09:20 +00:00
Salastil b742320108 Many Improvements 2026-07-21 12:51:31 -04:00
Salastil d16cd508b5 Log pending but not eligible items 2026-07-17 10:49:09 -04:00
Salastil a5b108d9fa Initial Commit 2026-07-17 10:33:20 -04:00
Salastil a5d35515f2 Initial commit 2026-07-17 10:15:17 -04:00