{"id":50596162,"url":"https://github.com/awsmin/awsmastropress","last_synced_at":"2026-06-05T14:30:22.890Z","repository":{"id":357645726,"uuid":"1237924322","full_name":"awsmin/awsmastropress","owner":"awsmin","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-13T16:32:43.000Z","size":314,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-13T18:26:26.376Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/awsmin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-13T16:32:16.000Z","updated_at":"2026-05-13T16:33:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/awsmin/awsmastropress","commit_stats":null,"previous_names":["awsmin/awsmastropress"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/awsmin/awsmastropress","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsmin%2Fawsmastropress","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsmin%2Fawsmastropress/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsmin%2Fawsmastropress/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsmin%2Fawsmastropress/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awsmin","download_url":"https://codeload.github.com/awsmin/awsmastropress/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awsmin%2Fawsmastropress/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33946818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-05T14:30:21.240Z","updated_at":"2026-06-05T14:30:22.883Z","avatar_url":"https://github.com/awsmin.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AstroPress\n\nA fully open-source, WordPress-compatible CMS built on Astro — deployable to Cloudflare in one command.\n\nNo PHP. No legacy baggage. TypeScript, Astro 4 SSR, Drizzle ORM, and Cloudflare's infrastructure.\n\n[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/awsmin/AstroPress)\n\n---\n\n## What it is\n\nAstroPress is a modern CMS that speaks WordPress — same `wp_*` database schema, same mental model — but runs on the edge with zero PHP. Developers get the extensibility of WordPress; users get a fast, cheap, globally-distributed site.\n\n**Key features:**\n- WordPress-compatible `wp_*` schema (Drizzle ORM + SQLite/D1)\n- Gutenberg block editor embedded in the admin (`@wordpress/block-editor` React island)\n- Custom post types, taxonomies, custom fields (ACF-style) — all managed via UI\n- WPForms-style form builder with entries, conditional logic, multi-page\n- Navigation menus with drag-and-drop reorder and submenu nesting\n- Plugin system — drop a package in `/plugins`, register it in `apps/admin/src/plugins.ts`\n- Cloudflare-native: D1 database + R2 object storage + Pages hosting\n- Session-based auth (Lucia v3)\n\n---\n\n## Monorepo Structure\n\n```\nastropress/\n├── apps/\n│   ├── admin/              # Astro SSR — CMS dashboard (port 4321)\n│   │   ├── src/\n│   │   │   ├── islands/    # React islands (BlockEditor, FormBuilder, etc.)\n│   │   │   ├── layouts/    # AdminLayout.astro\n│   │   │   ├── lib/        # icons.ts, media.ts, posts.ts, slugify.ts\n│   │   │   ├── middleware.ts\n│   │   │   ├── pages/\n│   │   │   │   ├── admin/  # Dashboard, Posts, Pages, Forms, Menus, Settings …\n│   │   │   │   └── api/    # REST endpoints\n│   │   │   └── plugins.ts  # Plugin bootstrap\n│   └── web/                # Astro SSR — public front-end (port 4322)\n│       └── src/\n│           ├── lib/        # query.ts (re-exported from core), formRenderer.ts\n│           ├── layouts/    # BaseLayout.astro\n│           └── pages/      # [slug].astro, blog/[slug].astro, index.astro …\n├── packages/\n│   ├── core/               # Drizzle schema, registry, query helpers, types\n│   │   └── src/\n│   │       ├── query.ts    # WordPress-style data helpers (queryPosts, getField …)\n│   │       ├── registry/   # Post type \u0026 taxonomy registry\n│   │       ├── schema/     # wp_posts, wp_options, wp_terms, wp_users …\n│   │       └── db.ts\n│   ├── auth/               # Lucia v3 session auth\n│   ├── api/                # Hono router foundation\n│   └── ui/                 # Shared React components\n├── plugins/\n│   └── seo/                # First-party SEO plugin\n├── themes/\n│   └── default/            # Default front-end theme\n├── docs/\n│   ├── plugin-api.md\n│   ├── theme-api.md\n│   └── deployment.md\n├── pnpm-workspace.yaml\n├── turbo.json\n└── wrangler.toml\n```\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n- Node.js 20+\n- pnpm 9+\n\n### 1. Install dependencies\n\n```bash\ngit clone https://github.com/awsmin/AstroPress\ncd astropress\npnpm install\n```\n\n### 2. Set up the local database\n\n```bash\npnpm db:setup        # runs migrations against local.db\npnpm db:seed         # optional: seeds demo content\n```\n\n### 3. Start dev servers\n\n```bash\npnpm dev\n```\n\n| App | URL |\n|-----|-----|\n| Admin | http://localhost:4321 |\n| Public site | http://localhost:4322 |\n\nVisit http://localhost:4321 — the **setup wizard** runs on first boot to create your admin account.\n\n### Restart a single app\n\n```bash\n# Kill port and restart\nlsof -ti :4321 | xargs kill -9\ncd apps/admin \u0026\u0026 pnpm dev\n```\n\n---\n\n## Data Layer — Query Helpers\n\nImport from `@astropress/core/query` in any Astro page:\n\n```astro\n---\nimport { queryPosts, getField, getPostTerms, getSiteInfo } from \"@astropress/core/query\";\n\nconst db = Astro.locals.db;\n\n// Like WP_Query\nconst { posts, total, pages } = await queryPosts(db, {\n  type: \"book\",\n  perPage: 12,\n  orderBy: \"title\",\n  order: \"asc\",\n});\n\n// Like ACF get_field / get_post_meta\nconst price = await getField(db, post.id, \"price\");\n\n// Like get_the_terms\nconst categories = await getPostTerms(db, post.id, \"category\");\n\n// Like get_bloginfo\nconst site = await getSiteInfo(db);\n---\n```\n\nFull API reference:\n\n| Function | WP equivalent |\n|---|---|\n| `queryPosts(db, args)` | `WP_Query` |\n| `getPost(db, idOrSlug, type?)` | `get_post()` |\n| `getPostById(db, id)` | `get_post()` |\n| `getPostBySlug(db, slug, type?)` | `get_page_by_path()` |\n| `getChildren(db, parentId)` | `get_children()` |\n| `getAncestors(db, postId)` | parent chain, root-first |\n| `getField(db, postId, key)` | ACF `get_field()` |\n| `theField(db, postId, key)` | ACF `the_field()` — never null |\n| `getFields(db, postId)` | all meta as `Record\u003cstring,string\u003e` |\n| `updatePostMeta(db, id, key, val)` | `update_post_meta()` |\n| `getTerms(db, taxonomy, args?)` | `get_terms()` |\n| `getPostTerms(db, postId, taxonomy)` | `get_the_terms()` |\n| `getPostsByTerm(db, slug, taxonomy)` | `WP_Query` with `tax_query` |\n| `getOption(db, name, fallback?)` | `get_option()` |\n| `updateOption(db, name, value)` | `update_option()` |\n| `getSiteInfo(db)` | `get_bloginfo()` |\n| `getAuthor(db, userId)` | `get_userdata()` |\n\nWP-cased aliases (`get_field`, `wp_query`, `the_field`, etc.) are also exported.\n\n---\n\n## Icon Pack\n\nAll admin UI icons live in `apps/admin/src/lib/icons.ts` and are importable by any admin page or island:\n\n```ts\nimport { adminIcons, getIcon, ICON_NAMES } from \"../lib/icons\";\n\n// In Astro templates\n\u003cFragment set:html={adminIcons.dashboard} /\u003e\n\u003cFragment set:html={getIcon(postType.config.icon)} /\u003e\n\n// For a picker (post type editor)\nICON_NAMES.forEach(name =\u003e adminIcons[name])\n```\n\nIcons: `dashboard`, `post`, `page`, `folder`, `forms`, `fields`, `posttypes`, `taxonomies`, `plugins`, `menus`, `settings`, `user`, `bolt`, `logout`, plus 40+ content-type icons (`book`, `image`, `video`, `music`, `camera`, `globe`, `calendar`, `star`, `chart`, `briefcase`, `users`, …).\n\nAll icons are flat inline SVGs — 24×24 viewBox, `stroke=\"currentColor\"`, stroke-width 1.75, round caps.\n\n---\n\n## Plugin System\n\n1. Create a package in `/plugins/my-plugin/`\n2. Export a plugin config:\n\n```ts\n// plugins/my-plugin/src/index.ts\nimport { definePlugin, registerPostType } from \"@astropress/core\";\n\nexport default definePlugin({\n  name: \"My Plugin\",\n  version: \"1.0.0\",\n  register() {\n    registerPostType(\"product\", {\n      label: \"Product\",\n      pluralLabel: \"Products\",\n      icon: \"bag\",\n      public: true,\n      showInMenu: true,\n      supports: [\"title\", \"editor\", \"thumbnail\", \"custom-fields\"],\n    });\n  },\n});\n```\n\n3. Load it in `apps/admin/src/plugins.ts`:\n\n```ts\nimport myPlugin from \"@astropress/my-plugin\";\nloadPlugin(myPlugin);\n```\n\n---\n\n## Custom Fields (ACF-style)\n\nCustom field groups are created in the admin under **Structure → Custom Fields**. Each group has:\n- Location rules (attach to post type, taxonomy, etc.)\n- Field types: text, textarea, number, select, checkbox, radio, image, file, wysiwyg, repeater, group, flexible content, and more\n- Conditional logic per field\n\nRead values with `getField(db, postId, \"field_name\")` or the `get_field` alias.\n\n---\n\n## Forms\n\nForms are built in the admin under **Forms** using the drag-and-drop form builder. Embed on any page/post by adding the `astropress/form` block in the Gutenberg editor and selecting the form. The block renders server-side — no iframe, no JavaScript required for basic display.\n\nForm entries are viewable in **Forms → Entries**.\n\n---\n\n## Deploy to Cloudflare\n\n### One-click deploy\n\n[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/awsmin/AstroPress)\n\nClicking the button will:\n\n1. Fork this repo to your GitHub account\n2. Create a Cloudflare Pages project linked to the fork\n3. Prompt you to authorise Cloudflare to access your GitHub account\n4. Set up the CI/CD pipeline — every push to `main` triggers a build and deploy\n\n**What you need before clicking:**\n- A [Cloudflare account](https://dash.cloudflare.com/sign-up) (free tier works)\n- A GitHub account\n\n**After the fork is created**, finish the setup in two minutes:\n\n```bash\n# 1. Clone your fork\ngit clone https://github.com/\u003cyour-username\u003e/astropress\ncd astropress\n\n# 2. Create a D1 database and R2 bucket\nnpx wrangler d1 create astropress\nnpx wrangler r2 bucket create astropress-media\n\n# 3. Paste the database_id into both wrangler configs\n#    apps/admin/wrangler.toml  →  [[d1_databases]] database_id = \"...\"\n#    apps/web/wrangler.toml    →  [[d1_databases]] database_id = \"...\"\n\n# 4. Run migrations against production D1\nnpx wrangler d1 execute astropress --remote --file=./migrations/0001_init.sql\n\n# 5. Push — GitHub Actions deploys both apps automatically\ngit push\n```\n\nYour admin will be live at the Cloudflare Pages URL shown in the dashboard.\n\n---\n\n### Manual deploy (CLI only)\n\nIf you prefer not to use the button:\n\n#### 1. Create Cloudflare resources\n\n```bash\nnpx wrangler d1 create astropress\nnpx wrangler r2 bucket create astropress-media\nnpx wrangler pages project create astropress-admin\nnpx wrangler pages project create astropress-web\n```\n\n#### 2. Update wrangler configs\n\nIn `apps/admin/wrangler.toml` and `apps/web/wrangler.toml`:\n- Replace `database_id` with your D1 database ID\n- Set `SITE_URL` to your Pages URL\n\n#### 3. Add GitHub secrets\n\n| Secret | Value |\n|--------|-------|\n| `CLOUDFLARE_API_TOKEN` | API token with Pages + D1 + R2 permissions |\n| `CF_ACCOUNT_ID` | Your Cloudflare account ID |\n\n#### 4. Push to `main`\n\nGitHub Actions builds both apps, runs D1 migrations, and deploys to Cloudflare Pages automatically.\n\n---\n\n## Environment Variables\n\n| Variable | Used in | Description |\n|----------|---------|-------------|\n| `DATABASE_URL` | Both apps (dev) | SQLite URL, e.g. `file:./local.db` |\n| `AUTH_SECRET` | Admin | 32+ char string for session signing |\n| `SITE_URL` | Admin | Full public URL of the web app |\n| `R2_BUCKET` | Admin | R2 bucket name for media uploads |\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/](docs/).\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawsmin%2Fawsmastropress","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawsmin%2Fawsmastropress","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawsmin%2Fawsmastropress/lists"}