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.
- package/README.md +56 -0
- package/dist/index.js +160 -0
- package/package.json +40 -0
- package/templates/base/.dockerignore +44 -0
- package/templates/base/.env.example +38 -0
- package/templates/base/CHANGELOG.md +118 -0
- package/templates/base/Dockerfile +69 -0
- package/templates/base/README.md +147 -0
- package/templates/base/aphex.config.ts +45 -0
- package/templates/base/docker-compose.yml +31 -0
- package/templates/base/drizzle/0000_tiny_redwing.sql +226 -0
- package/templates/base/drizzle/meta/0000_snapshot.json +1379 -0
- package/templates/base/drizzle/meta/_journal.json +13 -0
- package/templates/base/drizzle.config.ts +17 -0
- package/templates/base/package.json +81 -0
- package/templates/base/prod.docker-compose.yml +61 -0
- package/templates/base/src/app.css +32 -0
- package/templates/base/src/app.d.ts +26 -0
- package/templates/base/src/app.html +11 -0
- package/templates/base/src/hooks.server.ts +52 -0
- package/templates/base/src/lib/assets/favicon.svg +5 -0
- package/templates/base/src/lib/auth-client.ts +19 -0
- package/templates/base/src/lib/index.ts +1 -0
- package/templates/base/src/lib/schemaTypes/index.ts +11 -0
- package/templates/base/src/lib/schemaTypes/post.ts +47 -0
- package/templates/base/src/lib/server/auth/better-auth/instance.ts +165 -0
- package/templates/base/src/lib/server/auth/index.ts +25 -0
- package/templates/base/src/lib/server/auth/instance.ts +7 -0
- package/templates/base/src/lib/server/auth/service.ts +486 -0
- package/templates/base/src/lib/server/cache/index.ts +10 -0
- package/templates/base/src/lib/server/db/auth-schema.ts +86 -0
- package/templates/base/src/lib/server/db/cms-schema.ts +23 -0
- package/templates/base/src/lib/server/db/index.ts +55 -0
- package/templates/base/src/lib/server/db/schema.ts +3 -0
- package/templates/base/src/lib/server/email/components/EmailLayout.svelte +24 -0
- package/templates/base/src/lib/server/email/components/EmailVerification.svelte +26 -0
- package/templates/base/src/lib/server/email/components/Invitation.svelte +30 -0
- package/templates/base/src/lib/server/email/components/PasswordReset.svelte +28 -0
- package/templates/base/src/lib/server/email/index.ts +28 -0
- package/templates/base/src/lib/server/email/invitation-hook.ts +56 -0
- package/templates/base/src/lib/server/email/renderer.ts +13 -0
- package/templates/base/src/lib/server/email/templates/email-verification.ts +8 -0
- package/templates/base/src/lib/server/email/templates/invitation.ts +8 -0
- package/templates/base/src/lib/server/email/templates/password-reset.ts +7 -0
- package/templates/base/src/lib/server/services/organization.ts +68 -0
- package/templates/base/src/lib/server/storage/index.ts +28 -0
- package/templates/base/src/lib/stores/activeTab.svelte.ts +14 -0
- package/templates/base/src/lib/utils/imageBuilder.ts +3 -0
- package/templates/base/src/lib/utils/slug.ts +33 -0
- package/templates/base/src/routes/(protected)/admin/+layout.server.ts +86 -0
- package/templates/base/src/routes/(protected)/admin/+layout.svelte +78 -0
- package/templates/base/src/routes/(protected)/admin/+page.server.ts +60 -0
- package/templates/base/src/routes/(protected)/admin/+page.svelte +34 -0
- package/templates/base/src/routes/(protected)/admin/organizations/+page.server.ts +39 -0
- package/templates/base/src/routes/(protected)/admin/organizations/+page.svelte +31 -0
- package/templates/base/src/routes/(protected)/admin/organizations/_components/CreateOrganization.svelte +141 -0
- package/templates/base/src/routes/(protected)/admin/organizations/_components/OrganizationsList.svelte +102 -0
- package/templates/base/src/routes/(protected)/admin/settings/+layout.server.ts +42 -0
- package/templates/base/src/routes/(protected)/admin/settings/+layout.svelte +116 -0
- package/templates/base/src/routes/(protected)/admin/settings/+page.server.ts +16 -0
- package/templates/base/src/routes/(protected)/admin/settings/+page.svelte +32 -0
- package/templates/base/src/routes/(protected)/admin/settings/_components/AccountSettings.svelte +177 -0
- package/templates/base/src/routes/(protected)/admin/settings/_components/ApiKeysSettings.svelte +472 -0
- package/templates/base/src/routes/(protected)/admin/settings/_components/OrganizationsSettings.svelte +182 -0
- package/templates/base/src/routes/(protected)/admin/settings/account/+page.server.ts +25 -0
- package/templates/base/src/routes/(protected)/admin/settings/account/+page.svelte +23 -0
- package/templates/base/src/routes/(protected)/admin/settings/api-keys/+page.server.ts +54 -0
- package/templates/base/src/routes/(protected)/admin/settings/api-keys/+page.svelte +14 -0
- package/templates/base/src/routes/(protected)/admin/settings/members/+page.server.ts +31 -0
- package/templates/base/src/routes/(protected)/admin/settings/members/+page.svelte +338 -0
- package/templates/base/src/routes/(protected)/admin/settings/roles/+page.server.ts +17 -0
- package/templates/base/src/routes/(protected)/admin/settings/roles/+page.svelte +339 -0
- package/templates/base/src/routes/+layout.svelte +12 -0
- package/templates/base/src/routes/+page.svelte +2 -0
- package/templates/base/src/routes/api/[...slug]/+server.ts +35 -0
- package/templates/base/src/routes/api/instance-settings/+server.ts +70 -0
- package/templates/base/src/routes/api/invitations/+server.ts +54 -0
- package/templates/base/src/routes/api/invitations/[id]/accept/+server.ts +54 -0
- package/templates/base/src/routes/api/invitations/[id]/reject/+server.ts +49 -0
- package/templates/base/src/routes/api/settings/api-keys/+server.ts +57 -0
- package/templates/base/src/routes/api/settings/api-keys/[id]/+server.ts +53 -0
- package/templates/base/src/routes/god-mode/+layout.server.ts +32 -0
- package/templates/base/src/routes/god-mode/+layout.svelte +155 -0
- package/templates/base/src/routes/god-mode/+page.server.ts +10 -0
- package/templates/base/src/routes/god-mode/+page.svelte +32 -0
- package/templates/base/src/routes/god-mode/_components/CreateOrganization.svelte +141 -0
- package/templates/base/src/routes/god-mode/_components/OrganizationsList.svelte +169 -0
- package/templates/base/src/routes/god-mode/organizations/+page.server.ts +43 -0
- package/templates/base/src/routes/god-mode/organizations/+page.svelte +74 -0
- package/templates/base/src/routes/invitations/+page.server.ts +48 -0
- package/templates/base/src/routes/invitations/+page.svelte +264 -0
- package/templates/base/src/routes/invite/[token]/+page.server.ts +59 -0
- package/templates/base/src/routes/invite/[token]/+page.svelte +109 -0
- package/templates/base/src/routes/login/+page.server.ts +14 -0
- package/templates/base/src/routes/login/+page.svelte +332 -0
- package/templates/base/src/routes/media/[id]/[filename]/+server.ts +3 -0
- package/templates/base/src/routes/reset-password/[token]/+page.server.ts +15 -0
- package/templates/base/src/routes/reset-password/[token]/+page.svelte +179 -0
- package/templates/base/src/routes/verify-email/+page.svelte +41 -0
- package/templates/base/static/images/aphex-darkmode.png +0 -0
- package/templates/base/static/images/aphex-lightmode.png +0 -0
- package/templates/base/static/robots.txt +3 -0
- package/templates/base/svelte.config.js +19 -0
- package/templates/base/tsconfig.json +20 -0
- 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)
|