create-vexcms 0.0.20 → 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 -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/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 -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
|
@@ -1,99 +1,78 @@
|
|
|
1
1
|
import type { Metadata } from "next"
|
|
2
2
|
|
|
3
|
-
import { buildSiteMetadata } from "@vexcms/core"
|
|
4
3
|
import { fetchQuery } from "convex/nextjs"
|
|
5
4
|
|
|
6
5
|
import { api } from "@convex/_generated/api"
|
|
7
6
|
import { anyApi } from "convex/server"
|
|
8
7
|
|
|
8
|
+
const TITLE_SUFFIX = " | Vex CMS"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves an `upload()` field's stored media id to a public URL via the
|
|
12
|
+
* Convex file storage adapter. Returns `undefined` when the media document
|
|
13
|
+
* no longer exists or the deployment is unreachable.
|
|
14
|
+
*/
|
|
15
|
+
async function resolveMediaUrl(mediaId: string): Promise<string | undefined> {
|
|
16
|
+
try {
|
|
17
|
+
const result = (await fetchQuery(anyApi.vex.media.getUrl, {
|
|
18
|
+
adapter: "convex",
|
|
19
|
+
mediaId,
|
|
20
|
+
})) as { url?: string; error?: string }
|
|
21
|
+
return result.url
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
9
27
|
/**
|
|
10
28
|
* Generate Next.js Metadata for a page.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
29
|
+
*
|
|
30
|
+
* Fetches site settings and, when a slug is given, the matching page
|
|
31
|
+
* document, then merges them — page-level `metaTitle`/`metaDescription`/
|
|
32
|
+
* `ogImage` win over the site's defaults from `siteSettings`.
|
|
13
33
|
*
|
|
14
34
|
* @param props.slug - Optional page slug to fetch per-page SEO overrides
|
|
15
35
|
*/
|
|
16
|
-
export async function generatePageMetadata(props: {
|
|
17
|
-
slug?: string
|
|
18
|
-
}): Promise<Metadata> {
|
|
36
|
+
export async function generatePageMetadata(props: { slug?: string }): Promise<Metadata> {
|
|
19
37
|
try {
|
|
20
|
-
|
|
21
|
-
const settings = (await fetchQuery(api.siteSettings.get)) as Record<
|
|
22
|
-
string,
|
|
23
|
-
unknown
|
|
24
|
-
> | null
|
|
25
|
-
|
|
38
|
+
const settings = (await fetchQuery(api.siteSettings.get)) as Record<string, unknown> | null
|
|
26
39
|
if (!settings) {
|
|
27
40
|
return { title: "Untitled" }
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
let siteOgImageUrl: string | undefined
|
|
32
|
-
const ogImageId = settings.ogImage as string | undefined
|
|
33
|
-
if (ogImageId) {
|
|
34
|
-
try {
|
|
35
|
-
const mediaDoc = (await fetchQuery(
|
|
36
|
-
anyApi.vex.collections.getDocument,
|
|
37
|
-
{
|
|
38
|
-
collectionSlug: "media",
|
|
39
|
-
documentId: ogImageId,
|
|
40
|
-
}
|
|
41
|
-
)) as Record<string, unknown> | null
|
|
42
|
-
siteOgImageUrl = (mediaDoc?.url as string) || undefined
|
|
43
|
-
} catch {
|
|
44
|
-
// Media document not found — skip ogImage
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Fetch page data for per-page overrides
|
|
49
|
-
let pageData: Record<string, unknown> | null = null
|
|
43
|
+
let pageData: Record<string, unknown> | undefined
|
|
50
44
|
if (props.slug) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
const pages = (await fetchQuery(api.pages.getBySlug, { slug: props.slug })) as
|
|
46
|
+
| Record<string, unknown>[]
|
|
47
|
+
| undefined
|
|
48
|
+
pageData = pages?.[0]
|
|
55
49
|
}
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
ogImage: siteOgImageUrl,
|
|
65
|
-
twitterHandle: settings.twitterHandle as string | undefined,
|
|
66
|
-
},
|
|
67
|
-
page: pageData
|
|
68
|
-
? {
|
|
69
|
-
title: pageData.title as string | undefined,
|
|
70
|
-
metaTitle: pageData.metaTitle as string | undefined,
|
|
71
|
-
metaDescription: pageData.metaDescription as string | undefined,
|
|
72
|
-
ogImage: pageData.ogImage as string | undefined,
|
|
73
|
-
}
|
|
74
|
-
: undefined,
|
|
75
|
-
titleSuffix: " | Vex CMS",
|
|
76
|
-
})
|
|
51
|
+
const pageTitle = (pageData?.metaTitle as string | undefined) ?? (pageData?.title as string | undefined)
|
|
52
|
+
const siteName = settings.name as string | undefined
|
|
53
|
+
const title = (pageTitle ?? (settings.metaTitle as string | undefined) ?? siteName ?? "Untitled") + TITLE_SUFFIX
|
|
54
|
+
const description =
|
|
55
|
+
(pageData?.metaDescription as string | undefined) ??
|
|
56
|
+
(settings.metaDescription as string | undefined) ??
|
|
57
|
+
(settings.description as string | undefined)
|
|
77
58
|
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
59
|
+
// `upload()` fields always store an array of media ids — the first entry
|
|
60
|
+
// is the selection. Page-level ogImage wins over the site default.
|
|
61
|
+
const pageOgImageId = (pageData?.ogImage as string[] | undefined)?.[0]
|
|
62
|
+
const siteOgImageId = (settings.ogImage as string[] | undefined)?.[0]
|
|
63
|
+
const ogImageId = pageOgImageId ?? siteOgImageId
|
|
64
|
+
const ogImageUrl = ogImageId ? await resolveMediaUrl(ogImageId) : undefined
|
|
65
|
+
|
|
66
|
+
const twitterHandle = settings.twitterHandle as string | undefined
|
|
67
|
+
|
|
68
|
+
const metadata: Metadata = { title, description }
|
|
83
69
|
|
|
84
|
-
if (
|
|
85
|
-
metadata.openGraph = {
|
|
86
|
-
title: meta.title,
|
|
87
|
-
description: meta.description,
|
|
88
|
-
images: [{ url: meta.ogImage }],
|
|
89
|
-
}
|
|
70
|
+
if (ogImageUrl) {
|
|
71
|
+
metadata.openGraph = { title, description, images: [{ url: ogImageUrl }] }
|
|
90
72
|
}
|
|
91
73
|
|
|
92
|
-
if (
|
|
93
|
-
metadata.twitter = {
|
|
94
|
-
card: "summary_large_image",
|
|
95
|
-
site: meta.twitterHandle,
|
|
96
|
-
}
|
|
74
|
+
if (twitterHandle) {
|
|
75
|
+
metadata.twitter = { card: "summary_large_image", site: twitterHandle }
|
|
97
76
|
}
|
|
98
77
|
|
|
99
78
|
return metadata
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { betterAuthAdapter } from "@vexcms/better-auth"
|
|
2
|
+
import { defineConfig } from "@vexcms/core"
|
|
3
|
+
import { convexFileStorage } from "@vexcms/file-storage-convex"
|
|
4
|
+
|
|
5
|
+
import { authOptions } from "@convex/auth/options"
|
|
6
|
+
|
|
7
|
+
import { access } from "~/auth/access"
|
|
8
|
+
import { footers, headers, images, pages, themes, users } from "~/vexcms/collections"
|
|
9
|
+
import { siteSettings } from "~/vexcms/globals"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* VexCMS configuration for the marketing site.
|
|
13
|
+
*
|
|
14
|
+
* Replaces `templates/base-nextjs`'s bare config wholesale (overlay copy is
|
|
15
|
+
* file-level, not a merge): carries base's `users`/`images` forward unchanged
|
|
16
|
+
* and adds the four marketing collections plus `siteSettings`. `vex dev`/
|
|
17
|
+
* `vex generate` consume this to produce the Convex schema and TypeScript
|
|
18
|
+
* types.
|
|
19
|
+
*/
|
|
20
|
+
const vexConfig = defineConfig({
|
|
21
|
+
access,
|
|
22
|
+
admin: {
|
|
23
|
+
sidebar: {
|
|
24
|
+
side: "right",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
authAdapter: betterAuthAdapter({ config: authOptions }),
|
|
28
|
+
storage: {
|
|
29
|
+
adapters: [convexFileStorage({ mediaCollections: [images] })],
|
|
30
|
+
},
|
|
31
|
+
collections: [users, pages, headers, footers, themes],
|
|
32
|
+
globals: [siteSettings],
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export default vexConfig
|