Skip to content

refactor(marketing): drop the docs and blog, now published drives elsewhere - #2427

Open
2witstudios wants to merge 8 commits into
pu/move-blogs-to-pagespace-dev-drivefrom
pu/retire-marketing-docs-blog
Open

refactor(marketing): drop the docs and blog, now published drives elsewhere#2427
2witstudios wants to merge 8 commits into
pu/move-blogs-to-pagespace-dev-drivefrom
pu/retire-marketing-docs-blog

Conversation

@2witstudios

@2witstudios 2witstudios commented Aug 16, 2026

Copy link
Copy Markdown
Owner

Stacked on #2426. Edge counterpart: PageSpace-Deploy#26.

⚠️ Merge order

Do not merge before PageSpace-Deploy#26 is deployed. Until the edge stops routing /docs and /blog here, merging this takes both sections down.

What goes

  • 34 docs page.tsx files (~3,000 lines of markdown embedded in TSX)
  • the 7-post blog and its 888-line data.ts
  • 30 MB of hero PNGs under public/blog

The content now lives in two PageSpace drives published at docs.pagespace.ai / blog.pagespace.ai, migrated by scripts/migrate-docs-blog-to-drives.ts from #2426.

Links

Outbound links go through a new lib/sites.ts (docsUrl() / blogUrl()) rather than bare paths. They're cross-origin now — a bare /docs/... would resolve against pagespace.ai and take the edge 301 instead of going straight to the destination. Hosts are overridable via NEXT_PUBLIC_DOCS_URL / NEXT_PUBLIC_BLOG_URL, defaulting to production.

Cascading removals

Each because its only consumer was deleted — the last two surfaced by knip:check, not by guesswork:

  • DocsContent / DocsSidebar, and with them react-markdown + remark-gfm
  • schema.tsx's Article schema (the published blog emits its own structured data)
  • pageMetadata.blog / .docs
  • ui/scroll-area.tsx + @radix-ui/react-scroll-area, hooks/use-mobile.ts

Two judgement calls

search-data.ts keeps its hand-maintained docs entries, repointed at the new host, but stops indexing blog post bodies. That index was built from data.ts at build time and there is no longer a build-time source for it. The palette still links out to the blog itself. (SearchDialog uses router.push, which hard-navigates on an absolute URL.)

sitemap.ts no longer lists either section. Both published drives emit their own sitemap.xml with canonical URLs on their own host; re-listing them under pagespace.ai would advertise URLs that only 301.

Verification

bun run typecheck, bun run lint, bun run knip:check, and a full marketing build all pass with neither route present.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DwC25dWRtdaV6h3mjqqsog


Review round 1 (3b0bc4a)

Codex caught three /docs links this PR left dangling — SecurityPageSections.tsx:41,47,396, two of them the security page's primary CTAs. With the docs routes deleted they resolve against pagespace.ai: in production they take the edge 301, but in a standalone or local marketing build they are a plain 404. All three go through docsUrl() now.

The miss happened because the original sweep read a truncated grep, so fixing only the links would leave the same failure mode open for the next route that moves. Added scripts/__tests__/marketing-no-local-docs-blog-links.test.ts, which walks apps/marketing/src and fails on any bare href="/docs...", href="/blog...", or markdown ](/docs...), reporting file:line. CI already runs that suite (cd scripts && bunx vitest run, ci.yml:135). It includes two tests proving it is not vacuous, and is mutation-checked.

Re-swept: zero bare /docs or /blog links remain in apps/marketing/src.

⚠️ Cross-PR collision

Two open blog PRs write to the paths this PR deletes — see the detailed comment. Short version: #1295 adds a post (browser-style-tabs) that is lost unless migrated to the blog drive; #1296 adds only an orphan image and is already inert. All 7 slugs currently on master are live in the drive, so nothing published is at risk.

…ewhere

Removes what the app no longer serves: 34 docs page.tsx files (~3,000
lines of markdown embedded in TSX), the 7-post blog and its 888-line
data.ts, and 30 MB of hero PNGs under public/blog. The content now lives
in two PageSpace drives published at docs.pagespace.ai and
blog.pagespace.ai (migrated by scripts/migrate-docs-blog-to-drives.ts).

Links out to those hosts go through the new lib/sites.ts helpers rather
than bare paths — they are cross-origin now, and a bare /docs/... would
resolve against pagespace.ai and take the edge 301 instead of going
straight to the destination.

Cascading removals, each because its only consumer was deleted:
- DocsContent/DocsSidebar, and with them react-markdown + remark-gfm
- schema.tsx's Article schema (the published blog emits its own)
- pageMetadata.blog / .docs
- ui/scroll-area.tsx + @radix-ui/react-scroll-area, hooks/use-mobile.ts
  (surfaced by knip:check, not guesswork)

search-data.ts keeps its hand-maintained docs entries, repointed at the
new host, but stops indexing blog post bodies — that index was built from
data.ts at build time and there is no longer a build-time source for it.
The palette still links to the blog itself.

sitemap.ts no longer lists either section. Both published drives emit
their own sitemap.xml with canonical URLs on their own host; listing them
under pagespace.ai would advertise URLs that only 301.

DO NOT MERGE before the Caddyfile change in PageSpace-Deploy is deployed.
Until then the edge still routes /docs and /blog here, and merging this
takes both sections down. Verified: typecheck, lint, knip:check, and a
marketing build all pass with neither route present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwC25dWRtdaV6h3mjqqsog
@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6d075324-82b8-4ee4-b6d6-627e585d0a76

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@2witstudios
2witstudios marked this pull request as ready for review August 16, 2026 13:54

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cee2c158de

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +14 to +15
export const DOCS_URL = process.env.NEXT_PUBLIC_DOCS_URL ?? 'https://docs.pagespace.ai';
export const BLOG_URL = process.env.NEXT_PUBLIC_BLOG_URL ?? 'https://blog.pagespace.ai';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expose the new site URLs to Docker builds

When a self-hosted or staging Docker build passes NEXT_PUBLIC_DOCS_URL or NEXT_PUBLIC_BLOG_URL, Docker discards them because the marketing Dockerfile's builder declares and exports only the existing public variables at lines 46–58. Since Next.js bakes these values into the client bundle during next build, setting them on the runtime container cannot help, and every Docker image continues linking to the production docs and blog hosts; add both variables to the builder's ARG and ENV lists (and pass them from the relevant Compose/workflow configuration).

AGENTS.md reference: AGENTS.md:L230-L234

Useful? React with 👍 / 👎.

export const metadata = createMetadata({
title: "Security",
description: "PageSpace security overview: opaque session tokens, direct-permission RBAC, account lockout, OAuth PKCE, encrypted secrets, rate limiting, continuously verified audit logs, SSRF and path-traversal hardening.",
path: "/docs/security",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Redirect the remaining security CTAs before deleting routes

In a standalone or local marketing deployment without the production edge wildcard redirect, deleting this route leaves the security page's CTAs in SecurityPageSections.tsx lines 41, 47, and 396 pointing at /docs/security and /docs/security/zero-trust, so those prominent links now return the marketing app's 404 instead of honoring the newly configurable docs host. The repo-wide /docs search also shows these are the remaining source links; convert them to docsUrl(...) before removing the local routes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this was a real miss, fixed in 3b0bc4a.

All three links are now docsUrl(...): SecurityPageSections.tsx:41 and :396 ("Security Documentation" / "Security Docs") and :47 ("Zero-Trust Architecture"). Your framing of the impact is right: in production they would have taken the edge 301, but in a standalone or local marketing build with no edge they were a plain 404 — and two of them are the primary CTAs on that page.

The root cause is worth naming: my original sweep read a truncated grep, so these three never entered the changeset. Fixing only the links would leave the same failure mode open for the next route that moves.

So I also added scripts/__tests__/marketing-no-local-docs-blog-links.test.ts, which walks apps/marketing/src and fails on any bare href="/docs...", href="/blog...", or markdown ](/docs...), reporting file:line. CI already runs that suite (cd scripts && bunx vitest run, ci.yml:135). It carries two tests proving it is not vacuous — that it detects a bare link, and that it does not flag the absolute URLs the helpers emit — and I mutation-checked it by restoring one of these hrefs, which turns it red pointing at the exact line.

Re-swept afterwards: zero bare /docs or /blog links remain anywhere in apps/marketing/src. Verified with typecheck, lint, knip:check, and a full marketing build.

2witstudios and others added 2 commits August 18, 2026 00:27
…e next miss

Codex caught three `/docs` links I missed on the security page —
`SecurityPageSections.tsx:41,47,396`, two of them prominent CTAs ("Security
Documentation", "Zero-Trust Architecture"). With the docs routes deleted
these resolve against pagespace.ai: in production they take the edge 301,
but in a standalone or local marketing build they are a plain 404. Now they
go through `docsUrl()` like every other outbound docs link.

The miss happened because the original sweep read a truncated grep. So
rather than just fix the three links, this adds
`scripts/__tests__/marketing-no-local-docs-blog-links.test.ts`, which walks
`apps/marketing/src` and fails on any bare `href="/docs..."`, `href="/blog..."`,
or markdown `](/docs...)`, reporting file:line. CI already runs this suite
(`cd scripts && bunx vitest run`, ci.yml:135).

The guard carries two tests proving it is not vacuous — that it detects a
bare link, and that it does not flag the absolute URLs the helpers emit.
Mutation-checked: restoring one `/docs/security/zero-trust` href turns it
red with the exact file and line.

Verified: typecheck, lint, knip:check, marketing build, scripts suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwC25dWRtdaV6h3mjqqsog
@2witstudios

Copy link
Copy Markdown
Owner Author

⚠️ Cross-PR collision with the two open blog PRs

Flagging this because it is not visible from this diff alone, and it decides whether a post gets lost.

This PR deletes apps/marketing/src/app/blog/[slug]/data.ts and apps/marketing/public/blog/. Two open PRs still write to exactly those paths:

PR Adds Last updated Effect once this merges
#1295 — Blog: Tabs That Work Like Your Browser data.ts entry browser-style-tabs (+60) and a PNG 2026-05-13 Becomes unmergeable — modify/delete conflict on a deleted file. The post is lost unless migrated.
#1296 — Blog: Invite Collaborators Anywhere a PNG only, no data.ts entry 2026-05-08 Already inert — it adds an unreferenced image, and after this PR the directory is gone too.

What I verified

The decision

browser-style-tabs either gets migrated into the blog drive or it gets dropped. That is a content call, not a technical one — the mechanics are cheap either way (create the page from the PR's markdown, publish at /browser-style-tabs, close #1295).

No action taken on either PR. Raising it so the ordering is deliberate rather than discovered as a merge conflict.

@2witstudios

Copy link
Copy Markdown
Owner Author

Link targets verified end-to-end

The guard test proves no bare /docs or /blog links remain. It does not prove the rewritten ones point at anything real — a docsUrl("/page-types") that the migration published under a different path would still 404 after cutover.

So I extracted every docsUrl() / blogUrl() target in apps/marketing/src and requested each against the live published drive:

Target Status
docs/ (root) 200
docs/getting-started 200
docs/features 200
docs/page-types 200
docs/integrations 200
docs/integrations/github 200
docs/integrations/google-calendar 200
docs/integrations/mcp 200
docs/security 200
docs/security/zero-trust 200
blog/ (root) 200

All 11 resolve. Tested against pagespace-docs.pagespace.site / pagespace-blog.pagespace.site, which serve the same artifacts docs.pagespace.ai / blog.pagespace.ai will once the custom domains are registered and mirrored — same bucket, same paths, different host key.

Also re-ran the guard after merging master (95 files) to confirm none of those commits reintroduced a bare link. Clean.

@2witstudios

Copy link
Copy Markdown
Owner Author

CI-equivalent gates run locally, including the DB-backed ones

This PR's base is a feature branch, so no workflow triggers and it shows no checks — which is absent, not passing. I said so earlier and then closed the gap rather than leaving it: stood up a local Postgres 17 on 5433 with its own isolated database, and ran everything CI runs.

Gate Result
bun run typecheck (monorepo) 17/17
bun run lint 15/15
bun run knip:check within baseline
marketing build compiled, no docs/blog routes
scripts suite 319 passed, 17/17 files (was 4 failing on ENOTFOUND postgres)
web unit suite 17,776 passed, 1,186/1,188 files
bun run test:security all 51 suites passed
db:migrate clean, incl. master's two new migrations

The one failing test is master's, not this PR's

chat-mutation-matrix.integration.test.ts > interrupt and resume > hides the streaming placeholder… fails locally. I did not write it off as flaky — I checked:

  1. It fails 3 out of 3 runs in isolation, so it is deterministic locally, not random.
  2. It fails identically on origin/master (b7c0dfc6e) with the same database and no changes from this PR.
  3. It passes on CI — Unit Tests went green on the parent's merged head.

So it is a pre-existing local-environment discrepancy on master, not a regression here.

Separately, the earlier CI failure on the parent branch was a different thing — a genuine flake under concurrent load, confirmed by the same tests failing on the unrelated pu/env-services branch in the same 25-minute window (4 of 30 runs, 3 branches).

Still true

GitHub will retarget this PR to master when #2426 merges, and it gets real CI then. The above is the best available substitute until that happens, not a replacement for it.

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.

1 participant