Commit Graph
146 Commits
Author SHA1 Message Date
kfj001 9594187fc1 Merge branch 'main' into bugfix/spider-ssr-gate
CI Pipeline / backend-test (push) Successful in 18m31s
CI Pipeline / frontend-test (push) Successful in 26s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 19m12s
CI Pipeline / frontend-build (pull_request) Successful in 33s
CI Pipeline / quality-gate (pull_request) Successful in 4s
CI Pipeline / frontend-test (pull_request) Successful in 25s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 3s
2026-08-09 12:05:40 -07:00
robot cd6dc96214 style(admin): restyle admin user form to match global design system
CI Pipeline / backend-test (push) Successful in 9m51s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 14m47s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / quality-gate (push) Successful in 3s
- Replace hardcoded CSS custom properties with SCSS variables
- Add .form-error/.form-success alert banners
- Add .admin-form styles: .form-row grid, .form-group labels/inputs,
  focus rings, is-invalid states, placeholder styling
- Style .add-admin-section heading with $font-heading, divider underline
- Use button-style mixin for .btn-save (was unstyled before)
- Add .form-actions bar layout
- Add responsive breakpoint: stack form rows on mobile (<768px)
- Consistent spacing via $spacing-* tokens throughout
- Consistent border-radius via $radius-* tokens throughout
2026-08-09 08:44:46 +00:00
robot db402f8192 Merge pull request 'fix(ssr): remove color defaults from :root in _themes.scss' (#12) from fix/remove-root-color-defaults into main
CI Pipeline / backend-test (push) Successful in 9m52s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
2026-08-09 00:17:03 -07:00
robot bb45e8d6ae fix(ssr): inject derived gradients and migrate component SCSS to CSS vars
CI Pipeline / backend-test (push) Successful in 9m52s
CI Pipeline / frontend-build (pull_request) Successful in 31s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 9m59s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / quality-gate (push) Successful in 2s
The server-injected inline <style> was missing the three derived gradient
variables (--sky-gradient, --sunset-gradient, --mixed-gradient) that
components reference via var(). Without them, components fell back to
SCSS build-time defaults on the first paint, causing a visible flash
before ThemeService.apply() corrected the values.

Additionally, migrate three component SCSS files from hardcoded SCSS
color variables to CSS custom properties so the first paint uses the
server-injected runtime colors:
- login.component.scss: backgrounds, text colors, borders, focus rings
- events.component.scss: event date card background gradient
- about.component.scss: donation CTA background gradient
2026-08-09 06:49:24 +00:00
robot e78a725839 fix(ssr): remove color defaults from :root in _themes.scss
CI Pipeline / backend-test (push) Successful in 9m55s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 1s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 9m56s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / backend-lint (pull_request) Successful in 8s
The SSR server injects fresh color CSS custom properties as an inline
<style> block before </head>, but _themes.scss also set hardcoded
color defaults on :root at build time. The two :root blocks compete
in the cascade — the compiled stylesheet (loaded after the inline
<style>) overwrites the server-injected colors, causing a visible
flash where wrong colors render briefly before ThemeService.apply()
corrects them.

Removing the color properties from :root in _themes.scss makes the
server-injected <style> the sole source of truth for colors. Non-color
properties (fonts, radii, shadows) remain as build-time defaults since
they don't change between deploys.
2026-08-09 05:55:28 +00:00
kfj001 849856aecb Merge pull request 'feat(ssr): spider-only SSR serve CSR shell for regular users' (#11) from bugfix/spider-ssr-gate into main
CI Pipeline / backend-test (push) Successful in 20m11s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / backend-lint (push) Successful in 10s
Reviewed-on: http://192.168.1.74:30008/kfj001/kmtnflower/pulls/11
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-08-08 21:11:31 -07:00
robot 1b387700d3 fix(ssr): prefer index.csr.html over prerendered index.html for CSR users
CI Pipeline / backend-test (push) Successful in 20m24s
CI Pipeline / frontend-test (push) Successful in 25s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / backend-test (pull_request) Successful in 20m14s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (pull_request) Successful in 4s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / quality-gate (push) Successful in 3s
The browserIndexPath() function was resolving index.html first, which is
the prerendered output with stale build-time content baked into <app-root>.
Even with window.__STATION_CONFIG injection, the prerendered innerHTML
still shows old content on the first paint until Angular re-renders.

By preferring index.csr.html (the pure CSR shell with empty <app-root>),
regular users get a clean first paint — no stale prerendered DOM to flash.
2026-08-09 01:13:50 +00:00
robot b20fefc432 chore: add placeholder angular-app-engine-manifest.mjs for tests
CI Pipeline / backend-test (push) Successful in 19m58s
CI Pipeline / frontend-test (push) Successful in 25s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 20m7s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / frontend-test (pull_request) Successful in 25s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (pull_request) Successful in 3s
2026-08-09 00:56:29 +00:00
robot e44f27d1cd fix(ssr): inject fresh station config into CSR shell to eliminate flash
CI Pipeline / backend-test (push) Successful in 18m48s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 4s
CI Pipeline / backend-test (pull_request) Successful in 20m7s
The CSR shell previously rendered with hardcoded DEFAULT_CONFIG values,
then the client-side API call replaced them 50-500ms later, causing a
visible 'flash of stale content' (wrong colors, wrong station name).

Fix: SSR server fetches live station config from the backend API before
serving the CSR shell, injecting it as:
- Inline <script> seeding window.__STATION_CONFIG (client reads sync)
- Inline <style> setting CSS custom properties (correct colors first paint)

Also adds no-cache Cache-Control headers on HTML page responses to
prevent browsers from caching stale content.

StationConfigService now reads window.__STATION_CONFIG on construction
instead of always starting with DEFAULT_CONFIG.
2026-08-09 00:53:50 +00:00
robot 9561339cf5 fix(ssr): resolve index.html from production path for CSR shell
CI Pipeline / backend-test (push) Successful in 20m50s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / frontend-test (pull_request) Successful in 45s
CI Pipeline / quality-gate (pull_request) Successful in 4s
CI Pipeline / backend-lint (push) Successful in 15s
CI Pipeline / frontend-build (pull_request) Successful in 39s
CI Pipeline / backend-lint (pull_request) Successful in 16s
CI Pipeline / quality-gate (push) Successful in 5s
CI Pipeline / backend-test (pull_request) Successful in 37m43s
The CSR shell path was hardcoded to a local dev directory
(app/server/browser/index.html) which does not exist in the
Docker container. Browser files are copied to /usr/share/nginx/html
by the Dockerfile. browserIndexPath() now checks both production
and development paths, falling back to the first that exists.
2026-08-08 20:25:29 +00:00
Hermes Robot c5d80f4caa feat(ssr): spider-only SSR — serve CSR shell for regular users
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / frontend-build (pull_request) Successful in 31s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / backend-test (push) Successful in 12m23s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 17m40s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (pull_request) Successful in 10s
Add spider detection gate to SSR server so that search engine
crawlers get full server-side rendering with fresh API data,
while regular users receive the static CSR app shell from disk.

- backend/app/spider_detection.py: Python spider detection module
- spider_detection.ts: TypeScript mirror of spider detection patterns
- server.ts: SSR gate — isSpider() check before dispatching to
  Angular SSR engine; non-spiders get static index.html
- server.spec.ts: Added spider detection unit tests
- tsconfig.app.json: Include spider_detection.ts in compilation
- backend/app/models.py: Fix SQLAlchemy FK resolution (user_models import)
2026-08-08 17:48:37 +00:00
kfj001 96011ef200 Merge pull request 'fix(ssr): fetch station config during server-side rendering' (#10) from bugfix/ssr-fetch-config-on-server into main
CI Pipeline / frontend-test (push) Successful in 22s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / backend-test (push) Successful in 5m3s
CI Pipeline / backend-lint (push) Successful in 9s
Reviewed-on: http://192.168.1.74:30008/kfj001/kmtnflower/pulls/10
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-08-08 08:03:52 -07:00
robot 54bd869042 fix(ssr): fetch station config during server-side rendering
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / backend-test (pull_request) Successful in 5m14s
CI Pipeline / frontend-build (pull_request) Successful in 29s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / backend-lint (push) Successful in 7s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (pull_request) Successful in 22s
CI Pipeline / backend-test (push) Successful in 5m18s
The isPlatformBrowser guard in App.ngOnInit() was blocking
stationConfig.load() on the server, so SSR always rendered with
DEFAULT_CONFIG (hardcoded colors, assets, content).

Two-part fix:
- main.server.ts: Add APP_INITIALIZER that blocks server rendering
  until station config is fetched from the API. Only runs on the
  server — is a no-op in the browser to avoid blocking the client.
- app.ts: Move stationConfig.load() before the isPlatformBrowser
  guard so ngOnInit also calls it on the server as a safety net.

This ensures SSR HTML contains real DB values (colors, hero icon,
station name, etc.) instead of build-time defaults — eliminating
the stale-asset flash reported by users.
2026-08-08 12:10:05 +00:00
kfj001 5b1006d321 Merge pull request 'fix: ensure SSR always fetches fresh assets from database' (#9) from bugfix/ssr-asset-freshness into main
CI Pipeline / backend-test (push) Successful in 10m25s
CI Pipeline / frontend-test (push) Successful in 25s
CI Pipeline / backend-lint (push) Successful in 14s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-build (push) Successful in 30s
Reviewed-on: http://192.168.1.74:30008/kfj001/kmtnflower/pulls/9
2026-08-07 16:43:16 -07:00
DevOps QA 1db84a384f fix(ssr): manually register engine manifest to fix 404 on all routes
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / backend-test (pull_request) Successful in 9m38s
CI Pipeline / frontend-build (pull_request) Successful in 31s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / backend-test (push) Successful in 9m49s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / quality-gate (pull_request) Successful in 3s
The Angular 21 builder generates angular-app-engine-manifest.mjs at build
time but does NOT inject the import into server.mjs. Without manually
importing and registering the manifest via ɵsetAngularAppEngineManifest,
AngularNodeAppEngine fails with 'Angular app engine manifest is not set'
and every request returns 404.

This matches the exact issue reported in PR #14 on staging where nginx
starts successfully but returns 'Not Found' for all paths.
2026-08-07 19:02:10 +00:00
DevOps QA 89cac854ae fix: use full nginx.conf instead of conf.d snippet to avoid directive nesting error
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / backend-test (pull_request) Successful in 4m48s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-test (push) Successful in 5m1s
Alpine nginx includes conf.d/*.conf inside the http{} block, but our
template contained map/log_format directives and a server{} block that
conflicted with the include structure. Switched to a complete standalone
nginx.conf with proper user/events/http nesting.
2026-08-07 14:47:48 +00:00
DevOps QA 8f0adf9299 fix: add gettext to Alpine image for envsubst in entrypoint
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (pull_request) Successful in 29s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 12s
CI Pipeline / backend-test (pull_request) Successful in 5m48s
CI Pipeline / frontend-test (pull_request) Successful in 22s
CI Pipeline / backend-test (push) Successful in 5m46s
CI Pipeline / backend-lint (pull_request) Successful in 8s
2026-08-07 06:38:28 +00:00
DevOps QA c4e852684e fix: use bracket notation for process.env access (TS5.9 strict)
CI Pipeline / backend-test (push) Successful in 5m31s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 5m42s
CI Pipeline / frontend-build (pull_request) Successful in 29s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / backend-lint (pull_request) Successful in 9s
- src/main.server.ts: process.env['SSR_API_BASE_URL'] instead of
  process.env.SSR_API_BASE_URL to satisfy TS4111 index signature rule.
- server.spec.ts: update corresponding test assertions to match new syntax.
2026-08-07 06:03:02 +00:00
robot 328d067d97 fix: ensure SSR always fetches fresh assets from database
CI Pipeline / backend-test (push) Successful in 5m31s
CI Pipeline / frontend-test (push) Successful in 23s
CI Pipeline / frontend-build (push) Failing after 27s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / backend-test (pull_request) Successful in 5m24s
CI Pipeline / frontend-build (pull_request) Failing after 27s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (pull_request) Successful in 23s
Root cause: Dockerfile copied only browser/ to nginx; server/server.mjs
was built but never deployed or executed. Prerender baked DEFAULT_CONFIG
into static HTML with no API at build time, causing stale content on
every initial paint.

Changes:
- Dockerfile: serve stage uses node:22-alpine, installs nginx via apk,
  copies both browser/ and server/ bundles from build stage
- docker-entrypoint.sh: starts Angular SSR Node process on port 4200,
  waits for readiness, exports SSR_API_BASE_URL, then exec nginx
- nginx.conf.template: adds upstream ssr_backend block (127.0.0.1:4200),
  proxies / to SSR backend, static assets served directly with 1y cache
- app.server.routes.ts: all routes switched from RenderMode.Prerender
  to RenderMode.Server for zero stale content
- server.ts: removed dead prerender code (PRERENDERED_ROUTES,
  isPrerenderedRoute, allowStaticRouteRender)
- main.server.ts: injects SSR-aware apiBaseUrl via APP_CONFIG
  (reads SSR_API_BASE_URL env var)
- server.spec.ts: updated 5 test suites + added 5 new suites (31 tests)
  covering server.ts cleanup, RenderMode.Server, entrypoint lifecycle,
  Dockerfile build, nginx upstream proxy, and API URL injection
- vitest.config.ts: added server.spec.ts to include pattern

Request flow: Browser -> nginx (port 80) -> SSR server (port 4200)
-> Angular SSR -> HttpClient -> localhost:80/api/station-config
-> nginx proxies to api:8000 -> fresh data rendered in HTML
2026-08-07 05:33:40 +00:00
kfj001 2ece38e52d Merge pull request 'feat(seo): Phase 3 — preload LCP image and reduce APP_INITIALIZER blocking' (#8) from feat/seo-phase3-optimizations into main
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-test (push) Successful in 9m10s
CI Pipeline / quality-gate (push) Successful in 3s
Reviewed-on: http://192.168.1.74:30008/kfj001/kmtnflower/pulls/8
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-08-06 21:08:51 -07:00
kfj001 c4e2dc5b9f Merge pull request 'feat: add SSR and prerendering support' (#7) from feat/ssr-prerendering into main
CI Pipeline / backend-test (push) Successful in 9m9s
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / frontend-test (push) Successful in 26s
CI Pipeline / frontend-build (push) Successful in 32s
Reviewed-on: kfj001/kmtnflower#7
2026-08-06 21:06:48 -07:00
robot e2356954ef feat(seo): Phase 3 — preload LCP image and reduce APP_INITIALIZER blocking
CI Pipeline / backend-test (push) Successful in 10m9s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / frontend-test (pull_request) Successful in 25s
CI Pipeline / frontend-build (pull_request) Successful in 41s
CI Pipeline / frontend-test (push) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / backend-test (pull_request) Successful in 10m30s
- Add <link rel="preload"> for hero background image (LCP element) in index.html
- Remove blocking APP_INITIALIZER for config.json and station config loading
- Config loads now fire asynchronously in App.ngOnInit() (browser only)
- SSR/prerender renders instantly with default config — no network wait
- Improves LCP, Time to First Byte, and initial paint for SEO
2026-08-06 21:29:30 +00:00
Hermes Agent a8b6e65831 fix(ssr): enable static route rendering and fix self-import in server.ts
CI Pipeline / backend-test (push) Successful in 9m12s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / backend-test (pull_request) Successful in 10m0s
- Add AngularAppEngine.ɵallowStaticRouteRender flag so the engine serves
  prerendered static HTML from disk instead of falling back to SSR
- Rename default export to named const (requestHandler) to avoid self-import
  loop when the module is the entry point
- Use requestHandler directly in the main-module listener instead of
  importing './server.js' which caused TS2307 resolution failure
2026-08-06 18:10:39 +00:00
Hermes Agent 63bc2fb22d fix(ssr): remove broken dynamic import and lazy-init engine
CI Pipeline / backend-test (pull_request) Successful in 5m24s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / backend-test (push) Successful in 5m39s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / frontend-test (pull_request) Successful in 22s
CI Pipeline / quality-gate (push) Successful in 2s
- Replace await import('./main.server.js') with lazy getEngine()
  (original path was wrong — main.server.ts lives in src/, not root)
- AngularNodeAppEngine handles manifest registration internally;
  no manual bootstrap import needed
- Add npm scripts: build:ssr, build:prerender, serve:ssr:radio-station
- Add PRERENDER.md build documentation

Verified: clean build prerenders all 5 routes, 54 tests pass
2026-08-06 16:38:27 +00:00
Hermes Agent e95a158d57 fix(ssr): apply theme CSS vars during SSR and enable client hydration
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / backend-lint (push) Successful in 14s
CI Pipeline / backend-test (pull_request) Successful in 9m17s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-test (push) Successful in 9m4s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (pull_request) Successful in 24s
- ThemeService: remove isPlatformBrowser guard so CSS custom properties
  are applied during SSR/prerender — server-rendered HTML now carries the
  correct color scheme and gradients (fixes missing colors on test site).

- main.ts: add provideClientHydration() so Angular preserves the server-
  rendered DOM on the client instead of destroying and rebuilding it
  (fixes jarring full-page reload between static and dynamic versions).

- Tests: update theme.service.spec.ts to match new behaviour, expand
  coverage with gradient tests and empty-value handling (9 tests).
  Fix structured-data.component.spec.ts import paths. All 54 tests pass.
2026-08-06 03:24:57 +00:00
robot 2d26269c1a fix: align @angular/platform-server and @angular/ssr versions with Angular core
CI Pipeline / backend-test (push) Successful in 11m37s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-test (push) Successful in 37s
CI Pipeline / frontend-test (pull_request) Successful in 24s
CI Pipeline / frontend-build (pull_request) Successful in 33s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 11m59s
CI Pipeline / backend-lint (pull_request) Successful in 12s
The SSR packages were pinned to ^21.2.19 and ^21.2.20 which had
strict peer dependency requirements conflicting with @angular/common
at ^21.2.0. This caused npm ci (used in CI) to fail with peer
dependency resolution errors.

Aligned both packages to ^21.2.0 to match the rest of the Angular
core packages, and regenerated package-lock.json.
2026-08-05 22:11:05 +00:00
robot 0bde1cfc75 fix(tests): load JIT compiler before router imports in seo.service.spec
CI Pipeline / frontend-build (push) Failing after 10s
CI Pipeline / frontend-build (pull_request) Failing after 19s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-test (push) Failing after 14s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 11m56s
CI Pipeline / frontend-test (pull_request) Failing after 22s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / backend-test (push) Successful in 14m20s
The PlatformLocation static initializer in @angular/common/router
requires @angular/compiler to be loaded first. Move the compiler
import before any Angular router/common imports so the JIT facade
is available when the module loads.
2026-08-05 21:24:52 +00:00
robot a783626f34 feat: add SSR and prerendering support
CI Pipeline / backend-test (push) Successful in 10m24s
CI Pipeline / frontend-build (push) Failing after 23s
CI Pipeline / backend-lint (push) Successful in 12s
CI Pipeline / backend-test (pull_request) Successful in 10m45s
CI Pipeline / frontend-build (pull_request) Failing after 12s
CI Pipeline / backend-lint (pull_request) Successful in 13s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-test (push) Failing after 19s
CI Pipeline / frontend-test (pull_request) Failing after 20s
- Install @angular/ssr and @angular/platform-server (v21.2.x)
- Create server entry point (server.ts) and server bootstrap (main.server.ts)
- Add server routes config (app.server.routes.ts) with 5 prerendered routes:
  /, /about, /schedule, /events, /contact
- Update angular.json with server/ssr/prerender build configuration
- Add prerender-routes.txt for build-time static route generation
- Fix SSR compatibility:
  - App component: guard DOM access with isPlatformBrowser
  - StructuredDataComponent: skip head manipulation on server
  - ThemeService: skip CSS variable application on server
  - ShowService/EventService: return empty arrays on API failure
    (graceful fallback during prerender with no backend)
- Prerendered output verified: 5 static HTML routes in dist/
2026-08-05 20:22:43 +00:00
kfj001 fdbc1d78da Merge pull request 'feat(seo): Phase 2 — route-level metadata service and JSON-LD structured data' (#6) from feat/seo-phase2-metadata-structured-data into main
CI Pipeline / backend-test (push) Successful in 8m29s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 3s
Reviewed-on: http://192.168.1.74:30008/kfj001/kmtnflower/pulls/6
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-08-05 11:18:48 -07:00
robot c61162bff4 fix(seo): use bracket notation for Record index signature properties (TS4111)
CI Pipeline / backend-test (push) Successful in 5m10s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / quality-gate (push) Successful in 1s
CI Pipeline / backend-test (pull_request) Successful in 5m11s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / frontend-build (pull_request) Successful in 30s
2026-08-05 10:32:04 +00:00
robot 58267e8f59 feat(seo): Phase 2 — route-level metadata service and JSON-LD structured data
CI Pipeline / frontend-build (push) Failing after 29s
CI Pipeline / backend-test (push) Successful in 5m16s
CI Pipeline / backend-test (pull_request) Successful in 5m5s
CI Pipeline / frontend-build (pull_request) Failing after 29s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (pull_request) Successful in 27s
CI Pipeline / backend-lint (pull_request) Successful in 8s
Add SeoService for dynamic metadata management:
- Per-route document titles (e.g., 'KMountain Flower Radio — About')
- Meta descriptions from station config per route
- Canonical URLs, Open Graph tags, Twitter Card tags
- Input sanitization (HTML stripping, URL validation, attribute escaping)
- noindex robots directives for /login and /admin routes
- Centralized router watcher (zero per-component boilerplate)

Add StructuredDataService + StructuredDataComponent for JSON-LD:
- Organization schema (NCBOrganization) site-wide with contact info
- WebSite schema with publisher info
- WebPage schema per-route with breadcrumbs
- Dynamic Event schema on /events page
- RadioSeason schema on /schedule page

Wire up SeoService.watchRouter() in app-root ngOnInit.
Wire up StructuredDataComponent in app-root template.
Wire up EventsComponent and ScheduleComponent to feed data to
StructuredDataService for dynamic schema injection.

Includes full test suite for SeoService (22 tests).
2026-08-05 10:12:55 +00:00
kfj001 2716765223 Merge pull request 'Updates the generation means of robots.txt and sitemap.xml to be based off siteconfig's website param.' (#5) from bugfix/fix_API_robots_txt into main
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / backend-test (push) Successful in 7m40s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 29s
Reviewed-on: kfj001/kmtnflower#5
2026-08-04 22:15:22 -07:00
kfj001 4b123025e4 Updates the generation means of robots.txt and sitemap.xml to be based off siteconfig's website param.
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / backend-test (pull_request) Successful in 11m42s
CI Pipeline / frontend-test (pull_request) Successful in 31s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / backend-test (push) Successful in 10m0s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 12s
CI Pipeline / quality-gate (push) Successful in 3s
2026-08-04 20:33:25 -07:00
robot 55be42ed99 Merge pull request 'fix(seo): comprehensive SEO improvements per expert audit' (#4) from fix/seo-improvements into main
CI Pipeline / backend-test (push) Successful in 10m13s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 4s
2026-08-04 20:03:55 -07:00
kfj001 70bf1830b2 Merge branch 'main' into fix/seo-improvements
CI Pipeline / backend-test (push) Successful in 10m12s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / backend-test (pull_request) Successful in 10m11s
CI Pipeline / frontend-build (pull_request) Successful in 31s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 3s
2026-08-04 19:31:36 -07:00
Hermes Agent 58a25370b9 fix(seo): add admin UI for social media URLs
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / backend-test (pull_request) Successful in 10m43s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-test (push) Successful in 10m42s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (pull_request) Successful in 10s
CI Pipeline / quality-gate (pull_request) Successful in 4s
CI Pipeline / frontend-test (pull_request) Successful in 28s
Addresses reviewer feedback: social media fields (Facebook, Twitter/X, Instagram, YouTube) were added to the backend and frontend interfaces but had no admin UI for station owners to configure them.

Added a 'Social Media' section to the station config form with four URL inputs that persist via the existing station-config API.
2026-08-05 00:27:18 +00:00
Hermes Agent 3687e91f7f test(seo): add unit tests for sitemap export and API endpoints
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / backend-test (push) Successful in 10m37s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-test (pull_request) Successful in 10m26s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / frontend-build (pull_request) Successful in 31s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (pull_request) Successful in 2s
- test_sitemap_export.py: 19 tests covering SEOMAP_ROUTES constants,
  build_sitemap_xml() output structure, XML validity, trailing slash
  handling, and build_robots_txt() rules + sitemap reference
- test_sitemap_api.py: 7 tests covering /sitemap.xml and /robots.txt
  endpoint status codes, content types, content validation, and
  auth-gated /regenerate endpoint

Addresses reviewer REQUEST_CHANGES: 'This needs a unit test' and
'Unit test for this type too'
2026-08-04 22:01:22 +00:00
Hermes Robot 3dead2921a fix: switch sitemap/robots to dynamic generation + Nginx proxy
CI Pipeline / backend-test (pull_request) Successful in 8m56s
CI Pipeline / backend-lint (pull_request) Successful in 11s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-test (push) Successful in 8m58s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-test (pull_request) Successful in 28s
The reviewer noted the static sitemap.xml had a hardcoded domain in a
white-label app.  Instead of writing files to disk, we now serve
sitemap.xml and robots.txt as dynamic API endpoints that read
KMTN_WEBSITE_URL at runtime.

- Nginx proxies /sitemap.xml and /robots.txt to the backend API so
  search crawlers reach them at root paths.
- Removed the startup filesystem-write that would fail in Docker
  (backend can't write to Nginx container's static dir).
- Simplified sitemap_export.py to pure generation functions (no I/O).
- Added /api/sitemap/regenerate admin endpoint for consistency.
2026-08-04 20:06:00 +00:00
Hermes Robot 5484c761b4 fix: address reviewer feedback — dynamic sitemap/robots + wildcard CI branches
CI Pipeline / backend-test (push) Successful in 8m12s
CI Pipeline / frontend-build (push) Successful in 32s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 8m43s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-test (pull_request) Successful in 29s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 3s
- CI workflow: replace hardcoded branch list with ['**'] wildcard so
  pipeline runs on every branch push, not just 4 named branches.

- Sitemap/robots.txt: moved from static files to dynamic generation.
  New sitemap_export.py module (follows theme_export.py pattern) uses
  KMTN_WEBSITE_URL to generate tenant-aware sitemap.xml and robots.txt
  on app startup. Atomic writes to STATIC_FILES_DIR so Nginx serves them.

- Added /api/sitemap endpoint with /status (public) and /regenerate
  (admin-only) for runtime control.

Addresses reviewer comments on PR #4:
  1. 'more general way to ensure this runs on any branch on push'
  2. 'whitelabel app - this file may need to be dynamically generated'
2026-08-04 19:54:48 +00:00
Hermes Agent a7d994a122 chore: remove pytest artifacts from commit
CI Pipeline / backend-test (pull_request) Successful in 4m35s
CI Pipeline / frontend-test (pull_request) Successful in 27s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-test (push) Successful in 4m26s
CI Pipeline / frontend-build (push) Successful in 30s
2026-08-04 10:40:37 +00:00
Hermes Agent e3a742b708 fix(seo): add RouterLinkWithHref imports + default social media fields
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / backend-test (pull_request) Successful in 4m19s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / backend-test (push) Successful in 4m24s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / frontend-test (pull_request) Successful in 27s
CI Pipeline / frontend-build (pull_request) Successful in 30s
- Import RouterLinkWithHref in footer and events components (required for queryParams binding on <a>)
- Add social media fields to DEFAULT_CONFIG in station-config.service.ts
2026-08-04 10:32:47 +00:00
Hermes Agent bea1c97e88 fix(seo): comprehensive SEO improvements per expert audit
CI Pipeline / backend-test (pull_request) Successful in 4m20s
CI Pipeline / frontend-build (pull_request) Failing after 29s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 26s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 1s
CI Pipeline / frontend-test (pull_request) Successful in 27s
CI Pipeline / backend-test (push) Successful in 4m22s
CI Pipeline / frontend-build (push) Failing after 28s
- Add public/robots.txt (allow all, disallow /admin and /login, reference sitemap)
- Add public/sitemap.xml (5 public routes with change frequencies and priorities)
- Fix heading hierarchy: h2 -> h1 on about, schedule, events, contact pages
- Remove dead footer href="#" links:
  - Volunteer/Corporate Giving/Planned Giving -> routerLink to /contact with subject query param
  - Social media links -> conditional on config().social_* fields (hidden when empty)
- Fix logo ARIA: add aria-hidden="true" to decorative logo in about page divider
- Add social_facebook, social_twitter, social_instagram, social_youtube to:
  - StationConfig DB model + startup migration
  - StationConfigResponse and StationConfigUpdate schemas
  - Frontend StationConfig TypeScript interface
- Fix events "Learn More" dead link -> routerLink to /contact
2026-08-04 10:19:24 +00:00
kfj001 da02687260 Merge pull request 'fix(seo): use h1 as primary heading on about, schedule, events, and contact pages' (#3) from fix/seo-heading-hierarchy into main
CI Pipeline / backend-test (push) Successful in 4m24s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 27s
Reviewed-on: kfj001/kmtnflower#3
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-08-04 02:54:18 -07:00
robot b7e67fa244 fix(seo): use h1 as primary heading on about, schedule, events, and contact pages
CI Pipeline / backend-test (pull_request) Successful in 4m38s
CI Pipeline / frontend-test (pull_request) Successful in 31s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (pull_request) Successful in 1s
Each public page now has exactly one <h1> element for proper semantic
heading hierarchy:
- About: 'About [Station Name]' (h1), with subsection h2s for History/Team
- Schedule: 'Program Schedule' (h1)
- Events: 'Upcoming Events' (h1)
- Contact: 'Get in Touch' (h1)

The home page hero component already used <h1>, as did login and admin.
2026-08-04 09:26:48 +00:00
robot 65af2824a9 Merge pull request 'feat: admin user management API, multi-admin roles, and conditional bootstrap login' (#2) from feature/multi-admin-roles into main
CI Pipeline / backend-test (push) Successful in 8m7s
CI Pipeline / frontend-build (push) Successful in 33s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 2s
2026-07-31 14:24:16 -07:00
Hermes Agent 433bdef5d0 fix(auth): remove redundant startup bootstrap user creation
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / backend-test (push) Successful in 4m59s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / backend-test (pull_request) Successful in 5m2s
CI Pipeline / frontend-test (pull_request) Successful in 27s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 7s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 32s
The login endpoint already provisions the first admin user on demand
with a proper password_hash. The startup block in main.py created the
user without a password_hash, causing the login endpoint to see
has_admins=True and reject bootstrap credentials with 403.

Fix: remove the startup user-creation block so the login endpoint's
bootstrap path runs first and provisions the user correctly.
2026-07-31 18:28:26 +00:00
robot 85b3a4adc4 fix: add password_hash column migration and unit tests
CI Pipeline / backend-test (pull_request) Successful in 4m11s
CI Pipeline / backend-test (push) Successful in 4m5s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / frontend-test (pull_request) Successful in 26s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / backend-lint (push) Successful in 11s
- Add users.password_hash to _migrate_add_missing_columns pending list
- Skip tables that don't exist yet (prevents crash on fresh DB)
- Handle nullable columns (no DEFAULT clause) in ALTER TABLE
- Fix SQLite column type (use sql_type instead of hardcoded TEXT)
- Update migrate_roles.py to create schema columns/tables before seeding roles
- Add unit tests for migration: adds column, nullable, idempotent, no-op
2026-07-31 08:10:05 +00:00
robot 210da72e03 fix(frontend): add missing 'new' to EventEmitter instantiations
CI Pipeline / backend-test (push) Successful in 4m24s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / backend-test (pull_request) Successful in 4m18s
CI Pipeline / frontend-build (pull_request) Successful in 32s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / frontend-test (pull_request) Successful in 26s
CI Pipeline / quality-gate (pull_request) Successful in 2s
The Angular production build was failing with TS2348 errors because
EventEmitter<void>() was used without the 'new' keyword. This is not
valid TypeScript — EventEmitter is a constructor class, not a callable
value.

Fixes:
- src/app/admin/admin-users-tab.component.ts: added 'new' to saved and
  closed EventEmitter properties
2026-07-31 05:39:04 +00:00
Hermes Agent f8a04dba1a fix(ci): add frontend production build step to catch Angular build failures
CI Pipeline / backend-test (push) Successful in 7m58s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / backend-test (pull_request) Successful in 7m49s
CI Pipeline / frontend-test (pull_request) Successful in 29s
CI Pipeline / frontend-build (pull_request) Failing after 30s
CI Pipeline / quality-gate (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 3s
CI Pipeline / frontend-build (push) Failing after 31s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-lint (pull_request) Successful in 11s
- Add 'frontend-build' job that runs 'npx ng build --configuration production'
- Wire frontend-build into quality-gate dependencies
- Include feature/multi-admin-roles in CI push branches
- Build failures now fail the pipeline instead of being silently missed
2026-07-30 23:50:20 +00:00
Hermes Agent c74d905378 fix(deps): add bcrypt to requirements.txt for CI test collection
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 4m2s
CI Pipeline / frontend-test (pull_request) Successful in 30s
CI Pipeline / quality-gate (pull_request) Successful in 3s
2026-07-30 10:36:21 +00:00