create-aphex 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +56 -0
  2. package/dist/index.js +160 -0
  3. package/package.json +40 -0
  4. package/templates/base/.dockerignore +44 -0
  5. package/templates/base/.env.example +38 -0
  6. package/templates/base/CHANGELOG.md +118 -0
  7. package/templates/base/Dockerfile +69 -0
  8. package/templates/base/README.md +147 -0
  9. package/templates/base/aphex.config.ts +45 -0
  10. package/templates/base/docker-compose.yml +31 -0
  11. package/templates/base/drizzle/0000_tiny_redwing.sql +226 -0
  12. package/templates/base/drizzle/meta/0000_snapshot.json +1379 -0
  13. package/templates/base/drizzle/meta/_journal.json +13 -0
  14. package/templates/base/drizzle.config.ts +17 -0
  15. package/templates/base/package.json +81 -0
  16. package/templates/base/prod.docker-compose.yml +61 -0
  17. package/templates/base/src/app.css +32 -0
  18. package/templates/base/src/app.d.ts +26 -0
  19. package/templates/base/src/app.html +11 -0
  20. package/templates/base/src/hooks.server.ts +52 -0
  21. package/templates/base/src/lib/assets/favicon.svg +5 -0
  22. package/templates/base/src/lib/auth-client.ts +19 -0
  23. package/templates/base/src/lib/index.ts +1 -0
  24. package/templates/base/src/lib/schemaTypes/index.ts +11 -0
  25. package/templates/base/src/lib/schemaTypes/post.ts +47 -0
  26. package/templates/base/src/lib/server/auth/better-auth/instance.ts +165 -0
  27. package/templates/base/src/lib/server/auth/index.ts +25 -0
  28. package/templates/base/src/lib/server/auth/instance.ts +7 -0
  29. package/templates/base/src/lib/server/auth/service.ts +486 -0
  30. package/templates/base/src/lib/server/cache/index.ts +10 -0
  31. package/templates/base/src/lib/server/db/auth-schema.ts +86 -0
  32. package/templates/base/src/lib/server/db/cms-schema.ts +23 -0
  33. package/templates/base/src/lib/server/db/index.ts +55 -0
  34. package/templates/base/src/lib/server/db/schema.ts +3 -0
  35. package/templates/base/src/lib/server/email/components/EmailLayout.svelte +24 -0
  36. package/templates/base/src/lib/server/email/components/EmailVerification.svelte +26 -0
  37. package/templates/base/src/lib/server/email/components/Invitation.svelte +30 -0
  38. package/templates/base/src/lib/server/email/components/PasswordReset.svelte +28 -0
  39. package/templates/base/src/lib/server/email/index.ts +28 -0
  40. package/templates/base/src/lib/server/email/invitation-hook.ts +56 -0
  41. package/templates/base/src/lib/server/email/renderer.ts +13 -0
  42. package/templates/base/src/lib/server/email/templates/email-verification.ts +8 -0
  43. package/templates/base/src/lib/server/email/templates/invitation.ts +8 -0
  44. package/templates/base/src/lib/server/email/templates/password-reset.ts +7 -0
  45. package/templates/base/src/lib/server/services/organization.ts +68 -0
  46. package/templates/base/src/lib/server/storage/index.ts +28 -0
  47. package/templates/base/src/lib/stores/activeTab.svelte.ts +14 -0
  48. package/templates/base/src/lib/utils/imageBuilder.ts +3 -0
  49. package/templates/base/src/lib/utils/slug.ts +33 -0
  50. package/templates/base/src/routes/(protected)/admin/+layout.server.ts +86 -0
  51. package/templates/base/src/routes/(protected)/admin/+layout.svelte +78 -0
  52. package/templates/base/src/routes/(protected)/admin/+page.server.ts +60 -0
  53. package/templates/base/src/routes/(protected)/admin/+page.svelte +34 -0
  54. package/templates/base/src/routes/(protected)/admin/organizations/+page.server.ts +39 -0
  55. package/templates/base/src/routes/(protected)/admin/organizations/+page.svelte +31 -0
  56. package/templates/base/src/routes/(protected)/admin/organizations/_components/CreateOrganization.svelte +141 -0
  57. package/templates/base/src/routes/(protected)/admin/organizations/_components/OrganizationsList.svelte +102 -0
  58. package/templates/base/src/routes/(protected)/admin/settings/+layout.server.ts +42 -0
  59. package/templates/base/src/routes/(protected)/admin/settings/+layout.svelte +116 -0
  60. package/templates/base/src/routes/(protected)/admin/settings/+page.server.ts +16 -0
  61. package/templates/base/src/routes/(protected)/admin/settings/+page.svelte +32 -0
  62. package/templates/base/src/routes/(protected)/admin/settings/_components/AccountSettings.svelte +177 -0
  63. package/templates/base/src/routes/(protected)/admin/settings/_components/ApiKeysSettings.svelte +472 -0
  64. package/templates/base/src/routes/(protected)/admin/settings/_components/OrganizationsSettings.svelte +182 -0
  65. package/templates/base/src/routes/(protected)/admin/settings/account/+page.server.ts +25 -0
  66. package/templates/base/src/routes/(protected)/admin/settings/account/+page.svelte +23 -0
  67. package/templates/base/src/routes/(protected)/admin/settings/api-keys/+page.server.ts +54 -0
  68. package/templates/base/src/routes/(protected)/admin/settings/api-keys/+page.svelte +14 -0
  69. package/templates/base/src/routes/(protected)/admin/settings/members/+page.server.ts +31 -0
  70. package/templates/base/src/routes/(protected)/admin/settings/members/+page.svelte +338 -0
  71. package/templates/base/src/routes/(protected)/admin/settings/roles/+page.server.ts +17 -0
  72. package/templates/base/src/routes/(protected)/admin/settings/roles/+page.svelte +339 -0
  73. package/templates/base/src/routes/+layout.svelte +12 -0
  74. package/templates/base/src/routes/+page.svelte +2 -0
  75. package/templates/base/src/routes/api/[...slug]/+server.ts +35 -0
  76. package/templates/base/src/routes/api/instance-settings/+server.ts +70 -0
  77. package/templates/base/src/routes/api/invitations/+server.ts +54 -0
  78. package/templates/base/src/routes/api/invitations/[id]/accept/+server.ts +54 -0
  79. package/templates/base/src/routes/api/invitations/[id]/reject/+server.ts +49 -0
  80. package/templates/base/src/routes/api/settings/api-keys/+server.ts +57 -0
  81. package/templates/base/src/routes/api/settings/api-keys/[id]/+server.ts +53 -0
  82. package/templates/base/src/routes/god-mode/+layout.server.ts +32 -0
  83. package/templates/base/src/routes/god-mode/+layout.svelte +155 -0
  84. package/templates/base/src/routes/god-mode/+page.server.ts +10 -0
  85. package/templates/base/src/routes/god-mode/+page.svelte +32 -0
  86. package/templates/base/src/routes/god-mode/_components/CreateOrganization.svelte +141 -0
  87. package/templates/base/src/routes/god-mode/_components/OrganizationsList.svelte +169 -0
  88. package/templates/base/src/routes/god-mode/organizations/+page.server.ts +43 -0
  89. package/templates/base/src/routes/god-mode/organizations/+page.svelte +74 -0
  90. package/templates/base/src/routes/invitations/+page.server.ts +48 -0
  91. package/templates/base/src/routes/invitations/+page.svelte +264 -0
  92. package/templates/base/src/routes/invite/[token]/+page.server.ts +59 -0
  93. package/templates/base/src/routes/invite/[token]/+page.svelte +109 -0
  94. package/templates/base/src/routes/login/+page.server.ts +14 -0
  95. package/templates/base/src/routes/login/+page.svelte +332 -0
  96. package/templates/base/src/routes/media/[id]/[filename]/+server.ts +3 -0
  97. package/templates/base/src/routes/reset-password/[token]/+page.server.ts +15 -0
  98. package/templates/base/src/routes/reset-password/[token]/+page.svelte +179 -0
  99. package/templates/base/src/routes/verify-email/+page.svelte +41 -0
  100. package/templates/base/static/images/aphex-darkmode.png +0 -0
  101. package/templates/base/static/images/aphex-lightmode.png +0 -0
  102. package/templates/base/static/robots.txt +3 -0
  103. package/templates/base/svelte.config.js +19 -0
  104. package/templates/base/tsconfig.json +20 -0
  105. package/templates/base/vite.config.ts +138 -0
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # create-aphex
2
+
3
+ Scaffold a new Aphex CMS project. Copies the base template into a new
4
+ directory and rewrites `workspace:*` deps to their published versions.
5
+
6
+ ## Usage
7
+
8
+ ```bash
9
+ pnpm create aphex my-app
10
+ # or
11
+ npm create aphex my-app
12
+ # or
13
+ npx create-aphex my-app
14
+ ```
15
+
16
+ You can also invoke it through the [`aphx`](https://www.npmjs.com/package/aphx)
17
+ CLI, which shells out to this package:
18
+
19
+ ```bash
20
+ npx aphx create
21
+ ```
22
+
23
+ ## What it does
24
+
25
+ 1. Prompts for a project name (or takes the positional argument).
26
+ 2. Prompts for a template (currently only `base`).
27
+ 3. Copies `templates/base/` into the new directory.
28
+ 4. Rewrites `workspace:*` dependencies to their published versions.
29
+ 5. Writes a default `.env` with working dev defaults.
30
+
31
+ ## Templates
32
+
33
+ ### base
34
+
35
+ A full-featured Aphex CMS application:
36
+
37
+ - Better Auth (email + password, email verification, password reset)
38
+ - Organizations with parent/child hierarchy
39
+ - PostgreSQL + Drizzle ORM with RLS policies
40
+ - S3-compatible storage (`@aphexcms/storage-s3`) with local-filesystem fallback
41
+ - Mailpit in dev / Resend in prod
42
+ - Auto-generated GraphQL API
43
+ - In-memory cache adapter for published-perspective reads
44
+
45
+ ## After scaffolding
46
+
47
+ ```bash
48
+ cd my-app
49
+ pnpm install
50
+ pnpm db:start # Start PostgreSQL + Mailpit via Docker
51
+ pnpm db:push # Apply the schema (dev)
52
+ pnpm dev # http://localhost:5173
53
+ ```
54
+
55
+ Open `http://localhost:5173/admin` — the first user to sign up becomes the
56
+ super admin.
package/dist/index.js ADDED
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+ import * as p from '@clack/prompts';
3
+ import { existsSync, mkdirSync, cpSync, readFileSync, writeFileSync, readdirSync } from 'fs';
4
+ import { join, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname } from 'path';
7
+ import pc from 'picocolors';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+ const templates = ['base'];
11
+ async function main() {
12
+ console.clear();
13
+ p.intro(pc.bgCyan(pc.black(' create-aphex ')));
14
+ const options = {
15
+ projectName: '',
16
+ template: 'base',
17
+ targetDir: ''
18
+ };
19
+ // Get project name
20
+ const projectName = await p.text({
21
+ message: 'What is your project name?',
22
+ placeholder: 'my-aphex-project',
23
+ validate: (value) => {
24
+ if (!value)
25
+ return 'Project name is required';
26
+ if (!/^[a-z0-9-]+$/.test(value)) {
27
+ return 'Project name can only contain lowercase letters, numbers, and hyphens';
28
+ }
29
+ }
30
+ });
31
+ if (p.isCancel(projectName)) {
32
+ p.cancel('Operation cancelled');
33
+ process.exit(0);
34
+ }
35
+ options.projectName = projectName;
36
+ options.targetDir = resolve(process.cwd(), options.projectName);
37
+ // Check if directory already exists
38
+ if (existsSync(options.targetDir)) {
39
+ const shouldOverwrite = await p.confirm({
40
+ message: `Directory ${pc.cyan(options.projectName)} already exists. Overwrite?`,
41
+ initialValue: false
42
+ });
43
+ if (p.isCancel(shouldOverwrite) || !shouldOverwrite) {
44
+ p.cancel('Operation cancelled');
45
+ process.exit(0);
46
+ }
47
+ }
48
+ options.template = 'base';
49
+ // Start scaffolding
50
+ const spinner = p.spinner();
51
+ spinner.start('Creating your Aphex project...');
52
+ try {
53
+ // Find the templates directory
54
+ // When running from npx, we need to look in the right place
55
+ const templatesDir = findTemplatesDir();
56
+ if (!templatesDir) {
57
+ throw new Error('Could not find templates directory');
58
+ }
59
+ const templatePath = join(templatesDir, options.template);
60
+ if (!existsSync(templatePath)) {
61
+ throw new Error(`Template "${options.template}" not found`);
62
+ }
63
+ // Create target directory
64
+ mkdirSync(options.targetDir, { recursive: true });
65
+ // Copy template files
66
+ cpSync(templatePath, options.targetDir, { recursive: true });
67
+ // Update package.json with project name and resolve workspace: references
68
+ const packageJsonPath = join(options.targetDir, 'package.json');
69
+ if (existsSync(packageJsonPath)) {
70
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
71
+ packageJson.name = options.projectName;
72
+ // Replace workspace:* references with actual package versions
73
+ for (const depType of ['dependencies', 'devDependencies']) {
74
+ const deps = packageJson[depType];
75
+ if (!deps)
76
+ continue;
77
+ for (const [name, version] of Object.entries(deps)) {
78
+ if (typeof version === 'string' && version.startsWith('workspace:')) {
79
+ // Look up the actual version from the package in the monorepo
80
+ const pkgDir = findPackageDir(templatesDir, name);
81
+ if (pkgDir) {
82
+ const pkgJson = JSON.parse(readFileSync(join(pkgDir, 'package.json'), 'utf-8'));
83
+ deps[name] = `^${pkgJson.version}`;
84
+ }
85
+ }
86
+ }
87
+ }
88
+ // Remove private flag and workspace-specific fields
89
+ delete packageJson.private;
90
+ writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, '\t') + '\n');
91
+ }
92
+ // Create .env from .env.example if it doesn't exist
93
+ const envPath = join(options.targetDir, '.env');
94
+ const envExamplePath = join(options.targetDir, '.env.example');
95
+ if (!existsSync(envPath) && existsSync(envExamplePath)) {
96
+ cpSync(envExamplePath, envPath);
97
+ }
98
+ spinner.stop('Project created successfully!');
99
+ // Show next steps
100
+ const nextSteps = [
101
+ `cd ${options.projectName}`,
102
+ 'cp .env.example .env',
103
+ 'pnpm install',
104
+ 'pnpm db:start',
105
+ 'pnpm db:push',
106
+ 'pnpm dev'
107
+ ];
108
+ p.note(nextSteps.join('\n'), 'Next steps');
109
+ p.outro(pc.green('Your Aphex CMS project is ready! Check out the README.md for more information.'));
110
+ }
111
+ catch (error) {
112
+ spinner.stop('Failed to create project');
113
+ p.log.error(pc.red(error.message));
114
+ process.exit(1);
115
+ }
116
+ }
117
+ function findPackageDir(templatesDir, packageName) {
118
+ // Templates sit alongside packages/ in the monorepo, or the packages
119
+ // are bundled relative to the templates dir in the npm package
120
+ const possibleRoots = [
121
+ resolve(templatesDir, '..'), // monorepo root (templates/ -> ../)
122
+ resolve(templatesDir, '../..') // npm package (dist/templates -> ../../)
123
+ ];
124
+ for (const root of possibleRoots) {
125
+ const packagesDir = join(root, 'packages');
126
+ if (!existsSync(packagesDir))
127
+ continue;
128
+ for (const dir of readdirSync(packagesDir)) {
129
+ const pkgJsonPath = join(packagesDir, dir, 'package.json');
130
+ if (existsSync(pkgJsonPath)) {
131
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
132
+ if (pkgJson.name === packageName) {
133
+ return join(packagesDir, dir);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+ function findTemplatesDir() {
141
+ // Try different possible locations for the templates directory
142
+ const possiblePaths = [
143
+ // When installed via npm/npx (templates are bundled in package)
144
+ resolve(__dirname, '../templates'),
145
+ // When running from built dist in package
146
+ resolve(__dirname, '../../templates'),
147
+ // When running from source in monorepo
148
+ resolve(__dirname, '../../../templates')
149
+ ];
150
+ for (const path of possiblePaths) {
151
+ if (existsSync(path)) {
152
+ return path;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+ main().catch((error) => {
158
+ console.error('Error:', error);
159
+ process.exit(1);
160
+ });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "create-aphex",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold a new Aphex CMS project — invoked via `pnpm create aphex`",
5
+ "publishConfig": {},
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/IcelandicIcecream/aphex.git",
9
+ "directory": "packages/create-aphex"
10
+ },
11
+ "type": "module",
12
+ "bin": {
13
+ "create-aphex": "./dist/index.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "templates"
18
+ ],
19
+ "scripts": {
20
+ "build": "node scripts/copy-templates.js && tsc",
21
+ "prepublishOnly": "pnpm build"
22
+ },
23
+ "keywords": [
24
+ "aphex",
25
+ "cms",
26
+ "cli",
27
+ "scaffolding",
28
+ "template"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@clack/prompts": "^0.11.0",
34
+ "picocolors": "^1.1.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.0.0",
38
+ "typescript": "^5.9.3"
39
+ }
40
+ }
@@ -0,0 +1,44 @@
1
+ # Build artifacts
2
+ build
3
+ node_modules
4
+ .svelte-kit
5
+ dist
6
+
7
+ # Environment files
8
+ .env
9
+ .env.*
10
+ !.env.example
11
+ !.env.docker
12
+
13
+ # OS files
14
+ .DS_Store
15
+ Thumbs.db
16
+
17
+ # Logs
18
+ *.log
19
+ npm-debug.log*
20
+ yarn-debug.log*
21
+ yarn-error.log*
22
+ pnpm-debug.log*
23
+
24
+ # Storage
25
+ storage/assets/
26
+
27
+ # IDE
28
+ .vscode
29
+ .idea
30
+ *.swp
31
+ *.swo
32
+
33
+ # Documentation
34
+ README.md
35
+ CONTRIBUTING.md
36
+ LICENSE
37
+
38
+ # Git
39
+ .git
40
+ .gitignore
41
+
42
+ # Testing
43
+ coverage
44
+ .nyc_output
@@ -0,0 +1,38 @@
1
+ # ============================
2
+ # Database Configuration
3
+ # ============================
4
+
5
+ ## Option (1): Using a full DATABASE_URL
6
+ ## -------------------------------------
7
+ DATABASE_URL="postgres://root:mysecretpassword@localhost:5432/local"
8
+
9
+ ## Option (2): Using individual connection settings
10
+ ## -----------------------------------------------
11
+ ## Uncomment and set these if not using DATABASE_URL
12
+ # PG_HOST=localhost
13
+ # PG_PORT=5432
14
+ # PG_USER=root
15
+ # PG_PASSWORD=mysecretpassword
16
+ # PG_DATABASE=local
17
+
18
+ # ============================
19
+ # Auth Configuration
20
+ # ============================
21
+ # BETTER_AUTH_SECRET / BETTER_AUTH_URL also work for backwards compatibility
22
+ BETTER_AUTH_SECRET=your-secret-key-here-change-in-production
23
+ BETTER_AUTH_URL=http://localhost:5173
24
+ AUTH_TRUSTED_ORIGINS=http://localhost:5173
25
+
26
+ # ============================
27
+ # Resend Email Configuration (optional)
28
+ # ============================
29
+ RESEND_API_KEY=re_your_api_key_here
30
+
31
+ # ============================
32
+ # S3_COMPATIBLE CONFIGURATION (optional)
33
+ # ============================
34
+ R2_ENDPOINT=
35
+ R2_ACCESS_KEY_ID=
36
+ R2_SECRET_ACCESS_KEY=
37
+ R2_BUCKET=
38
+ R2_PUBLIC_URL=
@@ -0,0 +1,118 @@
1
+ ---
2
+ **Heads up:** This project was scaffolded from `@aphexcms/base` `v0.0.2`.
3
+ When upgrading, read `CHANGELOG.md` in the template repo for notes on
4
+ what changed upstream and which files you may want to port into your
5
+ customized project.
6
+ ---
7
+
8
+ # AphexCMS Template Changelog
9
+
10
+ Notes for users upgrading an existing project scaffolded from this template.
11
+ Because the template is meant to be customized, changes here are **not**
12
+ automatically applied to your project — this file describes what changed
13
+ upstream so you can cherry-pick the bits you care about.
14
+
15
+ Format: each entry lists the files touched and a one-line reason. Use
16
+ `git diff` against the mirror repo (`IcelandicIcecream/aphex-base`) at the
17
+ tag matching the version you started from to see the exact changes.
18
+
19
+ ## Unreleased
20
+
21
+ - **feat(api): move invitation email-wrap into `aphex.config.ts → api`**
22
+ - Deleted: `src/routes/api/organizations/invitations/+server.ts` —
23
+ the SK shim that wrapped the built-in invite handler to send email.
24
+ - Added: `src/lib/server/email/invitation-hook.ts` — same logic, now a
25
+ Hono middleware registered via `config.api`. Runs BEFORE built-in
26
+ routes mount (Hono is registration-order-strict, so middleware needs
27
+ to register first to wrap a downstream handler).
28
+ - `aphex.config.ts` now passes `api: (app) => registerInvitationEmailHook(app)`.
29
+ - Why: lets us drop the last invitation-related entry from
30
+ `routes-exports.ts` and the matching SK route file in cms-core. The
31
+ only remaining `routes-exports` entry is `serveAssetCDN` (CDN URLs
32
+ live outside `/api` so can't move onto the catch-all).
33
+ - Upgrade: copy `src/lib/server/email/invitation-hook.ts` and the new
34
+ `api:` block in `aphex.config.ts`; delete your old
35
+ `src/routes/api/organizations/invitations/+server.ts`.
36
+
37
+ - **feat(api): replace per-endpoint `+server.ts` shims with a single Hono catch-all**
38
+ - Added: `src/routes/api/[...slug]/+server.ts` — forwards any unmatched
39
+ `/api/**` request to the Aphex Hono app on `event.locals.aphexCMS.apiApp`.
40
+ - Deleted (24 files) — all CMS-feature shims that just re-exported handlers
41
+ from `@aphexcms/cms-core/server`. They're now served by the catch-all:
42
+ - `src/routes/api/{schemas,documents,assets,organizations,roles,user}/+server.ts`
43
+ - `src/routes/api/schemas/[type]/+server.ts`
44
+ - `src/routes/api/documents/{query,[id]}/+server.ts`
45
+ - `src/routes/api/documents/[id]/{publish,versions}/+server.ts`
46
+ - `src/routes/api/documents/[id]/versions/[version]/+server.ts`
47
+ - `src/routes/api/documents/[id]/versions/[version]/restore/+server.ts`
48
+ - `src/routes/api/assets/{bulk,[id]}/+server.ts`
49
+ - `src/routes/api/assets/[id]/references/+server.ts`
50
+ - `src/routes/api/assets/references/counts/+server.ts`
51
+ - `src/routes/api/organizations/{switch,members,[id]}/+server.ts`
52
+ - `src/routes/api/roles/[name]/+server.ts`
53
+ - `src/routes/api/user/{cms-preference,reset-password,request-password-reset}/+server.ts`
54
+ - Kept (studio-locals — your own endpoints): `instance-settings`,
55
+ `invitations`, `invitations/[id]/{accept,reject}`,
56
+ `organizations/invitations`, `settings/api-keys`, `settings/api-keys/[id]`.
57
+ - Why: SvelteKit prefers specific routes over the catch-all, so any
58
+ `+server.ts` you keep wins. Custom endpoints can still go in
59
+ `src/routes/**/+server.ts` as before, OR be registered onto the Hono
60
+ app via `aphex.config.ts → api: (app) => { app.post(...) }`.
61
+ - Upgrade: copy `src/routes/api/[...slug]/+server.ts` from the template,
62
+ then delete any of the 24 shims you haven't customized. If you
63
+ customized one, leave it — it'll continue to win over the catch-all.
64
+
65
+ ## 0.0.2
66
+
67
+ - **chore(deps): bump `@aphexcms/cms-core` to `^2.1.2` and `@aphexcms/ui` to `^0.3.4`**
68
+ - Fixes ESM resolution of the `schema-context.svelte` rune module
69
+ (`ERR_MODULE_NOT_FOUND`) and adds the `svelte` export condition on
70
+ subpath exports (`/client`, `/server`, `/schema`, `/routes/*`, etc.) so
71
+ SvelteKit's Vite plugin claims them instead of Node's default loader
72
+ trying to import raw `.svelte` files (`ERR_UNKNOWN_FILE_EXTENSION`).
73
+ - No source changes required in the template — reinstall to pick it up.
74
+
75
+ - **chore(styles): drop `@import '@aphexcms/ui/themes/aphex'` from `src/app.css`**
76
+ - The `@aphexcms/ui/themes/aphex` theme file was removed from
77
+ `@aphexcms/ui@0.3.4`. If you kept that import in your own `app.css`,
78
+ delete the line when you upgrade or the build will fail to resolve it.
79
+
80
+ - **fix(admin): delete-asset modal + document-editor header + boolean autosave**
81
+ - All three fixes live in `@aphexcms/cms-core`; upgrading the dep is enough.
82
+ - Long asset filenames no longer stretch the delete confirm dialog.
83
+ - Document editor top-row actions are vertically centered.
84
+ - Autosave compares against an initial-defaults snapshot — unchecking a
85
+ boolean now saves, and fields with `initialValue: true` no longer
86
+ auto-create an empty doc on mount.
87
+
88
+ - **chore(drizzle): regenerate initial migration**
89
+ - `drizzle/0000_tiny_redwing.sql`, `drizzle/meta/0000_snapshot.json`, `drizzle/meta/_journal.json`
90
+ - Template migration was stale vs. the current schema (missing
91
+ `cms_document_versions`, `cms_instance_settings`, `cms_roles`, the
92
+ `version_event` enum, and the `unpublished` document status).
93
+ - If you've already run `db:push` against your project DB you're fine;
94
+ users starting fresh from this template will now get the full schema.
95
+
96
+ - **chore(admin): comment out `<PermissionsDebug />`**
97
+ - `src/routes/(protected)/admin/+layout.svelte`
98
+ - Debug overlay disabled by default in the shipped template.
99
+
100
+ <!--
101
+ Example entry:
102
+
103
+ - **fix(members): reject self-invitation and duplicate members**
104
+ - `src/routes/(protected)/admin/settings/members/+page.svelte`
105
+ - Handled server-side in cms-core; UI unchanged. Safe to skip if you
106
+ haven't customized the invite flow.
107
+
108
+ - **feat(versions): add document versions API routes**
109
+ - `src/routes/api/documents/[id]/versions/+server.ts`
110
+ - `src/routes/api/documents/[id]/versions/[version]/+server.ts`
111
+ - `src/routes/api/documents/[id]/versions/[version]/restore/+server.ts`
112
+ - Clean re-exports from `@aphexcms/cms-core/server`. Port these over to
113
+ get version history in your admin UI.
114
+ -->
115
+
116
+ ## 0.0.1
117
+
118
+ - Initial template.
@@ -0,0 +1,69 @@
1
+ # Build stage
2
+ FROM node:20-alpine AS builder
3
+
4
+ # Install pnpm
5
+ RUN npm install -g pnpm
6
+
7
+ WORKDIR /app
8
+
9
+ # Copy workspace files
10
+ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
11
+ COPY turbo.json ./
12
+ COPY tsconfig.json ./
13
+
14
+ # Copy all packages and apps
15
+ COPY packages ./packages
16
+ COPY apps/studio ./apps/studio
17
+
18
+ # Install dependencies
19
+ RUN pnpm install --frozen-lockfile
20
+
21
+ # Build arguments for environment variables needed at build time
22
+ ARG ACCESS_API_KEY
23
+ ARG GITHUB_TOKEN
24
+ ARG RESEND_API_KEY
25
+ ARG AUTH_SECRET
26
+ ARG AUTH_URL
27
+ ARG DATABASE_URL
28
+
29
+ # Set as environment variables for the build
30
+ ENV ACCESS_API_KEY=$ACCESS_API_KEY
31
+ ENV GITHUB_TOKEN=$GITHUB_TOKEN
32
+ ENV RESEND_API_KEY=$RESEND_API_KEY
33
+ ENV AUTH_SECRET=$AUTH_SECRET
34
+ ENV AUTH_URL=$AUTH_URL
35
+ ENV DATABASE_URL=$DATABASE_URL
36
+
37
+ # Build the application
38
+ WORKDIR /app/apps/studio
39
+ RUN pnpm build
40
+
41
+ # Production stage
42
+ FROM node:20-alpine AS runner
43
+
44
+ # Copy workspace root files first
45
+ COPY --from=builder /app/package.json /app/package.json
46
+ COPY --from=builder /app/pnpm-workspace.yaml /app/pnpm-workspace.yaml
47
+ COPY --from=builder /app/node_modules /app/node_modules
48
+ COPY --from=builder /app/packages /app/packages
49
+
50
+ # Set working directory for the app
51
+ WORKDIR /app/apps/studio
52
+
53
+ # Copy studio app files
54
+ COPY --from=builder /app/apps/studio/build ./build
55
+ COPY --from=builder /app/apps/studio/package.json ./package.json
56
+ COPY --from=builder /app/apps/studio/node_modules ./node_modules
57
+ COPY --from=builder /app/apps/studio/drizzle ./drizzle
58
+ COPY --from=builder /app/apps/studio/drizzle.config.ts ./drizzle.config.ts
59
+
60
+ # Expose port
61
+ EXPOSE 3000
62
+
63
+ # Set environment to production
64
+ ENV NODE_ENV=production
65
+ ENV PORT=3000
66
+ ENV NODE_PATH=/app/node_modules
67
+
68
+ # Start the application
69
+ CMD ["node", "build"]
@@ -0,0 +1,147 @@
1
+ # Aphex CMS
2
+
3
+ A clean starting point for building with Aphex CMS. No example schemas — just the wiring.
4
+
5
+ > This directory is mirrored to [**IcelandicIcecream/aphex-base**](https://github.com/IcelandicIcecream/aphex-base) on every change, so you can clone it directly as a standalone project:
6
+ >
7
+ > ```bash
8
+ > git clone https://github.com/IcelandicIcecream/aphex-base my-app
9
+ > cd my-app && pnpm install
10
+ > ```
11
+ >
12
+ > Or scaffold via the CLI: `npx aphx create`.
13
+
14
+ ## Getting Started
15
+
16
+ ### 1. Install Dependencies
17
+
18
+ ```bash
19
+ pnpm install
20
+ ```
21
+
22
+ ### 2. Set Up Environment Variables
23
+
24
+ Copy the example file and update as needed:
25
+
26
+ ```bash
27
+ cp .env.example .env
28
+ ```
29
+
30
+ ### 3. Start PostgreSQL Database
31
+
32
+ ```bash
33
+ pnpm db:start
34
+ ```
35
+
36
+ ### 4. Run Database Migrations
37
+
38
+ ```bash
39
+ pnpm db:migrate
40
+ ```
41
+
42
+ ### 5. Start Development Server
43
+
44
+ ```bash
45
+ pnpm dev
46
+ ```
47
+
48
+ Your application will be available at `http://localhost:5173`
49
+
50
+ ### 6. First Login
51
+
52
+ 1. Go to `http://localhost:5173/login`
53
+ 2. Sign up with your email and password — the first user automatically becomes the super admin with a default organization
54
+ 3. Access God Mode at `/god-mode` for instance-level administration
55
+
56
+ ## Defining Content Schemas
57
+
58
+ Add your content schemas in `src/lib/schemaTypes/`. Two types are available:
59
+
60
+ - **`document`** — Top-level entities (e.g. Page, Post, Product)
61
+ - **`object`** — Reusable nested structures (e.g. SEO, Hero)
62
+
63
+ Example:
64
+
65
+ ```typescript
66
+ // src/lib/schemaTypes/post.ts
67
+ import type { SchemaType } from '@aphexcms/cms-core';
68
+
69
+ const post: SchemaType = {
70
+ type: 'document',
71
+ name: 'post',
72
+ title: 'Post',
73
+ fields: [
74
+ {
75
+ name: 'title',
76
+ type: 'string',
77
+ title: 'Title',
78
+ validation: (Rule) => Rule.required()
79
+ },
80
+ {
81
+ name: 'slug',
82
+ type: 'slug',
83
+ title: 'Slug',
84
+ source: 'title'
85
+ },
86
+ {
87
+ name: 'body',
88
+ type: 'text',
89
+ title: 'Body'
90
+ }
91
+ ]
92
+ };
93
+
94
+ export default post;
95
+ ```
96
+
97
+ Then register it in `src/lib/schemaTypes/index.ts`:
98
+
99
+ ```typescript
100
+ import post from './post.js';
101
+
102
+ export const schemaTypes = [post];
103
+ ```
104
+
105
+ Available field types: `string`, `text`, `number`, `boolean`, `slug`, `image`, `date`, `datetime`, `url`, `array`, `object`, `reference`
106
+
107
+ ## Available Scripts
108
+
109
+ - `pnpm dev` — Start the development server
110
+ - `pnpm build` — Build for production
111
+ - `pnpm preview` — Preview production build
112
+ - `pnpm db:start` — Start PostgreSQL via Docker
113
+ - `pnpm db:migrate` — Run database migrations
114
+ - `pnpm db:push` — Push schema changes (dev only)
115
+ - `pnpm db:generate` — Generate migration files
116
+ - `pnpm db:studio` — Open Drizzle Studio
117
+
118
+ ## Project Structure
119
+
120
+ ```
121
+ .
122
+ ├── src/
123
+ │ ├── lib/
124
+ │ │ ├── server/
125
+ │ │ │ ├── auth/ # Authentication (Better Auth)
126
+ │ │ │ ├── db/ # Database connection and schema
127
+ │ │ │ ├── email/ # Email templates and adapter
128
+ │ │ │ ├── services/ # Business logic
129
+ │ │ │ └── storage/ # File storage adapter
130
+ │ │ ├── schemaTypes/ # Your content type definitions
131
+ │ │ └── utils/ # Utility functions
132
+ │ └── routes/
133
+ │ ├── (protected)/admin/ # Admin panel
134
+ │ ├── api/ # API endpoints
135
+ │ ├── god-mode/ # Super admin panel
136
+ │ └── login/ # Authentication
137
+ ├── aphex.config.ts # CMS configuration
138
+ ├── drizzle/ # Database migrations
139
+ └── docker-compose.yml # PostgreSQL setup
140
+ ```
141
+
142
+ ## Learn More
143
+
144
+ - [Aphex CMS](https://github.com/IcelandicIcecream/aphex)
145
+ - [SvelteKit](https://kit.svelte.dev)
146
+ - [Drizzle ORM](https://orm.drizzle.team)
147
+ - [Better Auth](https://better-auth.com)