create-vexcms 0.0.19 → 0.1.0-alpha.2
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/LICENSE +202 -0
- package/README.md +93 -83
- package/dist/index.js +246 -81
- package/dist/index.mjs +30976 -0
- package/package.json +35 -15
- package/templates/base-nextjs/README.md +53 -205
- package/templates/base-nextjs/_env.example +7 -9
- package/templates/base-nextjs/_gitignore +41 -1
- package/templates/base-nextjs/convex/_generated/api.d.ts +6 -62
- package/templates/base-nextjs/convex/_generated/server.d.ts +19 -0
- package/templates/base-nextjs/convex/_generated/server.js +8 -0
- package/templates/base-nextjs/convex/auth/api.ts +49 -3
- package/templates/base-nextjs/convex/auth/db.ts +42 -300
- package/templates/base-nextjs/convex/auth/index.ts +11 -7
- package/templates/base-nextjs/convex/auth/options.ts +5 -29
- package/templates/base-nextjs/convex/auth/plugins/index.ts +15 -5
- package/templates/base-nextjs/convex/auth/sessions.ts +9 -15
- package/templates/base-nextjs/convex/http.ts +18 -3
- package/templates/base-nextjs/convex/schema.ts +104 -2
- package/templates/base-nextjs/convex/vex/firstUser.ts +10 -84
- package/templates/base-nextjs/convex/vex/globals.ts +19 -82
- package/templates/base-nextjs/convex/vex/media.ts +19 -246
- package/templates/base-nextjs/convex/vex.schema.ts +138 -0
- package/templates/base-nextjs/convex/vex.ts +19 -0
- package/templates/base-nextjs/convex.json +6 -0
- package/templates/base-nextjs/eslint.config.mjs +1 -21
- package/templates/base-nextjs/next.config.ts +10 -0
- package/templates/base-nextjs/package.json +47 -48
- package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +17 -5
- package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +2 -4
- package/templates/base-nextjs/src/app/(frontend)/layout.tsx +12 -2
- package/templates/base-nextjs/src/app/(frontend)/page.tsx +2 -113
- package/templates/base-nextjs/src/app/(vexcms)/admin/[[...path]]/page.tsx +40 -0
- package/templates/base-nextjs/src/app/(vexcms)/admin/clientProviders.tsx +22 -0
- package/templates/base-nextjs/src/app/(vexcms)/admin/layout.tsx +19 -0
- package/templates/base-nextjs/src/app/globals.css +240 -104
- package/templates/base-nextjs/src/app/layout.tsx +23 -13
- package/templates/base-nextjs/src/app/unauthorized/page.tsx +24 -0
- package/templates/base-nextjs/src/auth/access.ts +46 -0
- package/templates/base-nextjs/src/auth/client.tsx +17 -5
- package/templates/base-nextjs/src/auth/hasPermission.ts +39 -0
- package/templates/base-nextjs/src/auth/serverUtils.ts +15 -22
- package/templates/base-nextjs/src/components/WelcomePage.tsx +116 -0
- package/templates/base-nextjs/src/components/component-example.tsx +56 -464
- package/templates/base-nextjs/src/components/providers/auth.tsx +13 -0
- package/templates/base-nextjs/src/components/providers/client.tsx +6 -1
- package/templates/base-nextjs/src/components/providers/convex.tsx +53 -21
- package/templates/base-nextjs/src/components/providers/server.tsx +10 -7
- package/templates/{marketing-site → base-nextjs}/src/components/ui/accordion.tsx +30 -32
- package/templates/base-nextjs/src/components/ui/badge.tsx +10 -13
- package/templates/base-nextjs/src/components/ui/input.tsx +35 -5
- package/templates/base-nextjs/src/context/AuthContext.tsx +29 -0
- package/templates/base-nextjs/src/db/constants/auth.ts +0 -1
- package/templates/base-nextjs/src/db/constants/index.ts +22 -35
- package/templates/base-nextjs/src/lib/utils.ts +0 -12
- package/templates/base-nextjs/src/proxy.ts +98 -11
- package/templates/base-nextjs/src/vex.config.ts +35 -0
- package/templates/base-nextjs/src/vex.types.ts +293 -0
- package/templates/base-nextjs/src/vexcms/collections/images.ts +15 -0
- package/templates/base-nextjs/src/vexcms/collections/index.ts +2 -1
- package/templates/base-nextjs/src/vexcms/collections/users.ts +7 -35
- package/templates/base-nextjs/tsconfig.json +3 -0
- package/templates/marketing-site/convex/_generated/api.d.ts +81 -0
- package/templates/marketing-site/convex/_generated/api.js +23 -0
- package/templates/marketing-site/convex/_generated/dataModel.d.ts +60 -0
- package/templates/marketing-site/convex/_generated/server.d.ts +162 -0
- package/templates/marketing-site/convex/_generated/server.js +101 -0
- package/templates/marketing-site/convex/footers.ts +11 -7
- package/templates/marketing-site/convex/headers.ts +15 -5
- package/templates/marketing-site/convex/pages.ts +20 -35
- package/templates/marketing-site/convex/schema.ts +119 -0
- package/templates/marketing-site/convex/seed.ts +582 -35
- package/templates/marketing-site/convex/siteSettings.ts +19 -5
- package/templates/marketing-site/convex/theme.ts +55 -61
- package/templates/marketing-site/convex/vex.schema.ts +355 -0
- package/templates/marketing-site/src/app/(frontend)/PageContent.tsx +27 -160
- package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +6 -9
- package/templates/marketing-site/src/app/(frontend)/layout.tsx +4 -2
- package/templates/marketing-site/src/app/(frontend)/page.tsx +2 -5
- package/templates/marketing-site/src/components/SiteFooter.tsx +6 -3
- package/templates/marketing-site/src/components/SiteHeader.tsx +6 -3
- package/templates/marketing-site/src/components/ThemeLive.tsx +59 -0
- package/templates/marketing-site/src/components/ThemeStyle.tsx +45 -89
- package/templates/marketing-site/src/components/motion-primitives/animated-group.tsx +2 -2
- package/templates/marketing-site/src/lib/metadata.ts +51 -72
- package/templates/marketing-site/src/vex.config.ts +35 -0
- package/templates/marketing-site/src/vex.types.ts +634 -0
- package/templates/marketing-site/src/vexcms/api.ts +23 -0
- package/templates/marketing-site/src/vexcms/blocks/CTA/config.ts +3 -3
- package/templates/marketing-site/src/vexcms/blocks/CTA/index.tsx +2 -2
- package/templates/marketing-site/src/vexcms/blocks/FAQ/config.ts +4 -4
- package/templates/marketing-site/src/vexcms/blocks/FAQ/index.tsx +2 -2
- package/templates/marketing-site/src/vexcms/blocks/Features/config.ts +4 -8
- package/templates/marketing-site/src/vexcms/blocks/Features/index.tsx +2 -2
- package/templates/marketing-site/src/vexcms/blocks/Footer/config.ts +7 -11
- package/templates/marketing-site/src/vexcms/blocks/Footer/index.tsx +1 -1
- package/templates/marketing-site/src/vexcms/blocks/Header/config.ts +9 -9
- package/templates/marketing-site/src/vexcms/blocks/Header/index.tsx +4 -5
- package/templates/marketing-site/src/vexcms/blocks/Hero/config.ts +3 -4
- package/templates/marketing-site/src/vexcms/blocks/Hero/index.tsx +10 -19
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/config.ts +4 -9
- package/templates/marketing-site/src/vexcms/blocks/HowItWorks/index.tsx +3 -6
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/config.ts +28 -68
- package/templates/marketing-site/src/vexcms/blocks/Roadmap/index.tsx +13 -43
- package/templates/marketing-site/src/vexcms/blocks/config.ts +7 -22
- package/templates/marketing-site/src/vexcms/blocks/constants.ts +2 -3
- package/templates/marketing-site/src/vexcms/blocks/index.ts +9 -8
- package/templates/marketing-site/src/vexcms/collections/footers.ts +1 -1
- package/templates/marketing-site/src/vexcms/collections/headers.ts +1 -1
- package/templates/marketing-site/src/vexcms/collections/index.ts +2 -1
- package/templates/marketing-site/src/vexcms/collections/pages.ts +12 -21
- package/templates/marketing-site/src/vexcms/collections/themeColors.ts +118 -0
- package/templates/marketing-site/src/vexcms/collections/themes.ts +29 -111
- package/templates/marketing-site/src/vexcms/globals/index.ts +1 -1
- package/templates/marketing-site/src/vexcms/globals/siteSettings.ts +43 -27
- package/templates/base-nextjs/convex/auth/adapter/index.ts +0 -230
- package/templates/base-nextjs/convex/auth/adapter/utils.ts +0 -547
- package/templates/base-nextjs/convex/auth/config.ts +0 -10
- package/templates/base-nextjs/convex/vex/auth.ts +0 -38
- package/templates/base-nextjs/convex/vex/collections.ts +0 -321
- package/templates/base-nextjs/convex/vex/helpers.ts +0 -33
- package/templates/base-nextjs/convex/vex/impersonation.ts +0 -51
- package/templates/base-nextjs/convex/vex/migrate.ts +0 -74
- package/templates/base-nextjs/convex/vex/model/collections.ts +0 -196
- package/templates/base-nextjs/convex/vex/model/media.ts +0 -87
- package/templates/base-nextjs/convex/vex/model/versions.ts +0 -254
- package/templates/base-nextjs/convex/vex/previewSnapshot.ts +0 -53
- package/templates/base-nextjs/convex/vex/versions.ts +0 -648
- package/templates/base-nextjs/src/app/admin/AdminLayoutWrapper.tsx +0 -33
- package/templates/base-nextjs/src/app/admin/AdminPageWrapper.tsx +0 -38
- package/templates/base-nextjs/src/app/admin/RichTextFieldWithMedia.tsx +0 -101
- package/templates/base-nextjs/src/app/admin/[[...path]]/page.tsx +0 -15
- package/templates/base-nextjs/src/app/admin/layout.tsx +0 -51
- package/templates/base-nextjs/src/app/favicon.ico +0 -0
- package/templates/base-nextjs/src/components/providers/theme.tsx +0 -11
- package/templates/base-nextjs/src/components/ui/theme-toggle.tsx +0 -67
- package/templates/base-nextjs/src/db/types.ts +0 -6
- package/templates/base-nextjs/src/vexcms/access.ts +0 -44
- package/templates/base-nextjs/src/vexcms/auth.ts +0 -4
- package/templates/base-nextjs/src/vexcms/collections/media.ts +0 -15
- package/templates/base-nextjs/vex.config.ts +0 -25
- package/templates/marketing-site/public/favicons/favicon.ico +0 -0
- package/templates/marketing-site/src/app/(frontend)/preview/[slug]/PreviewPageContent.tsx +0 -55
- package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +0 -27
- package/templates/marketing-site/src/app/(frontend)/preview/layout.tsx +0 -18
- package/templates/marketing-site/src/app/admin/AdminLayoutWrapper.tsx +0 -71
- package/templates/marketing-site/src/components/ThemeInjector.tsx +0 -128
- package/templates/marketing-site/src/components/admin/IconPickerField.tsx +0 -134
- package/templates/marketing-site/src/components/admin/ThemeImport.tsx +0 -174
- package/templates/marketing-site/src/components/admin/ThemeImportField.tsx +0 -33
- package/templates/marketing-site/src/db/constants/index.ts +0 -52
- package/templates/marketing-site/src/lib/colorConvert.ts +0 -171
- package/templates/marketing-site/src/vexcms/access.ts +0 -36
- package/templates/marketing-site/vex.config.ts +0 -32
package/package.json
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-vexcms",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"description": "Scaffold a new VexCMS project",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cms",
|
|
7
|
+
"headless-cms",
|
|
8
|
+
"convex",
|
|
9
|
+
"typescript",
|
|
10
|
+
"create",
|
|
11
|
+
"scaffold"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"author": "Isaiah Anyimi",
|
|
15
|
+
"homepage": "https://docs.vexcms.dev",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/ianyimi/vex.git",
|
|
19
|
+
"directory": "packages/create-vexcms"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
4
22
|
"type": "module",
|
|
5
23
|
"bin": {
|
|
6
24
|
"create-vexcms": "./dist/index.js"
|
|
@@ -10,24 +28,26 @@
|
|
|
10
28
|
"templates"
|
|
11
29
|
],
|
|
12
30
|
"publishConfig": {
|
|
13
|
-
"access": "public"
|
|
31
|
+
"access": "public",
|
|
32
|
+
"tag": "alpha"
|
|
14
33
|
},
|
|
15
34
|
"dependencies": {
|
|
16
|
-
"chalk": "
|
|
17
|
-
"commander": "
|
|
18
|
-
"execa": "
|
|
19
|
-
"fs-extra": "
|
|
20
|
-
"@inquirer/prompts": "
|
|
21
|
-
"ora": "
|
|
22
|
-
"sort-package-json": "
|
|
23
|
-
"validate-npm-package-name": "
|
|
35
|
+
"chalk": "5.3.0",
|
|
36
|
+
"commander": "12.1.0",
|
|
37
|
+
"execa": "9.6.1",
|
|
38
|
+
"fs-extra": "11.3.6",
|
|
39
|
+
"@inquirer/prompts": "7.10.1",
|
|
40
|
+
"ora": "8.1.0",
|
|
41
|
+
"sort-package-json": "2.10.0",
|
|
42
|
+
"validate-npm-package-name": "5.0.0",
|
|
43
|
+
"yaml": "2.9.0"
|
|
24
44
|
},
|
|
25
45
|
"devDependencies": {
|
|
26
|
-
"@types/fs-extra": "
|
|
27
|
-
"@types/validate-npm-package-name": "
|
|
28
|
-
"tsup": "
|
|
29
|
-
"typescript": "
|
|
30
|
-
"vitest": "
|
|
46
|
+
"@types/fs-extra": "11.0.4",
|
|
47
|
+
"@types/validate-npm-package-name": "4.0.2",
|
|
48
|
+
"tsup": "8.5.1",
|
|
49
|
+
"typescript": "6.0.3",
|
|
50
|
+
"vitest": "4.1.10"
|
|
31
51
|
},
|
|
32
52
|
"scripts": {
|
|
33
53
|
"build": "tsup src/index.ts --format esm --dts",
|
|
@@ -1,256 +1,104 @@
|
|
|
1
|
-
#
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
- **[Next.js](https://nextjs.org)** - The React framework for production with App Router
|
|
6
|
-
- **[Better Auth](https://www.better-auth.com/)** - Modern authentication with email/password and OAuth providers
|
|
7
|
-
- **[Convex](https://convex.dev/)** - Real-time database and backend
|
|
8
|
-
- **[shadcn/ui](https://ui.shadcn.com/)** - Beautifully designed components built on Radix UI
|
|
9
|
-
- **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework
|
|
3
|
+
A VexCMS project built with Next.js, Better Auth, and Convex.
|
|
10
4
|
|
|
11
5
|
## Getting Started
|
|
12
6
|
|
|
13
|
-
### 1.
|
|
14
|
-
|
|
15
|
-
- Node.js 18+ and npm/pnpm/yarn installed
|
|
16
|
-
- A Convex account (free at [convex.dev](https://convex.dev))
|
|
17
|
-
|
|
18
|
-
### 2. Clone and Install
|
|
7
|
+
### 1. Install dependencies
|
|
19
8
|
|
|
20
9
|
```bash
|
|
21
|
-
# Clone this template
|
|
22
|
-
git clone <your-repo-url>
|
|
23
|
-
cd <your-project-name>
|
|
24
|
-
|
|
25
|
-
# Install dependencies
|
|
26
|
-
npm install
|
|
27
|
-
# or
|
|
28
10
|
pnpm install
|
|
29
|
-
# or
|
|
30
|
-
yarn install
|
|
31
11
|
```
|
|
32
12
|
|
|
33
|
-
###
|
|
34
|
-
|
|
35
|
-
Copy the example environment file:
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
cp .env.example .env
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### 4. Generate Better Auth Secret
|
|
42
|
-
|
|
43
|
-
Generate a secure secret for Better Auth:
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npm run secret:create
|
|
47
|
-
# or
|
|
48
|
-
pnpm secret:create
|
|
49
|
-
# or
|
|
50
|
-
yarn secret:create
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
This will generate a random 32-character hex string and copy it to your clipboard.
|
|
54
|
-
|
|
55
|
-
### 5. Configure Convex
|
|
56
|
-
|
|
57
|
-
#### Set up Convex project
|
|
13
|
+
### 2. Start Convex
|
|
58
14
|
|
|
59
15
|
```bash
|
|
60
16
|
npx convex dev
|
|
61
17
|
```
|
|
62
18
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- Create a new Convex project (or link to an existing one)
|
|
66
|
-
- Generate your `CONVEX_URL` and `NEXT_PUBLIC_CONVEX_URL`
|
|
67
|
-
- Start the Convex development server
|
|
68
|
-
|
|
69
|
-
#### Add environment variables to Convex
|
|
70
|
-
|
|
71
|
-
You need to add two critical environment variables to your Convex deployment via the [Convex Dashboard](https://dashboard.convex.dev):
|
|
19
|
+
On first run this creates (or links) a Convex project and prints your
|
|
20
|
+
deployment's URL and site URL.
|
|
72
21
|
|
|
73
|
-
|
|
74
|
-
2. **SITE_URL**: Set to your application URL
|
|
75
|
-
- Development: `http://localhost:3000` (or whatever port your dev server uses)
|
|
76
|
-
- Production: Your production domain (e.g., `https://yourdomain.com`)
|
|
22
|
+
### 3. Fill in your environment variables
|
|
77
23
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
3. Add both `BETTER_AUTH_SECRET` and `SITE_URL`
|
|
83
|
-
|
|
84
|
-
### 6. Update Your .env File
|
|
85
|
-
|
|
86
|
-
Update your `.env` file with the values from Convex setup:
|
|
24
|
+
`.env.local` already has a generated `BETTER_AUTH_SECRET` and a
|
|
25
|
+
`http://localhost:3010` `SITE_URL`/`NEXT_PUBLIC_SITE_URL` pair, so the app
|
|
26
|
+
builds and typechecks immediately. Replace the placeholder Convex URLs with the
|
|
27
|
+
real ones `npx convex dev` printed:
|
|
87
28
|
|
|
88
29
|
```env
|
|
89
|
-
NODE_ENV=development
|
|
90
|
-
|
|
91
|
-
# From Convex setup
|
|
92
|
-
CONVEX_URL=https://your-deployment.convex.cloud
|
|
93
30
|
NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
|
|
94
|
-
CONVEX_SITE_URL=https://your-deployment.convex.site
|
|
95
31
|
NEXT_PUBLIC_CONVEX_SITE_URL=https://your-deployment.convex.site
|
|
96
|
-
|
|
97
|
-
# Better Auth (same secret you added to Convex Dashboard)
|
|
98
|
-
BETTER_AUTH_SECRET=your-generated-secret-here
|
|
99
|
-
NEXT_PUBLIC_SITE_URL=http://localhost:3000
|
|
100
32
|
```
|
|
101
33
|
|
|
102
|
-
|
|
34
|
+
Then, in the [Convex Dashboard](https://dashboard.convex.dev) → Settings →
|
|
35
|
+
Environment Variables, add `BETTER_AUTH_SECRET` (same value as `.env.local`)
|
|
36
|
+
and `SITE_URL` (`http://localhost:3010` for local dev).
|
|
37
|
+
|
|
38
|
+
### 4. Run the dev servers
|
|
103
39
|
|
|
104
40
|
```bash
|
|
105
|
-
# Terminal 1
|
|
106
|
-
|
|
41
|
+
# Terminal 1
|
|
42
|
+
pnpm vex:dev
|
|
107
43
|
|
|
108
|
-
# Terminal 2
|
|
109
|
-
npm run dev
|
|
110
|
-
# or
|
|
44
|
+
# Terminal 2
|
|
111
45
|
pnpm dev
|
|
112
|
-
# or
|
|
113
|
-
yarn dev
|
|
114
46
|
```
|
|
115
47
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
## Authentication Setup
|
|
48
|
+
### 5. Create your admin account
|
|
119
49
|
|
|
120
|
-
|
|
50
|
+
Visit `http://localhost:3010`, click "Create Admin Account", and sign up. The
|
|
51
|
+
first user to sign up on a fresh project is automatically promoted to admin —
|
|
52
|
+
see `convex/vex/firstUser.ts`. From there, "Go to Admin Panel" opens `/admin`.
|
|
121
53
|
|
|
122
54
|
<!-- {{OAUTH_SETUP_GUIDE}} -->
|
|
123
55
|
|
|
124
|
-
## Customizing Authentication
|
|
125
|
-
|
|
126
|
-
### Adding OAuth Providers
|
|
127
|
-
|
|
128
|
-
To add or modify OAuth providers:
|
|
129
|
-
|
|
130
|
-
1. **Update Better Auth configuration** in `convex/auth/index.ts`:
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
export const createAuth = (ctx: GenericActionCtx<DataModel>) => {
|
|
134
|
-
return betterAuth({
|
|
135
|
-
socialProviders: {
|
|
136
|
-
google: {
|
|
137
|
-
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
138
|
-
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
139
|
-
redirectURI: `${process.env.SITE_URL}/api/auth/callback/google`,
|
|
140
|
-
},
|
|
141
|
-
// Add more providers
|
|
142
|
-
github: {
|
|
143
|
-
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
144
|
-
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
// ... rest of config
|
|
148
|
-
})
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
2. **Add credentials to Convex Dashboard** as environment variables
|
|
153
|
-
|
|
154
|
-
3. **Consult the Better Auth documentation** for provider-specific configuration:
|
|
155
|
-
- [Better Auth Providers](https://www.better-auth.com/docs/authentication/social-login)
|
|
156
|
-
|
|
157
|
-
### Customizing User Schema
|
|
158
|
-
|
|
159
|
-
To add custom fields to your user model:
|
|
160
|
-
|
|
161
|
-
1. Update the `user.additionalFields` in `convex/auth/index.ts`
|
|
162
|
-
2. Update your Convex schema accordingly
|
|
163
|
-
3. See [Better Auth User Management](https://www.better-auth.com/docs/concepts/user-management) for details
|
|
164
|
-
|
|
165
|
-
### Email/Password Configuration
|
|
166
|
-
|
|
167
|
-
The template has email/password authentication enabled by default. To customize:
|
|
168
|
-
|
|
169
|
-
- [Better Auth Email/Password Docs](https://www.better-auth.com/docs/authentication/email-password)
|
|
170
|
-
- [Email Verification Setup](https://www.better-auth.com/docs/authentication/email-verification)
|
|
171
|
-
|
|
172
56
|
## Project Structure
|
|
173
57
|
|
|
174
58
|
```
|
|
175
59
|
.
|
|
176
60
|
├── src/
|
|
177
61
|
│ ├── app/ # Next.js App Router
|
|
178
|
-
│ │ ├── (frontend)/
|
|
179
|
-
│ │ ├──
|
|
180
|
-
│ │ └── layout.tsx
|
|
181
|
-
│ ├── auth/
|
|
182
|
-
│ ├── components/
|
|
183
|
-
│ ├──
|
|
184
|
-
│
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
│ ├──
|
|
188
|
-
│
|
|
189
|
-
|
|
190
|
-
└── .env
|
|
62
|
+
│ │ ├── (frontend)/ # Public site routes
|
|
63
|
+
│ │ ├── (vexcms)/admin/ # Admin panel routes
|
|
64
|
+
│ │ └── layout.tsx # Root layout
|
|
65
|
+
│ ├── auth/ # Auth client + server helpers, access control
|
|
66
|
+
│ ├── components/ # React components
|
|
67
|
+
│ ├── vexcms/collections/ # Collection definitions
|
|
68
|
+
│ ├── vex.config.ts # VexCMS config
|
|
69
|
+
│ └── env.mjs # Typed environment variables
|
|
70
|
+
├── convex/ # Convex backend
|
|
71
|
+
│ ├── auth/ # Better Auth config + plugins
|
|
72
|
+
│ ├── vex/ # Bootstrap, media, globals endpoints
|
|
73
|
+
│ └── schema.ts # Database schema
|
|
74
|
+
└── .env.local # Environment variables (not committed)
|
|
191
75
|
```
|
|
192
76
|
|
|
193
|
-
##
|
|
77
|
+
## Scripts
|
|
194
78
|
|
|
195
79
|
```bash
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
80
|
+
pnpm dev # Start the Next.js dev server
|
|
81
|
+
pnpm vex:dev # Start VexCMS's schema watcher + convex dev
|
|
82
|
+
pnpm vex:generate # Regenerate the Convex schema and types once
|
|
83
|
+
pnpm build # Build for production
|
|
84
|
+
pnpm typecheck # Type check without emitting
|
|
85
|
+
pnpm lint # Lint
|
|
86
|
+
pnpm format # Format with Prettier
|
|
87
|
+
pnpm secret:create # Generate a Better Auth secret
|
|
204
88
|
```
|
|
205
89
|
|
|
206
|
-
## Documentation
|
|
207
|
-
|
|
208
|
-
### Core Technologies
|
|
90
|
+
## Documentation
|
|
209
91
|
|
|
92
|
+
- **VexCMS**: [docs.vexcms.dev](https://docs.vexcms.dev)
|
|
210
93
|
- **Next.js**: [nextjs.org/docs](https://nextjs.org/docs)
|
|
211
94
|
- **Better Auth**: [better-auth.com/docs](https://www.better-auth.com/docs)
|
|
212
95
|
- **Convex**: [docs.convex.dev](https://docs.convex.dev)
|
|
213
|
-
- **shadcn/ui**: [ui.shadcn.com](https://ui.shadcn.com)
|
|
214
|
-
- **Tailwind CSS**: [tailwindcss.com/docs](https://tailwindcss.com/docs)
|
|
215
|
-
|
|
216
|
-
### Integration Guides
|
|
217
|
-
|
|
218
|
-
- **Better Auth + Convex**: [better-auth.com/docs/integrations/convex](https://www.better-auth.com/docs/integrations/convex)
|
|
219
|
-
- **Better Auth + Next.js**: [better-auth.com/docs/integrations/nextjs](https://www.better-auth.com/docs/integrations/nextjs)
|
|
220
96
|
|
|
221
97
|
## Deployment
|
|
222
98
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
2. **Update Convex environment variables** in production:
|
|
232
|
-
- Set `SITE_URL` to your production domain
|
|
233
|
-
- Keep `BETTER_AUTH_SECRET` the same (or rotate it)
|
|
234
|
-
|
|
235
|
-
3. **Deploy your application** to your hosting provider (Vercel recommended):
|
|
236
|
-
|
|
237
|
-
For Vercel:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
npm install -g vercel
|
|
241
|
-
vercel
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
Or connect your GitHub repository to Vercel for automatic deployments.
|
|
245
|
-
|
|
246
|
-
4. **Update environment variables** in your hosting provider with production values:
|
|
247
|
-
- `NEXT_PUBLIC_CONVEX_URL`
|
|
248
|
-
- `NEXT_PUBLIC_CONVEX_SITE_URL`
|
|
249
|
-
- `NEXT_PUBLIC_SITE_URL`
|
|
250
|
-
- `BETTER_AUTH_SECRET`
|
|
251
|
-
|
|
252
|
-
See [Next.js Deployment](https://nextjs.org/docs/app/building-your-application/deploying) and [Convex Production Deployment](https://docs.convex.dev/production/hosting) for detailed deployment instructions.
|
|
253
|
-
|
|
254
|
-
## License
|
|
255
|
-
|
|
256
|
-
MIT
|
|
99
|
+
1. `npx convex deploy`
|
|
100
|
+
2. In the Convex Dashboard, set `SITE_URL` to your production domain (keep
|
|
101
|
+
`BETTER_AUTH_SECRET` the same, or rotate it and update both places).
|
|
102
|
+
3. Deploy the Next.js app (e.g. Vercel) with `NEXT_PUBLIC_CONVEX_URL`,
|
|
103
|
+
`NEXT_PUBLIC_CONVEX_SITE_URL`, `NEXT_PUBLIC_SITE_URL`, and
|
|
104
|
+
`BETTER_AUTH_SECRET` set to their production values.
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
# Since the ".env" file is gitignored,
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# Since the ".env.local" file is gitignored, use this file to build a new one
|
|
2
|
+
# when you clone the repo. Keep it up to date when you add new variables to
|
|
3
|
+
# `.env.local`.
|
|
4
4
|
|
|
5
|
-
# This file
|
|
6
|
-
# secrets in it. If you are cloning this repo, create a copy of this file named
|
|
7
|
-
# ".env" and populate it with your secrets.
|
|
5
|
+
# This file is committed to version control — never put secrets in it.
|
|
8
6
|
|
|
9
|
-
# When adding
|
|
10
|
-
# should be updated accordingly.
|
|
7
|
+
# When adding new environment variables, update the schema in "src/env.mjs".
|
|
11
8
|
|
|
12
9
|
NEXT_PUBLIC_CONVEX_URL=""
|
|
13
10
|
NEXT_PUBLIC_CONVEX_SITE_URL=""
|
|
14
11
|
|
|
15
|
-
NEXT_PUBLIC_SITE_URL="http://localhost:
|
|
12
|
+
NEXT_PUBLIC_SITE_URL="http://localhost:3010"
|
|
13
|
+
SITE_URL="http://localhost:3010"
|
|
16
14
|
BETTER_AUTH_SECRET=""
|
|
17
15
|
|
|
18
16
|
# {{ENV_OAUTH_VARS}}
|
|
@@ -1,2 +1,42 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.*
|
|
7
|
+
.yarn/*
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
10
|
+
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
12
|
+
|
|
13
|
+
# testing
|
|
14
|
+
/coverage
|
|
15
|
+
|
|
16
|
+
# next.js
|
|
17
|
+
/.next/
|
|
18
|
+
/out/
|
|
19
|
+
|
|
20
|
+
# production
|
|
21
|
+
/build
|
|
22
|
+
|
|
23
|
+
# misc
|
|
24
|
+
.DS_Store
|
|
25
|
+
*.pem
|
|
26
|
+
|
|
27
|
+
# debug
|
|
28
|
+
npm-debug.log*
|
|
29
|
+
yarn-debug.log*
|
|
30
|
+
yarn-error.log*
|
|
31
|
+
.pnpm-debug.log*
|
|
32
|
+
|
|
33
|
+
# env files
|
|
34
|
+
.env
|
|
35
|
+
.env.*
|
|
36
|
+
|
|
37
|
+
# vercel
|
|
38
|
+
.vercel
|
|
39
|
+
|
|
40
|
+
# typescript
|
|
41
|
+
*.tsbuildinfo
|
|
42
|
+
next-env.d.ts
|
|
@@ -8,45 +8,17 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type * as auth_adapter_index from "../auth/adapter/index.js";
|
|
12
|
-
import type * as auth_adapter_utils from "../auth/adapter/utils.js";
|
|
13
11
|
import type * as auth_api from "../auth/api.js";
|
|
14
|
-
import type * as auth_config from "../auth/config.js";
|
|
15
12
|
import type * as auth_db from "../auth/db.js";
|
|
16
13
|
import type * as auth_index from "../auth/index.js";
|
|
17
14
|
import type * as auth_options from "../auth/options.js";
|
|
18
15
|
import type * as auth_plugins_index from "../auth/plugins/index.js";
|
|
19
16
|
import type * as auth_sessions from "../auth/sessions.js";
|
|
20
17
|
import type * as http from "../http.js";
|
|
21
|
-
import type * as
|
|
22
|
-
import type * as
|
|
23
|
-
import type * as
|
|
24
|
-
import type * as vex_api_headers from "../vex/api/headers.js";
|
|
25
|
-
import type * as vex_api_index from "../vex/api/index.js";
|
|
26
|
-
import type * as vex_api_media from "../vex/api/media.js";
|
|
27
|
-
import type * as vex_api_pages from "../vex/api/pages.js";
|
|
28
|
-
import type * as vex_api_posts from "../vex/api/posts.js";
|
|
29
|
-
import type * as vex_api_themes from "../vex/api/themes.js";
|
|
30
|
-
import type * as vex_api_user from "../vex/api/user.js";
|
|
31
|
-
import type * as vex_auth from "../vex/auth.js";
|
|
32
|
-
import type * as vex_collections from "../vex/collections.js";
|
|
33
|
-
import type * as vex_impersonation from "../vex/impersonation.js";
|
|
18
|
+
import type * as vex from "../vex.js";
|
|
19
|
+
import type * as vex_firstUser from "../vex/firstUser.js";
|
|
20
|
+
import type * as vex_globals from "../vex/globals.js";
|
|
34
21
|
import type * as vex_media from "../vex/media.js";
|
|
35
|
-
import type * as vex_migrate from "../vex/migrate.js";
|
|
36
|
-
import type * as vex_model_api_articles from "../vex/model/api/articles.js";
|
|
37
|
-
import type * as vex_model_api_categories from "../vex/model/api/categories.js";
|
|
38
|
-
import type * as vex_model_api_footers from "../vex/model/api/footers.js";
|
|
39
|
-
import type * as vex_model_api_headers from "../vex/model/api/headers.js";
|
|
40
|
-
import type * as vex_model_api_media from "../vex/model/api/media.js";
|
|
41
|
-
import type * as vex_model_api_pages from "../vex/model/api/pages.js";
|
|
42
|
-
import type * as vex_model_api_posts from "../vex/model/api/posts.js";
|
|
43
|
-
import type * as vex_model_api_themes from "../vex/model/api/themes.js";
|
|
44
|
-
import type * as vex_model_api_user from "../vex/model/api/user.js";
|
|
45
|
-
import type * as vex_model_collections from "../vex/model/collections.js";
|
|
46
|
-
import type * as vex_model_media from "../vex/model/media.js";
|
|
47
|
-
import type * as vex_model_versions from "../vex/model/versions.js";
|
|
48
|
-
import type * as vex_previewSnapshot from "../vex/previewSnapshot.js";
|
|
49
|
-
import type * as vex_versions from "../vex/versions.js";
|
|
50
22
|
|
|
51
23
|
import type {
|
|
52
24
|
ApiFromModules,
|
|
@@ -55,45 +27,17 @@ import type {
|
|
|
55
27
|
} from "convex/server";
|
|
56
28
|
|
|
57
29
|
declare const fullApi: ApiFromModules<{
|
|
58
|
-
"auth/adapter/index": typeof auth_adapter_index;
|
|
59
|
-
"auth/adapter/utils": typeof auth_adapter_utils;
|
|
60
30
|
"auth/api": typeof auth_api;
|
|
61
|
-
"auth/config": typeof auth_config;
|
|
62
31
|
"auth/db": typeof auth_db;
|
|
63
32
|
"auth/index": typeof auth_index;
|
|
64
33
|
"auth/options": typeof auth_options;
|
|
65
34
|
"auth/plugins/index": typeof auth_plugins_index;
|
|
66
35
|
"auth/sessions": typeof auth_sessions;
|
|
67
36
|
http: typeof http;
|
|
68
|
-
|
|
69
|
-
"vex/
|
|
70
|
-
"vex/
|
|
71
|
-
"vex/api/headers": typeof vex_api_headers;
|
|
72
|
-
"vex/api/index": typeof vex_api_index;
|
|
73
|
-
"vex/api/media": typeof vex_api_media;
|
|
74
|
-
"vex/api/pages": typeof vex_api_pages;
|
|
75
|
-
"vex/api/posts": typeof vex_api_posts;
|
|
76
|
-
"vex/api/themes": typeof vex_api_themes;
|
|
77
|
-
"vex/api/user": typeof vex_api_user;
|
|
78
|
-
"vex/auth": typeof vex_auth;
|
|
79
|
-
"vex/collections": typeof vex_collections;
|
|
80
|
-
"vex/impersonation": typeof vex_impersonation;
|
|
37
|
+
vex: typeof vex;
|
|
38
|
+
"vex/firstUser": typeof vex_firstUser;
|
|
39
|
+
"vex/globals": typeof vex_globals;
|
|
81
40
|
"vex/media": typeof vex_media;
|
|
82
|
-
"vex/migrate": typeof vex_migrate;
|
|
83
|
-
"vex/model/api/articles": typeof vex_model_api_articles;
|
|
84
|
-
"vex/model/api/categories": typeof vex_model_api_categories;
|
|
85
|
-
"vex/model/api/footers": typeof vex_model_api_footers;
|
|
86
|
-
"vex/model/api/headers": typeof vex_model_api_headers;
|
|
87
|
-
"vex/model/api/media": typeof vex_model_api_media;
|
|
88
|
-
"vex/model/api/pages": typeof vex_model_api_pages;
|
|
89
|
-
"vex/model/api/posts": typeof vex_model_api_posts;
|
|
90
|
-
"vex/model/api/themes": typeof vex_model_api_themes;
|
|
91
|
-
"vex/model/api/user": typeof vex_model_api_user;
|
|
92
|
-
"vex/model/collections": typeof vex_model_collections;
|
|
93
|
-
"vex/model/media": typeof vex_model_media;
|
|
94
|
-
"vex/model/versions": typeof vex_model_versions;
|
|
95
|
-
"vex/previewSnapshot": typeof vex_previewSnapshot;
|
|
96
|
-
"vex/versions": typeof vex_versions;
|
|
97
41
|
}>;
|
|
98
42
|
|
|
99
43
|
/**
|
|
@@ -21,6 +21,17 @@ import {
|
|
|
21
21
|
} from "convex/server";
|
|
22
22
|
import type { DataModel } from "./dataModel.js";
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Typesafe environment variables.
|
|
26
|
+
*
|
|
27
|
+
* This includes platform-provided env vars and any variables declared in
|
|
28
|
+
* `convex.config.ts`.
|
|
29
|
+
*/
|
|
30
|
+
type Env = {
|
|
31
|
+
readonly CONVEX_CLOUD_URL: string;
|
|
32
|
+
readonly CONVEX_SITE_URL: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
24
35
|
/**
|
|
25
36
|
* Define a query in this Convex app's public API.
|
|
26
37
|
*
|
|
@@ -95,6 +106,14 @@ export declare const internalAction: ActionBuilder<DataModel, "internal">;
|
|
|
95
106
|
*/
|
|
96
107
|
export declare const httpAction: HttpActionBuilder;
|
|
97
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Typesafe environment variables.
|
|
111
|
+
*
|
|
112
|
+
* This includes platform-provided env vars and any variables declared in
|
|
113
|
+
* `convex.config.ts`.
|
|
114
|
+
*/
|
|
115
|
+
export declare const env: Env;
|
|
116
|
+
|
|
98
117
|
/**
|
|
99
118
|
* A set of services for use within Convex query functions.
|
|
100
119
|
*
|
|
@@ -91,3 +91,11 @@ export const internalAction = internalActionGeneric;
|
|
|
91
91
|
* @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
|
|
92
92
|
*/
|
|
93
93
|
export const httpAction = httpActionGeneric;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Typesafe environment variables.
|
|
97
|
+
*
|
|
98
|
+
* This includes platform-provided env vars and any variables declared in
|
|
99
|
+
* `convex.config.ts`.
|
|
100
|
+
*/
|
|
101
|
+
export const env = process.env;
|
|
@@ -1,8 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { get } from "@vexcms/core/server";
|
|
2
|
+
import { v } from "convex/values";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type OrganizationID,
|
|
6
|
+
type SessionID,
|
|
7
|
+
TABLE_SLUG_ORGANIZATIONS,
|
|
8
|
+
TABLE_SLUG_SESSIONS,
|
|
9
|
+
TABLE_SLUG_USERS,
|
|
10
|
+
type UserID,
|
|
11
|
+
} from "~/db/constants";
|
|
12
|
+
|
|
13
|
+
import { query } from "../_generated/server";
|
|
2
14
|
|
|
3
15
|
export const identifyCurrentUser = query({
|
|
4
16
|
args: {},
|
|
5
17
|
handler: async (ctx) => {
|
|
6
|
-
return ctx.auth.getUserIdentity()
|
|
18
|
+
return ctx.auth.getUserIdentity();
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const getUserOrg = query({
|
|
23
|
+
args: {
|
|
24
|
+
noOrgs: v.optional(v.boolean()),
|
|
25
|
+
},
|
|
26
|
+
handler: async (ctx, args) => {
|
|
27
|
+
const empty = { user: null, organization: undefined };
|
|
28
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
29
|
+
if (!identity) {
|
|
30
|
+
return empty;
|
|
31
|
+
}
|
|
32
|
+
const session = await get({
|
|
33
|
+
ctx,
|
|
34
|
+
id: identity.sessionId as SessionID,
|
|
35
|
+
collection: TABLE_SLUG_SESSIONS,
|
|
36
|
+
});
|
|
37
|
+
if (!session) {
|
|
38
|
+
return empty;
|
|
39
|
+
}
|
|
40
|
+
const user = await get({ ctx, id: identity.subject as UserID, collection: TABLE_SLUG_USERS });
|
|
41
|
+
if (!user) {
|
|
42
|
+
return empty;
|
|
43
|
+
}
|
|
44
|
+
if (!session.activeOrganizationId || args.noOrgs) {
|
|
45
|
+
return { user, organization: undefined };
|
|
46
|
+
}
|
|
47
|
+
const organization = await get({
|
|
48
|
+
ctx,
|
|
49
|
+
id: session.activeOrganizationId as OrganizationID,
|
|
50
|
+
collection: TABLE_SLUG_ORGANIZATIONS,
|
|
51
|
+
});
|
|
52
|
+
return { user, organization: organization ?? undefined };
|
|
7
53
|
},
|
|
8
|
-
})
|
|
54
|
+
});
|