create-vexcms 0.0.20 → 0.1.0-alpha.3
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 +98 -80
- package/dist/index.js +974 -639
- package/dist/index.mjs +30976 -0
- package/package.json +35 -15
- package/templates/base-nextjs/README.md +59 -203
- 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/tsconfig.json +2 -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 +46 -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 -25
- 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 +97 -12
- 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 +44 -120
- package/templates/marketing-site/src/components/motion-primitives/reveal.module.css +32 -0
- package/templates/marketing-site/src/components/motion-primitives/text-effect.tsx +41 -267
- 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 +12 -55
- 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 -333
- 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.3",
|
|
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,173 +1,65 @@
|
|
|
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
|
+
Better Auth runs inside your Convex deployment, not in the Next.js server —
|
|
35
|
+
it does **not** read `.env.local`. Set the same two values on the
|
|
36
|
+
deployment itself:
|
|
103
37
|
|
|
104
38
|
```bash
|
|
105
|
-
|
|
106
|
-
npx convex
|
|
107
|
-
|
|
108
|
-
# Terminal 2: Run the application
|
|
109
|
-
npm run dev
|
|
110
|
-
# or
|
|
111
|
-
pnpm dev
|
|
112
|
-
# or
|
|
113
|
-
yarn dev
|
|
39
|
+
npx convex env set SITE_URL http://localhost:3010
|
|
40
|
+
npx convex env set BETTER_AUTH_SECRET <the BETTER_AUTH_SECRET value from .env.local>
|
|
114
41
|
```
|
|
115
42
|
|
|
116
|
-
|
|
43
|
+
Skipping this step is the most common cause of a `403` on your first sign-in
|
|
44
|
+
attempt.
|
|
117
45
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
This project uses Better Auth for authentication.
|
|
121
|
-
|
|
122
|
-
<!-- {{OAUTH_SETUP_GUIDE}} -->
|
|
46
|
+
### 4. Run the dev servers
|
|
123
47
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
To add or modify OAuth providers:
|
|
129
|
-
|
|
130
|
-
1. **Update Better Auth configuration** in `convex/auth/index.ts`:
|
|
48
|
+
```bash
|
|
49
|
+
# Terminal 1
|
|
50
|
+
pnpm vex:dev
|
|
131
51
|
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
}
|
|
52
|
+
# Terminal 2
|
|
53
|
+
pnpm dev
|
|
150
54
|
```
|
|
151
55
|
|
|
152
|
-
|
|
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:
|
|
56
|
+
### 5. Create your admin account
|
|
160
57
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
58
|
+
Visit `http://localhost:3010`, click "Create Admin Account", and sign up. The
|
|
59
|
+
first user to sign up on a fresh project is automatically promoted to admin —
|
|
60
|
+
see `convex/vex/firstUser.ts`. From there, "Go to Admin Panel" opens `/admin`.
|
|
164
61
|
|
|
165
|
-
|
|
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)
|
|
62
|
+
<!-- {{OAUTH_SETUP_GUIDE}} -->
|
|
171
63
|
|
|
172
64
|
## Project Structure
|
|
173
65
|
|
|
@@ -175,82 +67,46 @@ The template has email/password authentication enabled by default. To customize:
|
|
|
175
67
|
.
|
|
176
68
|
├── src/
|
|
177
69
|
│ ├── app/ # Next.js App Router
|
|
178
|
-
│ │ ├── (frontend)/
|
|
179
|
-
│ │ ├──
|
|
180
|
-
│ │ └── layout.tsx
|
|
181
|
-
│ ├── auth/
|
|
182
|
-
│ ├── components/
|
|
183
|
-
│ ├──
|
|
184
|
-
│
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
│ ├──
|
|
188
|
-
│
|
|
189
|
-
|
|
190
|
-
└── .env
|
|
70
|
+
│ │ ├── (frontend)/ # Public site routes
|
|
71
|
+
│ │ ├── (vexcms)/admin/ # Admin panel routes
|
|
72
|
+
│ │ └── layout.tsx # Root layout
|
|
73
|
+
│ ├── auth/ # Auth client + server helpers, access control
|
|
74
|
+
│ ├── components/ # React components
|
|
75
|
+
│ ├── vexcms/collections/ # Collection definitions
|
|
76
|
+
│ ├── vex.config.ts # VexCMS config
|
|
77
|
+
│ └── env.mjs # Typed environment variables
|
|
78
|
+
├── convex/ # Convex backend
|
|
79
|
+
│ ├── auth/ # Better Auth config + plugins
|
|
80
|
+
│ ├── vex/ # Bootstrap, media, globals endpoints
|
|
81
|
+
│ └── schema.ts # Database schema
|
|
82
|
+
└── .env.local # Environment variables (not committed)
|
|
191
83
|
```
|
|
192
84
|
|
|
193
|
-
##
|
|
85
|
+
## Scripts
|
|
194
86
|
|
|
195
87
|
```bash
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
88
|
+
pnpm dev # Start the Next.js dev server
|
|
89
|
+
pnpm vex:dev # Start VexCMS's schema watcher + convex dev
|
|
90
|
+
pnpm vex:generate # Regenerate the Convex schema and types once
|
|
91
|
+
pnpm build # Build for production
|
|
92
|
+
pnpm typecheck # Type check without emitting
|
|
93
|
+
pnpm lint # Lint
|
|
94
|
+
pnpm format # Format with Prettier
|
|
95
|
+
pnpm secret:create # Generate a Better Auth secret
|
|
204
96
|
```
|
|
205
97
|
|
|
206
|
-
## Documentation
|
|
207
|
-
|
|
208
|
-
### Core Technologies
|
|
98
|
+
## Documentation
|
|
209
99
|
|
|
100
|
+
- **VexCMS**: [docs.vexcms.dev](https://docs.vexcms.dev)
|
|
210
101
|
- **Next.js**: [nextjs.org/docs](https://nextjs.org/docs)
|
|
211
102
|
- **Better Auth**: [better-auth.com/docs](https://www.better-auth.com/docs)
|
|
212
103
|
- **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
104
|
|
|
221
105
|
## Deployment
|
|
222
106
|
|
|
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
|
|
107
|
+
1. `npx convex deploy`
|
|
108
|
+
2. In the Convex Dashboard, set `SITE_URL` to your production domain (keep
|
|
109
|
+
`BETTER_AUTH_SECRET` the same, or rotate it and update both places).
|
|
110
|
+
3. Deploy the Next.js app (e.g. Vercel) with `NEXT_PUBLIC_CONVEX_URL`,
|
|
111
|
+
`NEXT_PUBLIC_CONVEX_SITE_URL`, `NEXT_PUBLIC_SITE_URL`, and
|
|
112
|
+
`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
|
+
});
|