Skip to content

feat(seo): public crawlable snapshots, social cards, oEmbed & sitemap#36

Merged
krazyjakee merged 3 commits into
masterfrom
feat/members-embed-user
Jun 15, 2026
Merged

feat(seo): public crawlable snapshots, social cards, oEmbed & sitemap#36
krazyjakee merged 3 commits into
masterfrom
feat/members-embed-user

Conversation

@krazyjakee

Copy link
Copy Markdown
Contributor

Summary

  • Adds a same-origin SEO/social-embed layer so public spaces, channels, and forum posts get crawlable, indexable URLs and rich social cards (the Flutter web client is CanvasKit-rendered and invisible to crawlers, so this must be server-side).
  • Crawlers get server-rendered HTML snapshots; humans get a daccord:// redirect landing page. Gating is "public space = public": any non-hidden channel in a public space is rendered.
  • Full SEO sweep: <meta name="description">, Open Graph + Twitter Card with a banner → splash → icon image fallback, absolute canonical/og:url, and article:published_time/modified_time/author on posts.
  • Structured data (Schema.org JSON-LD): DiscussionForumPosting, BreadcrumbList, CollectionPage.
  • New endpoints: /sitemap.xml, /robots.txt, and an /oembed?url=...&format=json provider with <link rel="alternate" ... +oembed> discovery on every page.
  • docs/seo.md documents URL structure, gating, meta tags, structured data, oEmbed, and the client share-link mapping.

Notes for reviewers

  • This branch also contains the prior feat(members): embed user objects commit; the SEO work is the feat(seo): ... commit on top.
  • Pure helpers are unit-tested (url_seg/url_unseg round-trip, cdn_url normalisation, space_social_image fallback order, twitter_card, xml_escape, iso_datetime, resolve_post_title, hidden-channel exclusion) — 10 tests pass.
  • Verified live against a seeded DB as Googlebot: meta tags, JSON-LD, breadcrumbs, oEmbed JSON, sitemap and robots all render correctly.

Test plan

  • cargo clippy --all-targets is clean
  • cargo test --lib seo:: passes
  • curl -A Googlebot /s/{slug}/{channel}/{post} returns an HTML snapshot with OG/Twitter/JSON-LD
  • curl /sitemap.xml, /robots.txt, /oembed?url=...&format=json return expected output
  • A non-public space returns 404 to crawlers

🤖 Generated with Claude Code

krazyjakee and others added 2 commits June 11, 2026 16:22
Add an opt-in `?with_user=true` query param to the member list and search
endpoints. When set, each member carries its public `user` object, resolved
for the whole page in a single batched query, so clients no longer have to
fetch users one at a time after listing members.

Default behavior is unchanged (no embedded `user`), and the embedded object is
the PublicUser shape — sensitive fields (is_admin, mfa_enabled, disabled,
flags) are stripped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Server-render public spaces, channels, and forum posts for crawlers (the
Flutter web client is CanvasKit and invisible to bots), and emit rich
social-card metadata when links are pasted into chat/social apps.

- Per-server sitemap.xml + robots.txt enumerating public spaces/channels/posts
- Full meta tags: description, Open Graph, Twitter Card with banner→splash→icon
  image fallback, canonical, and article published/modified/author on posts
- Structured data: DiscussionForumPosting, BreadcrumbList, CollectionPage
- oEmbed provider endpoint (/oembed) + discovery links on every page
- Gating: any non-hidden channel in a public space is crawlable
- docs/seo.md documents URL structure, gating, meta, structured data, oEmbed

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@krazyjakee krazyjakee merged commit d4a84b2 into master Jun 15, 2026
3 checks passed
@krazyjakee krazyjakee deleted the feat/members-embed-user branch June 15, 2026 21:23
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