fix(seo): comprehensive SEO improvements per expert audit #4

Merged
robot merged 8 commits from fix/seo-improvements into main 2026-08-04 20:03:55 -07:00
Member

Summary

Addresses all items from the SEO expert review:

Changes

  1. Add public/robots.txt — Allow all crawlers, disallow /admin and /login, reference sitemap URL.

  2. Add public/sitemap.xml — Includes all 5 public routes (/, /about, /schedule, /events, /contact) with appropriate change frequencies and priorities.

  3. Fix heading hierarchy — Changed <h2> to <h1> on About, Schedule, Events, and Contact page components for proper SEO heading structure.

  4. Remove/populate dead footer links — Eliminated all href="#" placeholder links:

    • Volunteer → routes to /contact?subject=volunteer
    • Corporate Giving → routes to /contact?subject=advertising
    • Planned Giving → routes to /contact?subject=general
    • Social media links → conditional rendering based on config().social_* fields (hidden when empty, no dead links)
    • Events "Learn More" → routes to /contact?subject=general
  5. Fix Logo ARIA — Added aria-hidden="true" to the decorative logo image in the About page divider section.

  6. Backend support for social media — Added social_facebook, social_twitter, social_instagram, social_youtube fields to:

    • StationConfig DB model + startup migration
    • StationConfigResponse and StationConfigUpdate Pydantic schemas
    • Frontend StationConfig TypeScript interface

Files Changed

  • .gitea/workflows/ci.yaml — Added branch trigger for fix/seo-improvements
  • backend/app/models.py — Added social media columns to StationConfig
  • backend/app/schemas.py — Added social media fields to response/update schemas
  • backend/app/main.py — Added social media columns to startup migration
  • public/robots.txt — New file
  • public/sitemap.xml — New file
  • src/app/about/about.component.html — h1 heading + aria-hidden on decorative logo
  • src/app/contact/contact.component.html — h1 heading
  • src/app/events/events.component.html — h1 heading + fixed dead "Learn More" link
  • src/app/footer/footer.component.html — Removed dead links, conditional social media
  • src/app/interfaces/station-config.ts — Added social media fields
  • src/app/schedule/schedule.component.html — h1 heading
## Summary Addresses all items from the SEO expert review: ### Changes 1. **Add `public/robots.txt`** — Allow all crawlers, disallow `/admin` and `/login`, reference sitemap URL. 2. **Add `public/sitemap.xml`** — Includes all 5 public routes (`/`, `/about`, `/schedule`, `/events`, `/contact`) with appropriate change frequencies and priorities. 3. **Fix heading hierarchy** — Changed `<h2>` to `<h1>` on About, Schedule, Events, and Contact page components for proper SEO heading structure. 4. **Remove/populate dead footer links** — Eliminated all `href="#"` placeholder links: - Volunteer → routes to `/contact?subject=volunteer` - Corporate Giving → routes to `/contact?subject=advertising` - Planned Giving → routes to `/contact?subject=general` - Social media links → conditional rendering based on `config().social_*` fields (hidden when empty, no dead links) - Events "Learn More" → routes to `/contact?subject=general` 5. **Fix Logo ARIA** — Added `aria-hidden="true"` to the decorative logo image in the About page divider section. 6. **Backend support for social media** — Added `social_facebook`, `social_twitter`, `social_instagram`, `social_youtube` fields to: - `StationConfig` DB model + startup migration - `StationConfigResponse` and `StationConfigUpdate` Pydantic schemas - Frontend `StationConfig` TypeScript interface ### Files Changed - `.gitea/workflows/ci.yaml` — Added branch trigger for `fix/seo-improvements` - `backend/app/models.py` — Added social media columns to `StationConfig` - `backend/app/schemas.py` — Added social media fields to response/update schemas - `backend/app/main.py` — Added social media columns to startup migration - `public/robots.txt` — New file - `public/sitemap.xml` — New file - `src/app/about/about.component.html` — h1 heading + aria-hidden on decorative logo - `src/app/contact/contact.component.html` — h1 heading - `src/app/events/events.component.html` — h1 heading + fixed dead "Learn More" link - `src/app/footer/footer.component.html` — Removed dead links, conditional social media - `src/app/interfaces/station-config.ts` — Added social media fields - `src/app/schedule/schedule.component.html` — h1 heading
robot added 1 commit 2026-08-04 03:23:08 -07:00
fix(seo): comprehensive SEO improvements per expert audit
CI Pipeline / backend-test (pull_request) Successful in 4m20s
CI Pipeline / frontend-test (pull_request) Successful in 27s
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 / backend-test (push) Successful in 4m22s
CI Pipeline / frontend-test (push) Successful in 26s
CI Pipeline / frontend-build (push) Failing after 28s
CI Pipeline / backend-lint (push) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 1s
bea1c97e88
- 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
robot added 1 commit 2026-08-04 03:32:50 -07:00
fix(seo): add RouterLinkWithHref imports + default social media fields
CI Pipeline / backend-test (push) Successful in 4m24s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (pull_request) Successful in 2s
CI Pipeline / frontend-test (push) Successful in 28s
CI Pipeline / frontend-build (push) Successful in 29s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 4m19s
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
e3a742b708
- 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
robot added 1 commit 2026-08-04 03:40:41 -07:00
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 / 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-test (push) Successful in 4m26s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
a7d994a122
kfj001 left a comment
Owner

Got some questions. Let's get em answered and see what comes next.

Got some questions. Let's get em answered and see what comes next.
@@ -3,3 +3,3 @@
on:
push:
branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles]
branches: [main, develop, feature/tests-and-ci, feature/multi-admin-roles, fix/seo-improvements]
Owner

There has got to be a more general way to ensure this runs on any branch on push.

There has got to be a more general way to ensure this runs on any branch on push.
kfj001 marked this conversation as resolved
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
Owner

This is a whitelabel app - this file may need to be dynaimcally generated...

This is a whitelabel app - this file may need to be dynaimcally generated...
robot added 1 commit 2026-08-04 12:55:04 -07:00
fix: address reviewer feedback — dynamic sitemap/robots + wildcard CI branches
CI Pipeline / backend-test (push) Successful in 8m12s
CI Pipeline / frontend-test (push) Successful in 28s
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 / 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 Pipeline / quality-gate (pull_request) Successful in 2s
5484c761b4
- 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'
robot added 1 commit 2026-08-04 13:06:25 -07:00
fix: switch sitemap/robots to dynamic generation + Nginx proxy
CI Pipeline / backend-test (push) Successful in 8m58s
CI Pipeline / frontend-test (push) Successful in 29s
CI Pipeline / frontend-build (push) Successful in 31s
CI Pipeline / backend-lint (push) Successful in 10s
CI Pipeline / backend-test (pull_request) Successful in 8m56s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / frontend-build (pull_request) Successful in 32s
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
3dead2921a
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.
Author
Member

Addressed reviewer feedback

1. CI workflow - wildcard branches (.gitea/workflows/ci.yaml)

Changed the hardcoded branch list to branches: ["**"] so the pipeline triggers on any branch push, not just 4 named branches.

2. Dynamic sitemap.xml + robots.txt

Removed the static files and replaced them with dynamic API endpoints:

  • GET /api/sitemap/sitemap.xml - generates XML using KMTN_WEBSITE_URL so each tenant gets their own domain
  • GET /api/sitemap/robots.txt - same dynamic approach, with the Sitemap directive pointing to the correct tenant URL
  • Nginx now proxies /sitemap.xml and /robots.txt to those backend endpoints, so search crawlers hit the right content at root paths

This follows the same white-label pattern as theme.json - content is derived from config at runtime, not baked into git.

## Addressed reviewer feedback ### 1. CI workflow - wildcard branches (`.gitea/workflows/ci.yaml`) Changed the hardcoded branch list to `branches: ["**"]` so the pipeline triggers on **any** branch push, not just 4 named branches. ### 2. Dynamic sitemap.xml + robots.txt Removed the static files and replaced them with dynamic API endpoints: - `GET /api/sitemap/sitemap.xml` - generates XML using `KMTN_WEBSITE_URL` so each tenant gets their own domain - `GET /api/sitemap/robots.txt` - same dynamic approach, with the Sitemap directive pointing to the correct tenant URL - Nginx now proxies `/sitemap.xml` and `/robots.txt` to those backend endpoints, so search crawlers hit the right content at root paths This follows the same white-label pattern as `theme.json` - content is derived from config at runtime, not baked into git.
Author
Member

Status: Ready for review and merge

Both of your feedback items have been addressed:

1. CI workflow triggers on any branch

Changed from hardcoded branch list to branches: ["**"] wildcard in .gitea/workflows/ci.yaml — pipeline now fires on every branch push, not just the 4 named ones.

2. Dynamic sitemap.xml + robots.txt (white-label aware)

Removed static files. Added backend API endpoints that generate content from KMTN_WEBSITE_URL at runtime, so each tenant gets their own domain in the output. Nginx proxies /sitemap.xml and /robots.txt to the backend — same pattern as the existing theme.json export.

CI: Run #94 passed (all 5 jobs: backend-test, frontend-build, frontend-test, backend-lint, quality-gate). PR is mergeable.

All 5 SEO tasks completed:

  • robots.txt (dynamic)
  • sitemap.xml (dynamic)
  • Heading hierarchy (h1 on all public pages)
  • Dead footer links (wired to real routes + conditional social media)
  • Logo ARIA (aria-hidden on decorative about page logo)

Ready for your approval to merge to main.

## Status: Ready for review and merge Both of your feedback items have been addressed: ### 1. CI workflow triggers on any branch Changed from hardcoded branch list to `branches: ["**"]` wildcard in `.gitea/workflows/ci.yaml` — pipeline now fires on every branch push, not just the 4 named ones. ### 2. Dynamic sitemap.xml + robots.txt (white-label aware) Removed static files. Added backend API endpoints that generate content from `KMTN_WEBSITE_URL` at runtime, so each tenant gets their own domain in the output. Nginx proxies `/sitemap.xml` and `/robots.txt` to the backend — same pattern as the existing `theme.json` export. **CI**: Run #94 passed (all 5 jobs: backend-test, frontend-build, frontend-test, backend-lint, quality-gate). PR is mergeable. **All 5 SEO tasks completed:** - [x] robots.txt (dynamic) - [x] sitemap.xml (dynamic) - [x] Heading hierarchy (h1 on all public pages) - [x] Dead footer links (wired to real routes + conditional social media) - [x] Logo ARIA (aria-hidden on decorative about page logo) Ready for your approval to merge to main.
kfj001 requested changes 2026-08-04 14:41:06 -07:00
Dismissed
kfj001 left a comment
Owner

Closer! Let's get some tests in this.

Closer! Let's get some tests in this.
@@ -0,0 +1,60 @@
"""Sitemap & robots.txt API: tenant-aware SEO files served by the backend.
Owner

This needs a unit test.

This needs a unit test.
@@ -0,0 +1,52 @@
"""Sitemap & robots.txt generation: tenant-aware SEO content.
Owner

Unit test for this type too.

Unit test for this type too.
kfj001 marked this conversation as resolved
robot added 1 commit 2026-08-04 15:01:25 -07:00
test(seo): add unit tests for sitemap export and API endpoints
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-lint (push) Successful in 10s
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 (push) Successful in 3s
CI Pipeline / quality-gate (pull_request) Successful in 2s
3687e91f7f
- 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'
@@ -23,6 +23,11 @@ export interface StationConfig {
play_store_icon_url: string;
play_store_url: string;
app_store_embed_html: string;
// Social media
Owner

I don't think we've added any admin UI to set these values.

I don't think we've added any admin UI to set these values.
kfj001 marked this conversation as resolved
robot added 1 commit 2026-08-04 17:27:31 -07:00
fix(seo): add admin UI for social media URLs
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 (push) Successful in 11s
CI Pipeline / backend-test (pull_request) Successful in 10m43s
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 4s
CI Pipeline / frontend-test (pull_request) Successful in 28s
CI Pipeline / frontend-build (pull_request) Successful in 32s
58a25370b9
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.
kfj001 added 1 commit 2026-08-04 19:31:38 -07:00
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 / backend-test (pull_request) Successful in 10m11s
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 / 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
70bf1830b2
kfj001 approved these changes 2026-08-04 19:56:45 -07:00
kfj001 left a comment
Owner

👍

👍
robot merged commit 55be42ed99 into main 2026-08-04 20:03:55 -07:00
kfj001 deleted branch fix/seo-improvements 2026-08-04 20:05:10 -07:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WesternTechnologies/kmtnflower#4