Skip to content

Migrate from Cloudflare Workers to Haverstack server backend#99

Open
cuibonobo wants to merge 8 commits into
mainfrom
claude/cuibonobo-stack-migration-aij5rb
Open

Migrate from Cloudflare Workers to Haverstack server backend#99
cuibonobo wants to merge 8 commits into
mainfrom
claude/cuibonobo-stack-migration-aij5rb

Conversation

@cuibonobo

Copy link
Copy Markdown
Owner

Summary

This PR migrates the website from a custom Cloudflare Workers-based backend to the Haverstack server as the primary backend service. The API client has been completely refactored to communicate with Haverstack's wire format instead of the previous custom format.

Key Changes

Backend Migration

  • Removed Cloudflare Workers infrastructure: Deleted all functions/ directory code including custom resource/type controllers, database models, and media handlers
  • Removed local database layer: Eliminated D1 database models and query builders (functions/stack/models/)
  • Removed Wrangler configuration: Simplified wrangler.toml to only contain basic project metadata

API Client Refactoring (src/lib/api.ts)

  • Introduced wire format types (WireRecord, WireListResult, WireType) matching Haverstack server format
  • Implemented conversion functions (wireToResource, wireToPageMeta, toWireContent) to translate between wire format and application models
  • Refactored HTTP helpers to use standard fetch with proper JSON serialization
  • Updated all API endpoints to use new Haverstack server URLs and query formats:
    • Changed from /resources to /records endpoints
    • Implemented records/query POST endpoint for filtered searches
    • Added support for typeId format (type@version)
  • Added PageMetaType support for managing article metadata (slugs, publication dates) separately from content

Type System Updates

  • Updated ResourceBase interface: renamed created_date/updated_date to createdAt/updatedAt, replaced is_public with parentId
  • Removed slug field from ArticleType.content (now stored in separate PageMetaType)
  • Added new PageMetaType interface for site generator metadata

CLI and Build System Updates

  • Updated cli.ts to use new API functions and handle page metadata separately
  • Modified lock.ts to manage article slugs through the new page metadata system
  • Updated site.ts, feed.ts, and sitemap.ts to use buildPageMetaMap() for slug resolution
  • Changed media handling to use new attachment format with fileId instead of id
  • Updated dev server command from Wrangler to simple HTTP server

Configuration

  • Added .env.example documenting STACK_SERVER_URL and authentication token configuration
  • Removed Cloudflare-specific environment variables and bindings

Implementation Details

  • Wire format conversion is centralized in dedicated functions for maintainability
  • Attachment handling now properly separates stored attachments array from typed content fields
  • Page metadata is fetched and cached separately from article content, enabling independent updates
  • All HTTP requests include proper Content-Type headers and authentication

https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz

claude added 3 commits June 21, 2026 15:12
- Replace Cloudflare Functions (/stack, /media) with haverstack REST API
- Rewrite src/lib/api.ts to speak haverstack wire format (records/query/types/attachments)
- Update attachment model: id → fileId, upload via raw binary POST instead of multipart
- Rename ResourceBase date fields: created_date/updated_date → createdAt/updatedAt
- Remove is_public field; createResource now sets { access: 'public' } permissions
- Update CLI init command to register types in haverstack format (id, baseId, version, schema)
- Replace wrangler dev server with npx serve; remove D1/R2/migrate scripts
- Delete codec/bucket.ts and all functions/ (no longer needed)
- Add .env.example documenting STACK_SERVER_URL and API_TOKEN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
Articles no longer carry a slug in their content — the slug lives in a
linked site.gen/page-meta@1 record (parentId → article ID). The CLI
editing workflow is unchanged: slug still appears in the frontmatter, but
on commit it is written to/updated in page-meta rather than the article.

The site generator, feed builder, and sitemap builder all load a page-meta
map once and use it to resolve article slugs when constructing URLs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
- Add @haverstack/adapter-api, @haverstack/wire-types, @haverstack/core
- Rewrite api.ts around APIAdapter singleton; wire types now come from
  @haverstack/wire-types instead of local definitions
- Date fields on records are already Date objects via the adapter, so
  wireToResource no longer needs to parse ISO strings
- hashSchema from @haverstack/core replaces the local getTypeHash
  implementation in codec/type.ts; exported via api.ts to keep the
  TypeSchema cast internal
- media.ts is unchanged: attachment uploads require Content-Type and
  Content-Disposition headers that the adapter's putAttachment does not
  support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying cuibonobo with  Cloudflare Pages  Cloudflare Pages

Latest commit: efc6264
Status:🚫  Build failed.

View logs

claude added 5 commits June 25, 2026 14:01
Upgrades @haverstack/core to 0.8.0 and @haverstack/adapter-api to 0.6.0.
Exposes getStack() from api.ts (Stack.create wrapping the APIAdapter singleton)
and rewrites media.ts to use stack.putAttachment/getAttachment/deleteAttachment,
removing direct fetch calls and the auth.ts dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
Removes the attachments array from record content schemas and from
ResourceBase. Attachments are now stored as AttachmentAssociation
entries on the record via Stack.associate/dissociate. The label field
carries the filename, replacing the old tag categorization (which is
derivable from mimeType). On commit, existing associations are
dissociated and their bytes deleted before re-uploading.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
Reads all resources from the old stack API, transforms the format
(snake_case dates, attachments-in-content → AttachmentAssociation,
article slug → site.gen/page-meta@1), and writes to the new
haverstack server. Preserves original resource IDs by calling
adapter.createRecord() directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
Adds ensureTypes() which creates the four content types (note, article,
page, site.gen/page-meta) before migrating resources, skipping any that
already exist. Resource creation now checks adapter.getRecord() first and
skips if present. Page-meta is checked via queryRecords before creating.
Attachments compare expected filenames against existing association labels
and skip if all are present; a partial set is cleared and fully re-uploaded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
Uses @haverstack/adapter-sqlite directly so adapter.createRecord() stores
the original resource id, createdAt, and updatedAt exactly — the HTTP API
path (APIAdapter) ignored those fields and generated new values.

- Install @haverstack/adapter-sqlite@^0.5.0 (migration-only dependency)
- Replace APIAdapter with SQLiteAdapter; open or initialize based on
  whether DB_PATH already exists
- Replace NEW_STACK_URL / NEW_API_TOKEN env vars with DB_PATH / ENTITY_ID / TIMEZONE
- Cast SQLiteAdapter to StackAdapter to bridge adapter-sqlite targeting
  @haverstack/core@0.5.0 while the app uses core@0.8.0; the missing
  fields (ownerEntityId, timezone) are not used by the migration logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCjeen8sSta78QhUWGDQSz
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