create-vexcms 0.1.0-alpha.17 → 0.1.0-alpha.19
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 +11 -0
- package/dist/index.js +8 -1
- package/package.json +1 -1
- package/templates/base-nextjs/README.md +3 -2
- package/templates/base-nextjs/convex/auth/options.ts +18 -26
- package/templates/base-nextjs/convex/auth/schema.ts +51 -0
- package/templates/base-nextjs/convex/vex/globals.ts +1 -1
- package/templates/base-nextjs/convex/vex/media.ts +1 -1
- package/templates/base-nextjs/convex/vex.ts +1 -1
- package/templates/base-nextjs/package.json +6 -6
- package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +27 -15
- package/templates/base-nextjs/src/app/(vexcms)/admin/[[...path]]/page.tsx +1 -1
- package/templates/base-nextjs/src/app/(vexcms)/admin/clientProviders.tsx +9 -14
- package/templates/base-nextjs/src/app/(vexcms)/admin/layout.tsx +1 -2
- package/templates/base-nextjs/src/app/api/vex/revalidate/route.ts +1 -1
- package/templates/base-nextjs/src/auth/access.ts +14 -2
- package/templates/base-nextjs/src/vex.config.server.ts +30 -0
- package/templates/base-nextjs/src/vex.config.ts +14 -11
- package/templates/base-nextjs/src/vexcms/collections/images.ts +1 -1
- package/templates/marketing-site/convex/seed.ts +1 -1
- package/templates/marketing-site/convex/siteSettings.ts +1 -1
- package/templates/marketing-site/convex/theme.ts +1 -1
- package/templates/marketing-site/src/app/(vexcms)/admin/layout.tsx +1 -4
- package/templates/marketing-site/src/lib/metadata.ts +9 -2
- package/templates/marketing-site/src/vex.config.server.ts +27 -0
- package/templates/marketing-site/src/vex.config.ts +12 -9
- package/templates/marketing-site/src/vexcms/api.ts +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,17 @@ site:
|
|
|
100
100
|
- `convex/seed.ts` — an idempotent `init` mutation seeding site settings, a header, a footer, the
|
|
101
101
|
starter palettes, and a complete home page from the blocks' own defaults (`pnpm seed`)
|
|
102
102
|
|
|
103
|
+
### Configuration
|
|
104
|
+
|
|
105
|
+
Every scaffold ships two config files: `src/vex.config.ts` — client-safe, holds collections,
|
|
106
|
+
globals, media collections, access rules, and the auth collections
|
|
107
|
+
`betterAuthCollections(authSchema)` derives from `src/auth/schema.ts` — and
|
|
108
|
+
`src/vex.config.server.ts`, which layers the Better Auth adapter and the storage adapter(s)
|
|
109
|
+
onto it via `defineServerConfig`. Nothing is generated for auth: `src/auth/schema.ts` is the
|
|
110
|
+
one source of truth for model names and fields (organizations on or off, per the `--orgs`
|
|
111
|
+
flag) — the client config calls `betterAuthCollections()` on it directly, and
|
|
112
|
+
`src/auth/options.ts` spreads it into the real Better Auth options.
|
|
113
|
+
|
|
103
114
|
## Getting started
|
|
104
115
|
|
|
105
116
|
After scaffolding:
|
package/dist/index.js
CHANGED
|
@@ -2339,7 +2339,8 @@ var VexFrameworkInstaller = class {
|
|
|
2339
2339
|
}
|
|
2340
2340
|
/**
|
|
2341
2341
|
* Configure the Better Auth organizations plugin.
|
|
2342
|
-
* When enabled, replaces placeholders with organization import
|
|
2342
|
+
* When enabled, replaces placeholders with the organization import, plugin
|
|
2343
|
+
* instance, and schema descriptor,
|
|
2343
2344
|
* and leaves every org-aware file (`createGetAuth` call sites, `access.ts`,
|
|
2344
2345
|
* `AuthContext.tsx`, `hasPermission.ts`, `convex/auth/api.ts`,
|
|
2345
2346
|
* `db/constants/index.ts`) untouched.
|
|
@@ -2369,6 +2370,12 @@ var VexFrameworkInstaller = class {
|
|
|
2369
2370
|
}
|
|
2370
2371
|
await fs5.writeFile(pluginsPath, content);
|
|
2371
2372
|
}
|
|
2373
|
+
const schemaPath = path2.join(this.targetPath, "convex/auth/schema.ts");
|
|
2374
|
+
if (await fs5.pathExists(schemaPath)) {
|
|
2375
|
+
let content = await fs5.readFile(schemaPath, "utf-8");
|
|
2376
|
+
content = enabled ? content.replace(" // {{ORGANIZATIONS_SCHEMA}}", " organization: true,") : content.replace(/.*\/\/ \{\{ORGANIZATIONS_SCHEMA\}\}\n?/, "");
|
|
2377
|
+
await fs5.writeFile(schemaPath, content);
|
|
2378
|
+
}
|
|
2372
2379
|
if (!enabled) {
|
|
2373
2380
|
await this.stripOrganizationWiring();
|
|
2374
2381
|
}
|
package/package.json
CHANGED
|
@@ -77,10 +77,11 @@ see `convex/vex/firstUser.ts`. From there, "Go to Admin Panel" opens `/admin`.
|
|
|
77
77
|
│ ├── auth/ # Auth client + server helpers, access control
|
|
78
78
|
│ ├── components/ # React components
|
|
79
79
|
│ ├── vexcms/collections/ # Collection definitions
|
|
80
|
-
│ ├── vex.config.ts # VexCMS config
|
|
80
|
+
│ ├── vex.config.ts # VexCMS client config (collections, globals, access)
|
|
81
|
+
│ ├── vex.config.server.ts # VexCMS server config (auth adapter, storage adapters)
|
|
81
82
|
│ └── env.mjs # Typed environment variables
|
|
82
83
|
├── convex/ # Convex backend
|
|
83
|
-
│ ├── auth/ # Better Auth config + plugins
|
|
84
|
+
│ ├── auth/ # Better Auth config + plugins (schema.ts, options.ts)
|
|
84
85
|
│ ├── vex/ # Bootstrap, media, globals endpoints
|
|
85
86
|
│ └── schema.ts # Database schema
|
|
86
87
|
└── .env.local # Environment variables (not committed)
|
|
@@ -2,20 +2,28 @@ import type { BetterAuthOptions } from "better-auth"
|
|
|
2
2
|
|
|
3
3
|
import { anonRoleDatabaseHook } from "@vexcms/better-auth"
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
TABLE_SLUG_ACCOUNTS,
|
|
7
|
-
TABLE_SLUG_SESSIONS,
|
|
8
|
-
TABLE_SLUG_USERS,
|
|
9
|
-
TABLE_SLUG_VERIFICATIONS,
|
|
10
|
-
USER_ROLES,
|
|
11
|
-
} from "~/db/constants"
|
|
5
|
+
import { USER_ROLES } from "~/db/constants"
|
|
12
6
|
|
|
13
7
|
import { createPlugins } from "./plugins"
|
|
8
|
+
import { authSchema } from "./schema"
|
|
14
9
|
|
|
10
|
+
// `plugins` on the shared schema is a descriptor map for
|
|
11
|
+
// `betterAuthCollections`, not Better Auth plugin instances — dropped by name
|
|
12
|
+
// so the intent survives a reordering of the object literal below.
|
|
13
|
+
const { plugins: _pluginDescriptors, ...schemaOptions } = authSchema
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Better Auth options for this project.
|
|
17
|
+
*
|
|
18
|
+
* Model names and `user.additionalFields` come from `./schema`, the one module
|
|
19
|
+
* both this file and `src/vex.config.ts` read, so a field added there reaches
|
|
20
|
+
* the database and the admin panel without being written twice. Everything
|
|
21
|
+
* below the spread is server-only.
|
|
22
|
+
*
|
|
23
|
+
* @see ./schema for the shared, client-safe half
|
|
24
|
+
*/
|
|
15
25
|
export const authOptions: BetterAuthOptions = {
|
|
16
|
-
|
|
17
|
-
modelName: TABLE_SLUG_ACCOUNTS,
|
|
18
|
-
},
|
|
26
|
+
...schemaOptions,
|
|
19
27
|
baseURL: process.env.SITE_URL,
|
|
20
28
|
databaseHooks: {
|
|
21
29
|
// Ties Better Auth's anonymous-plugin users to `access.anonRole` (see
|
|
@@ -28,21 +36,5 @@ export const authOptions: BetterAuthOptions = {
|
|
|
28
36
|
// {{OAUTH_PROVIDERS}}
|
|
29
37
|
plugins: createPlugins(),
|
|
30
38
|
secret: process.env.BETTER_AUTH_SECRET,
|
|
31
|
-
session: {
|
|
32
|
-
modelName: TABLE_SLUG_SESSIONS,
|
|
33
|
-
},
|
|
34
39
|
trustedOrigins: [process.env.SITE_URL!],
|
|
35
|
-
user: {
|
|
36
|
-
additionalFields: {
|
|
37
|
-
roles: {
|
|
38
|
-
type: "string[]",
|
|
39
|
-
defaultValue: [USER_ROLES.user],
|
|
40
|
-
required: true,
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
modelName: TABLE_SLUG_USERS,
|
|
44
|
-
},
|
|
45
|
-
verification: {
|
|
46
|
-
modelName: TABLE_SLUG_VERIFICATIONS,
|
|
47
|
-
},
|
|
48
40
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { BetterAuthSchemaInput } from "@vexcms/better-auth/client"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
TABLE_SLUG_ACCOUNTS,
|
|
5
|
+
TABLE_SLUG_SESSIONS,
|
|
6
|
+
TABLE_SLUG_USERS,
|
|
7
|
+
TABLE_SLUG_VERIFICATIONS,
|
|
8
|
+
USER_ROLES,
|
|
9
|
+
} from "~/db/constants"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Everything about your Better Auth setup that determines which collections
|
|
13
|
+
* exist and which fields they carry — and nothing else.
|
|
14
|
+
*
|
|
15
|
+
* This is the one auth module the browser reads: `src/vex.config.ts` passes it
|
|
16
|
+
* to `betterAuthCollections()` to build the admin panel's auth collections,
|
|
17
|
+
* and `./options` spreads it so the same model names and fields reach the
|
|
18
|
+
* database. Add a `user.additionalFields` entry here and it shows up in both.
|
|
19
|
+
*
|
|
20
|
+
* Keep it inert. `./options` reads `process.env` and `./plugins` pulls
|
|
21
|
+
* `better-auth/next-js` (→ `next/headers`), which is why they are separate
|
|
22
|
+
* files — importing either from the client config would drag the server into
|
|
23
|
+
* your browser bundle. Runtime plugin options (admin roles, mailers, OAuth
|
|
24
|
+
* secrets) belong on the real plugin instances in `./plugins`; only options
|
|
25
|
+
* that change which TABLES exist belong here.
|
|
26
|
+
*
|
|
27
|
+
* @see betterAuthCollections in @vexcms/better-auth/client
|
|
28
|
+
* @see ./options for the server-side options that spread this
|
|
29
|
+
*/
|
|
30
|
+
export const authSchema = {
|
|
31
|
+
account: { modelName: TABLE_SLUG_ACCOUNTS },
|
|
32
|
+
session: { modelName: TABLE_SLUG_SESSIONS },
|
|
33
|
+
user: {
|
|
34
|
+
modelName: TABLE_SLUG_USERS,
|
|
35
|
+
additionalFields: {
|
|
36
|
+
roles: {
|
|
37
|
+
type: "string[]",
|
|
38
|
+
defaultValue: [USER_ROLES.user],
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
verification: { modelName: TABLE_SLUG_VERIFICATIONS },
|
|
44
|
+
plugins: {
|
|
45
|
+
admin: true,
|
|
46
|
+
anonymous: true,
|
|
47
|
+
apiKey: true,
|
|
48
|
+
convex: true,
|
|
49
|
+
// {{ORGANIZATIONS_SCHEMA}}
|
|
50
|
+
},
|
|
51
|
+
} satisfies BetterAuthSchemaInput
|
|
@@ -2,7 +2,7 @@ import { createGetAuth } from "@vexcms/better-auth";
|
|
|
2
2
|
import { globalsApi } from "@vexcms/core/server";
|
|
3
3
|
|
|
4
4
|
import { TABLE_SLUG_ORGANIZATIONS, TABLE_SLUG_SESSIONS, TABLE_SLUG_USERS } from "~/db/constants";
|
|
5
|
-
import config from "~/vex.config";
|
|
5
|
+
import config from "~/vex.config.server";
|
|
6
6
|
|
|
7
7
|
import { mutation, query } from "../_generated/server";
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { createGetAuth } from "@vexcms/better-auth";
|
|
|
2
2
|
import { mediaApi } from "@vexcms/core";
|
|
3
3
|
|
|
4
4
|
import { TABLE_SLUG_ORGANIZATIONS, TABLE_SLUG_SESSIONS, TABLE_SLUG_USERS } from "~/db/constants";
|
|
5
|
-
import config from "~/vex.config";
|
|
5
|
+
import config from "~/vex.config.server";
|
|
6
6
|
|
|
7
7
|
import { mutation, query } from "../_generated/server";
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { createGetAuth } from "@vexcms/better-auth";
|
|
|
2
2
|
import { collectionsApi } from "@vexcms/core/server";
|
|
3
3
|
|
|
4
4
|
import { TABLE_SLUG_ORGANIZATIONS, TABLE_SLUG_SESSIONS, TABLE_SLUG_USERS } from "~/db/constants";
|
|
5
|
-
import config from "~/vex.config";
|
|
5
|
+
import config from "~/vex.config.server";
|
|
6
6
|
|
|
7
7
|
import { mutation, query } from "./_generated/server";
|
|
8
8
|
|
|
@@ -24,11 +24,11 @@
|
|
|
24
24
|
"@t3-oss/env-nextjs": "0.13.11",
|
|
25
25
|
"@tanstack/react-form": "1.33.1",
|
|
26
26
|
"@tanstack/react-query": "5.101.2",
|
|
27
|
-
"@vexcms/better-auth": "~0.1.0-alpha.
|
|
28
|
-
"@vexcms/core": "~0.1.0-alpha.
|
|
29
|
-
"@vexcms/file-storage-convex": "~0.1.0-alpha.
|
|
30
|
-
"@vexcms/next": "~0.1.0-alpha.
|
|
31
|
-
"@vexcms/react": "~0.1.0-alpha.
|
|
27
|
+
"@vexcms/better-auth": "~0.1.0-alpha.19",
|
|
28
|
+
"@vexcms/core": "~0.1.0-alpha.19",
|
|
29
|
+
"@vexcms/file-storage-convex": "~0.1.0-alpha.19",
|
|
30
|
+
"@vexcms/next": "~0.1.0-alpha.19",
|
|
31
|
+
"@vexcms/react": "~0.1.0-alpha.19",
|
|
32
32
|
"better-auth": "1.6.23",
|
|
33
33
|
"class-variance-authority": "0.7.1",
|
|
34
34
|
"clsx": "2.1.1",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@types/node": "20.19.43",
|
|
53
53
|
"@types/react": "19.2.17",
|
|
54
54
|
"@types/react-dom": "19.2.3",
|
|
55
|
-
"@vexcms/cli": "~0.1.0-alpha.
|
|
55
|
+
"@vexcms/cli": "~0.1.0-alpha.19",
|
|
56
56
|
"babel-plugin-react-compiler": "1.0.0",
|
|
57
57
|
"eslint": "9.39.5",
|
|
58
58
|
"eslint-config-next": "15.5.9",
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { AuthView } from "@daveyplate/better-auth-ui";
|
|
4
|
-
import { useSearchParams } from "next/navigation";
|
|
4
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
5
5
|
import { Activity } from "react";
|
|
6
6
|
|
|
7
7
|
import { Dialog, DialogContent, DialogTitle } from "~/components/ui/dialog";
|
|
8
8
|
|
|
9
9
|
export default function AuthCard({ pathname }: { pathname: string }) {
|
|
10
|
+
const router = useRouter();
|
|
10
11
|
const searchParams = useSearchParams();
|
|
11
12
|
const isGuardRedirect = searchParams.has("redirectTo");
|
|
12
13
|
if (isGuardRedirect) {
|
|
@@ -16,20 +17,31 @@ export default function AuthCard({ pathname }: { pathname: string }) {
|
|
|
16
17
|
</main>
|
|
17
18
|
);
|
|
18
19
|
}
|
|
20
|
+
// No in-flow wrapper here on purpose. The Dialog portals into <body>, so a
|
|
21
|
+
// wrapping <main> would be an empty element sitting after the footer (the
|
|
22
|
+
// @auth slot renders last in the frontend layout). Next's post-navigation
|
|
23
|
+
// scroll handler targets the new segment's first DOM node and would scroll
|
|
24
|
+
// that empty element into view — i.e. jump the page to the bottom behind
|
|
25
|
+
// the modal. With only a portal rendered there is no node to target.
|
|
19
26
|
return (
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
<Dialog
|
|
28
|
+
onOpenChange={(open) => {
|
|
29
|
+
if (!open) {
|
|
30
|
+
router.back();
|
|
31
|
+
}
|
|
32
|
+
}}
|
|
33
|
+
open
|
|
34
|
+
>
|
|
35
|
+
<Activity mode="hidden">
|
|
36
|
+
<DialogTitle />
|
|
37
|
+
</Activity>
|
|
38
|
+
<DialogContent
|
|
39
|
+
aria-describedby={undefined}
|
|
40
|
+
className="grid place-items-center bg-transparent shadow-none ring-transparent"
|
|
41
|
+
showCloseButton={false}
|
|
42
|
+
>
|
|
43
|
+
<AuthView path={pathname} />
|
|
44
|
+
</DialogContent>
|
|
45
|
+
</Dialog>
|
|
34
46
|
);
|
|
35
47
|
}
|
|
@@ -4,7 +4,7 @@ import { NextAdminPage } from "@vexcms/next/server";
|
|
|
4
4
|
import { redirect } from "next/navigation";
|
|
5
5
|
|
|
6
6
|
import { fetchAuthQuery, getToken } from "~/auth/server";
|
|
7
|
-
import config from "~/vex.config";
|
|
7
|
+
import config from "~/vex.config.server";
|
|
8
8
|
|
|
9
9
|
export const dynamic = "force-dynamic";
|
|
10
10
|
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client"
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { StorageAdapterContextProvider, VexAccessProvider } from "@vexcms/react";
|
|
3
|
+
import { VexConfigProvider } from "@vexcms/react"
|
|
5
4
|
|
|
6
|
-
import
|
|
5
|
+
import config from "~/vex.config"
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
*
|
|
8
|
+
* Mounts the single `VexConfigProvider` for the admin panel's client tree.
|
|
10
9
|
*
|
|
11
|
-
* This is a "use client" component so
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* This is a "use client" component so `vex.config` is imported and owned on
|
|
11
|
+
* the client — it never crosses the RSC boundary as a prop. `access` and
|
|
12
|
+
* `storage.clientUploads` (the two fields that used to need their own
|
|
13
|
+
* providers) are already part of the config this imports.
|
|
15
14
|
*/
|
|
16
15
|
export function ClientProviders({ children }: { children: React.ReactNode }) {
|
|
17
|
-
return
|
|
18
|
-
<StorageAdapterContextProvider adapterClients={{ convex: uploadFile }}>
|
|
19
|
-
<VexAccessProvider access={access}>{children}</VexAccessProvider>
|
|
20
|
-
</StorageAdapterContextProvider>
|
|
21
|
-
);
|
|
16
|
+
return <VexConfigProvider config={config}>{children}</VexConfigProvider>
|
|
22
17
|
}
|
|
@@ -4,7 +4,6 @@ import { NextAdminLayout } from "@vexcms/next/client"
|
|
|
4
4
|
|
|
5
5
|
import { AuthServerProvider } from "~/components/providers/auth"
|
|
6
6
|
import { getCurrentUser } from "~/auth/serverUtils"
|
|
7
|
-
import config from "~/vex.config"
|
|
8
7
|
|
|
9
8
|
import { ClientProviders } from "./clientProviders"
|
|
10
9
|
|
|
@@ -13,7 +12,7 @@ export default async function AdminLayout({ children }: { children: ReactNode })
|
|
|
13
12
|
return (
|
|
14
13
|
<AuthServerProvider>
|
|
15
14
|
<ClientProviders>
|
|
16
|
-
<NextAdminLayout
|
|
15
|
+
<NextAdminLayout user={user ?? undefined}>
|
|
17
16
|
{children}
|
|
18
17
|
</NextAdminLayout>
|
|
19
18
|
</ClientProviders>
|
|
@@ -2,7 +2,7 @@ import { api } from "@convex/_generated/api"
|
|
|
2
2
|
import { createVexRevalidateRoute } from "@vexcms/next/cache"
|
|
3
3
|
|
|
4
4
|
import { fetchAuthQuery, getToken } from "~/auth/server"
|
|
5
|
-
import config from "~/vex.config"
|
|
5
|
+
import config from "~/vex.config.server"
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* `POST /api/vex/revalidate` — session-authorized path purge.
|
|
@@ -11,8 +11,16 @@ import { images, users } from "~/vexcms/collections"
|
|
|
11
11
|
* Access control (RBAC) for the admin panel and every registered collection.
|
|
12
12
|
*
|
|
13
13
|
* `admin` gets unrestricted access. `user` can read and update only their own
|
|
14
|
-
* profile row
|
|
15
|
-
* you register a new collection in `vex.config.ts`.
|
|
14
|
+
* profile row, cannot reach the admin panel, and can read `images`. Add a
|
|
15
|
+
* resource here whenever you register a new collection in `vex.config.ts`.
|
|
16
|
+
*
|
|
17
|
+
* `anonRole: user` also covers every unauthenticated public-site request,
|
|
18
|
+
* including the build-time metadata fetch. `images.read` is what lets the
|
|
19
|
+
* public site show uploaded media at all: `MediaImage` and
|
|
20
|
+
* `src/lib/metadata.ts` resolve uploads through the RBAC-gated
|
|
21
|
+
* `vex/media:getUrl`, so without it anonymous visitors get no images and the
|
|
22
|
+
* page ships no `og:image`. Narrow it with a `constraints` rule if some media
|
|
23
|
+
* must stay private; do not remove it.
|
|
16
24
|
*
|
|
17
25
|
* @see https://vexcms.dev/docs/access-control
|
|
18
26
|
*/
|
|
@@ -32,6 +40,10 @@ export const access = defineAccess({
|
|
|
32
40
|
adminPanel: {
|
|
33
41
|
access: false,
|
|
34
42
|
},
|
|
43
|
+
images: {
|
|
44
|
+
"*": false,
|
|
45
|
+
read: true,
|
|
46
|
+
},
|
|
35
47
|
user: {
|
|
36
48
|
"*": false,
|
|
37
49
|
read: {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { betterAuthAdapter } from "@vexcms/better-auth"
|
|
2
|
+
import { defineServerConfig } from "@vexcms/core"
|
|
3
|
+
import { convexFileStorage } from "@vexcms/file-storage-convex"
|
|
4
|
+
|
|
5
|
+
import { authOptions } from "@convex/auth/options"
|
|
6
|
+
|
|
7
|
+
import vexConfig from "./vex.config"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* VexCMS server-only configuration for this project.
|
|
11
|
+
*
|
|
12
|
+
* Layers the Better Auth adapter and Convex file storage onto the
|
|
13
|
+
* client-safe `vexConfig`. You write this file once at scaffold time and
|
|
14
|
+
* rarely revisit it — day-to-day settings live in `vex.config.ts`.
|
|
15
|
+
*
|
|
16
|
+
* @see defineServerConfig in @vexcms/core
|
|
17
|
+
* @see betterAuthAdapter in @vexcms/better-auth
|
|
18
|
+
* @see ./vex.config for the client-safe half this layers onto
|
|
19
|
+
*/
|
|
20
|
+
const config = defineServerConfig({
|
|
21
|
+
config: vexConfig,
|
|
22
|
+
server: {
|
|
23
|
+
auth: { adapter: betterAuthAdapter({ config: authOptions }) },
|
|
24
|
+
storage: {
|
|
25
|
+
adapters: [convexFileStorage()],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export default config
|
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { betterAuthCollections } from "@vexcms/better-auth/client"
|
|
2
2
|
import { defineConfig } from "@vexcms/core"
|
|
3
|
-
import {
|
|
3
|
+
import { uploadFile } from "@vexcms/file-storage-convex/client"
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { authSchema } from "@convex/auth/schema"
|
|
6
6
|
|
|
7
7
|
import { access } from "~/auth/access"
|
|
8
8
|
import { images, users } from "~/vexcms/collections"
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* VexCMS configuration for this project.
|
|
11
|
+
* VexCMS client-safe configuration for this project.
|
|
12
12
|
*
|
|
13
|
-
* Registers the admin sidebar layout
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Registers the admin sidebar layout and every collection, global, and
|
|
14
|
+
* media collection. Add collections/globals here as you define them —
|
|
15
|
+
* `vex dev` / `vex generate` derive the Convex schema and TypeScript types
|
|
16
|
+
* from this file, and the browser imports it directly via
|
|
17
|
+
* `VexConfigProvider`, so nothing here may reach a server SDK or an
|
|
18
|
+
* environment variable.
|
|
17
19
|
*
|
|
18
20
|
* @see defineConfig in @vexcms/core
|
|
19
|
-
* @see
|
|
21
|
+
* @see ./vex.config.server for the auth adapter and storage adapter wiring
|
|
20
22
|
*/
|
|
21
23
|
const vexConfig = defineConfig({
|
|
22
24
|
access,
|
|
25
|
+
authCollections: betterAuthCollections(authSchema),
|
|
23
26
|
admin: {
|
|
24
27
|
sidebar: {
|
|
25
28
|
side: "right",
|
|
26
29
|
},
|
|
27
30
|
},
|
|
28
|
-
authAdapter: betterAuthAdapter({ config: authOptions }),
|
|
29
31
|
storage: {
|
|
30
|
-
|
|
32
|
+
clientUploads: { convex: uploadFile },
|
|
31
33
|
},
|
|
32
34
|
collections: [users],
|
|
35
|
+
mediaCollections: [images],
|
|
33
36
|
})
|
|
34
37
|
|
|
35
38
|
export default vexConfig
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getGlobal } from "@vexcms/core/server"
|
|
2
2
|
|
|
3
3
|
import { GLOBAL_SLUG_SITE_SETTINGS, type TABLE_SLUG_THEMES } from "~/db/constants"
|
|
4
|
-
import config from "~/vex.config"
|
|
4
|
+
import config from "~/vex.config.server"
|
|
5
5
|
|
|
6
6
|
import type { Doc, Id } from "./_generated/dataModel"
|
|
7
7
|
import type { QueryCtx } from "./_generated/server"
|
|
@@ -6,7 +6,6 @@ import { AuthServerProvider } from "~/components/providers/auth";
|
|
|
6
6
|
import { getCurrentUser } from "~/auth/serverUtils";
|
|
7
7
|
import { ThemeLive } from "~/components/ThemeLive";
|
|
8
8
|
import { ThemeStyle } from "~/components/ThemeStyle";
|
|
9
|
-
import config from "~/vex.config";
|
|
10
9
|
|
|
11
10
|
import { ClientProviders } from "./clientProviders";
|
|
12
11
|
|
|
@@ -26,9 +25,7 @@ export default async function AdminLayout({ children }: { children: ReactNode })
|
|
|
26
25
|
<ClientProviders>
|
|
27
26
|
<ThemeStyle scope="admin" />
|
|
28
27
|
<ThemeLive scope="admin" />
|
|
29
|
-
<NextAdminLayout
|
|
30
|
-
{children}
|
|
31
|
-
</NextAdminLayout>
|
|
28
|
+
<NextAdminLayout user={user ?? undefined}>{children}</NextAdminLayout>
|
|
32
29
|
</ClientProviders>
|
|
33
30
|
</AuthServerProvider>
|
|
34
31
|
);
|
|
@@ -75,8 +75,15 @@ export async function generatePageMetadata(props: { slug?: string }): Promise<Me
|
|
|
75
75
|
imageUrl: ogImageUrl,
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// Discord, Slack and X pick the large-image layout from `twitter:card`, not
|
|
79
|
+
// from `og:image` alone — and Next auto-fills `card: "summary"` (a small
|
|
80
|
+
// thumbnail) whenever `twitter` is omitted. So the card type must be emitted
|
|
81
|
+
// whenever an image resolved, independently of whether a handle exists.
|
|
82
|
+
if (ogImageUrl || twitterHandle) {
|
|
83
|
+
metadata.twitter = {
|
|
84
|
+
card: ogImageUrl ? "summary_large_image" : "summary",
|
|
85
|
+
...(twitterHandle ? { site: twitterHandle } : {}),
|
|
86
|
+
}
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
return metadata
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { authOptions } from "@convex/auth/options"
|
|
2
|
+
import { betterAuthAdapter } from "@vexcms/better-auth"
|
|
3
|
+
import { defineServerConfig } from "@vexcms/core"
|
|
4
|
+
import { convexFileStorage } from "@vexcms/file-storage-convex"
|
|
5
|
+
|
|
6
|
+
import vexConfig from "./vex.config"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* VexCMS server-only configuration for the marketing site.
|
|
10
|
+
*
|
|
11
|
+
* Layers the Better Auth adapter and Convex file storage onto the
|
|
12
|
+
* client-safe `vexConfig`. You write this file once at scaffold time and
|
|
13
|
+
* rarely revisit it — day-to-day settings live in `vex.config.ts`.
|
|
14
|
+
*
|
|
15
|
+
* @see ./vex.config for the client-safe half this layers onto
|
|
16
|
+
*/
|
|
17
|
+
const config = defineServerConfig({
|
|
18
|
+
config: vexConfig,
|
|
19
|
+
server: {
|
|
20
|
+
auth: { adapter: betterAuthAdapter({ config: authOptions }) },
|
|
21
|
+
storage: {
|
|
22
|
+
adapters: [convexFileStorage()],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
export default config
|
|
@@ -1,33 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { betterAuthAdapter } from "@vexcms/better-auth"
|
|
1
|
+
import { betterAuthCollections } from "@vexcms/better-auth/client"
|
|
3
2
|
import { defineConfig } from "@vexcms/core"
|
|
4
|
-
import {
|
|
3
|
+
import { uploadFile } from "@vexcms/file-storage-convex/client"
|
|
4
|
+
|
|
5
|
+
import { authSchema } from "@convex/auth/schema"
|
|
5
6
|
|
|
6
7
|
import { access } from "~/auth/access"
|
|
7
8
|
import { footers, headers, images, pages, themes, users } from "~/vexcms/collections"
|
|
8
9
|
import { siteSettings } from "~/vexcms/globals"
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* VexCMS configuration for the marketing site.
|
|
12
|
+
* VexCMS client-safe configuration for the marketing site.
|
|
12
13
|
*
|
|
13
14
|
* Replaces `templates/base-nextjs`'s bare config wholesale (overlay copy is
|
|
14
15
|
* file-level, not a merge): carries base's `users`/`images` forward unchanged
|
|
15
|
-
* and adds the four marketing collections plus `siteSettings`.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* and adds the four marketing collections plus `siteSettings`. Imported
|
|
17
|
+
* directly by the browser via `VexConfigProvider`.
|
|
18
|
+
*
|
|
19
|
+
* @see ./vex.config.server for the auth adapter and storage adapter wiring
|
|
18
20
|
*/
|
|
19
21
|
const vexConfig = defineConfig({
|
|
20
22
|
access,
|
|
23
|
+
authCollections: betterAuthCollections(authSchema),
|
|
21
24
|
admin: {
|
|
22
25
|
sidebar: {
|
|
23
26
|
side: "right",
|
|
24
27
|
},
|
|
25
28
|
},
|
|
26
|
-
authAdapter: betterAuthAdapter({ config: authOptions }),
|
|
27
29
|
storage: {
|
|
28
|
-
|
|
30
|
+
clientUploads: { convex: uploadFile },
|
|
29
31
|
},
|
|
30
32
|
collections: [users, pages, headers, footers, themes],
|
|
33
|
+
mediaCollections: [images],
|
|
31
34
|
globals: [siteSettings],
|
|
32
35
|
routes: {
|
|
33
36
|
map: ({ collection, doc }) => {
|
|
@@ -3,7 +3,7 @@ import { createGetAuth } from "@vexcms/better-auth"
|
|
|
3
3
|
import { vexServerApi } from "@vexcms/core/server"
|
|
4
4
|
|
|
5
5
|
import { TABLE_SLUG_ORGANIZATIONS, TABLE_SLUG_SESSIONS, TABLE_SLUG_USERS } from "~/db/constants"
|
|
6
|
-
import config from "~/vex.config"
|
|
6
|
+
import config from "~/vex.config.server"
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Bound server API — `config`/`getAuth` are threaded once here so
|