CreatrWeb is an author-owned microblogging application built for publishing short-form posts on a personal site while still allowing lightweight community interaction. The product is centered on one canonical publisher, with authenticated visitors participating through comments and reactions rather than publishing their own primary posts.
The application is split into a React frontend and an Express API, with authentication handled in-app through Auth.js and persistence managed through Drizzle ORM on top of MySQL. It is designed to support direct publishing on your own domain, standardized public feeds, and a clear separation between publishing authority and member participation.
This repository contains a TypeScript monorepo with three main layers:
artifacts/microblog: the Vite + React frontendartifacts/api-server: the Express 5 backendlib/db: shared database schema and Drizzle configuration
At a high level, the app provides:
- owner-only post publishing and editing
- authenticated member comments and reactions
- rich post authoring with sanitized HTML storage
- standardized public feeds and export endpoints
- shared publishing through a single canonical MySQL database
- local and deployed app instances operating on the same authoritative content store
CreatrWeb behaves like a single-author social publishing site. The owner can publish canonical posts, while visitors can sign in and participate around those posts. The site is meant to live on the author's own domain and act as the primary home for published content.
owner: can create, edit, and delete posts; can upload media; can moderate commentsmember: can sign in, comment, and edit their own comments- unauthenticated visitors: can read the public site and consume its feeds
Publishing authority is intentionally separate from authentication. Logging in does not grant the right to publish posts.
The owner can create posts in two formats:
- legacy plain-text posts
- rich posts stored as sanitized HTML
Rich posts support:
- formatting through a toolbar-backed editor
- local image uploads
- owner-trusted
https:iframe embeds
HTML is sanitized on the server before it is stored, and the frontend renders rich content after that sanitization step.
Members can:
- comment on posts
- edit their own comments after posting
- react to content
Comments currently remain plain text even though posts support rich formatting.
The homepage acts as the main feed of posts and supports client-side browsing controls such as sorting and filtering. The owner-facing composer is collapsed by default and only expands when the owner chooses to start a post.
The site publishes public machine-readable outputs so content remains accessible outside the main web UI.
GET /feed.xml: Atom feedGET /feed.json: JSON Feed 1.1GET /export/json: mf2-JSON exportGET /export.json: compatibility alias retained for stability
These endpoints are part of the app’s long-term public surface and are intended to remain stable.
Authentication is handled by Auth.js in the Express server. The current provider set is:
- GitHub OAuth
- Google OAuth
The first owner account is established by signing in once and then promoting that user in the local database.
The app stores:
- users and local roles
- Auth.js accounts and sessions
- posts and comments
- reactions
The app now treats one MySQL database as the authoritative store for posts, comments, reactions, users, and Auth.js session data across both local and deployed runtimes.
- TypeScript across the repo
- npm workspaces monorepo
- React 19 + Vite frontend
- Express 5 backend
- Auth.js for authentication
- Drizzle ORM for persistence
- MySQL for storage
artifacts/
api-server/ Express API and auth runtime
microblog/ React frontend
lib/
db/ Shared schema and Drizzle config
api-spec/ OpenAPI source
api-client-react/ Generated React client
api-zod/ Generated Zod schemas
scripts/ Admin and maintenance scripts
docs/ Setup and dependency notes
Run the frontend and backend in separate terminals:
npm run dev:api
npm run dev:webDefault local origins:
- frontend:
http://localhost:3000 - backend:
http://localhost:8080
The frontend dev server proxies /api/* and /auth/* to the backend.
Core local variables are documented in docs/auth-setup.md and .env.example. The main ones are:
PORTFRONTEND_PORTAPI_ORIGINALLOWED_ORIGINSAUTH_SECRETAUTH_URLGITHUB_IDGITHUB_SECRETGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETDB_HOSTDB_PORTDB_NAMEDB_USERDB_PASSSQLITE_IMPORT_PATHfor the one-time SQLite import source
The runtime expects MySQL connection settings and uses one canonical database for both local and deployed app sessions. Local edits and deployed edits are expected to land in the same datastore when they share the same environment configuration.
This means:
- local and deployed app instances can read and write the same canonical content store
- the old SQLite content exists only as migration/recovery material rather than as the intended runtime database
After the first successful sign-in, promote the intended site owner using the helper script:
npm run list-users --workspace=@workspace/scripts
npm run promote-owner --workspace=@workspace/scripts -- --email you@example.comYou can also promote by user ID instead of email.
Useful root commands:
npm run typecheck
npm run build
npm run startLegacy SQLite import command:
npm run import-sqlite-to-mysql --workspace=@workspace/scripts- Auth.js is mounted under
/auth - the backend is the source of truth for authorization
- rich post HTML is sanitized on the server before persistence
- public feed and export routes are part of the stable site surface