Skip to content

feat(web): migrate apps/web to static Astro app - #9

Merged
caio-pizzol merged 5 commits into
mainfrom
caio-pizzol/astro-web-migration
May 19, 2026
Merged

feat(web): migrate apps/web to static Astro app#9
caio-pizzol merged 5 commits into
mainfrom
caio-pizzol/astro-web-migration

Conversation

@caio-pizzol

Copy link
Copy Markdown
Contributor

Replaces the static-HTML + generate-pages.ts setup with a static Astro build. Output stays purely static so deployment can keep using R2 + Cloudflare without a Worker between users and raw /documents/* files. The api.docxcorp.us worker is untouched, and the existing client-rendered homepage rides through as a public/ asset, no port in this PR.

Three commits in order:

  • Content drafts in apps/web/content/{dataset,classification,quality,download}.md, grounded in live /stats and DB queries (verified 2026-05-19)
  • The Astro app itself: 4 SEO content pages, /types/{type} + /topics/{topic} dynamic routes generated at build time from Neon, generated /sitemap.xml + /robots.txt + /llms.txt routes, shared Layout.astro with canonical/OG/JSON-LD/breadcrumb plumbing
  • tsconfig boundary fix so the Astro typecheck doesn't pull in the worker package

Must stay the same: the explorer JS on the homepage (apps/web/public/index.html); /documents/* and /extracted/* keep X-Robots-Tag: noindex from the Cloudflare Transform Rule; api.docxcorp.us worker.

Review: check the deploy workflow rewrite (.github/workflows/deploy-site.yml) — extensionless HTML keys via a find loop, aws s3 sync with no --delete so scraper-managed /documents/ and /extracted/ keys are not touched. Skip the lefthook + format chatter in the diff; it's noise.

Verified locally:

  • bun run --cwd apps/web typecheck -> 0 errors
  • DATABASE_URL=... bun run --cwd apps/web build -> 25 pages built in ~17s
  • Upload-key transformation simulated: index.html stays at /, everything else loses .html
  • Homepage favicon refs updated from /public/favicon.ico -> /favicon.ico (4 lines, only change to the legacy homepage)

Heads up: merging this auto-deploys to production R2 via the existing push trigger. There is no staging environment. After merge, the workflow will run on main; smoke test:

  1. curl -sI https://docxcorp.us/ -> 200, homepage HTML
  2. curl -sI https://docxcorp.us/dataset -> 200, text/html
  3. curl -s https://docxcorp.us/sitemap.xml | head -5 -> shows new routes
  4. curl -sI https://docxcorp.us/documents/<hash>.docx -> still 200 + x-robots-tag: noindex
  5. curl -s https://docxcorp.us/llms.txt | head -10 -> live counts

If anything regresses, revert the merge and dispatch the workflow against main; R2 will be re-uploaded from the previous good state on the next deploy.

Out of scope (follow-ups):

  • Port the explorer JS into an Astro island (PR 3b)
  • Document detail pages, language pages

…ontent

Four markdown drafts in apps/web/content/ for the SEO pages researchers
actually need before they download. Numbers are grounded in live API
and DB queries (verified 2026-05-19), not brand.md (some figures
there are stale). Limitations are first-class per brand voice.

Drafts include frontmatter (title, description, canonicalPath, status,
lastVerified, primaryQuery, secondaryQueries) so the Astro migration
can consume them as page source rather than rewriting from scratch.

Not wired into the build. Routes don't exist yet; these are content
input for the upcoming Astro migration (PR 3).
Replaces the static-HTML + generate-pages.ts setup with a static Astro
build. Output stays purely static so deployment can keep using R2 +
Cloudflare without a Worker between users and raw /documents/* files.

What's new
- 4 SEO content pages (/dataset, /classification, /quality, /download)
  rendered from the markdown drafts in apps/web/content/
- /types/{type} and /topics/{topic} dynamic routes generated at build
  time from live Neon queries via @neondatabase/serverless
- Generated /sitemap.xml, /robots.txt, /llms.txt routes (live numbers,
  no more stale hand-edited copies)
- Shared Layout.astro with canonical/OG/Twitter/JSON-LD/breadcrumb
  plumbing in one place

What's preserved
- The existing client-rendered homepage (apps/web/public/index.html)
  copies through Astro's public/ into dist/ unchanged; the explorer
  port is out of scope for this PR
- The api.docxcorp.us worker (apps/web/worker/) is untouched
- /documents/* and /extracted/* keep noindex via Cloudflare Transform

Deploy workflow
- bun install --frozen-lockfile then bun run --cwd apps/web build
- aws s3 sync uploads non-HTML assets with their natural extensions
- A find loop uploads HTML to extensionless keys (/classification.html
  -> /classification) to match the canonical URLs; index.html is the
  one exception and stays at /
- No --delete flag: the bucket also holds scraper-managed /documents/
  and /extracted/

Cleanup
- Removed legacy static apps/web/{robots.txt, llms.txt, sitemap.xml};
  Astro routes replace them
- Removed apps/web/scripts/{generate-pages,generate-stats,preview}.ts
- Top-level dev:web now runs astro dev
The Astro app's tsconfig.json used 'include: ["**/*"]', which pulled
in apps/web/worker/src/index.ts and ran the worker's code through the
Astro app's typecheck. The worker has its own runtime types and
deployment; it should not be checked under the Astro app's config.

Restrict the include to src/ only and exclude worker explicitly so each
package owns its own type boundary. Also drop an unused import from
types/[type].astro that the stricter pass surfaced.
@codecov

codecov Bot commented May 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

The web Astro build was only happening at deploy time, after merge.
That made 'CI green' misleading for PRs touching apps/web because the
build that actually ships was never run.

Add two steps:
- Astro check (fast, no env)
- Astro build with DATABASE_URL (the dynamic /types/* and /topics/*
  routes need live counts at build time)

Now every PR runs the same build that production runs.
Astro 6 requires Node >=22.12.0. The runner ships Node 20 by default,
and setup-bun doesn't install Node, so astro check / astro build fail
even when run via 'bun run'. Add actions/setup-node@v4 with node 22 to
both ci.yml and deploy-site.yml before the Astro steps.
@caio-pizzol
caio-pizzol merged commit f0cc637 into main May 19, 2026
2 checks passed
@caio-pizzol
caio-pizzol deleted the caio-pizzol/astro-web-migration branch May 19, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants