create-vexcms 0.0.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.
Files changed (116) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2409 -0
  3. package/package.json +37 -0
  4. package/templates/base-nextjs/.prettierignore +26 -0
  5. package/templates/base-nextjs/.prettierrc +7 -0
  6. package/templates/base-nextjs/README.md +256 -0
  7. package/templates/base-nextjs/_gitignore +2 -0
  8. package/templates/base-nextjs/components.json +24 -0
  9. package/templates/base-nextjs/convex/_generated/api.d.ts +125 -0
  10. package/templates/base-nextjs/convex/_generated/api.js +23 -0
  11. package/templates/base-nextjs/convex/_generated/dataModel.d.ts +60 -0
  12. package/templates/base-nextjs/convex/_generated/server.d.ts +143 -0
  13. package/templates/base-nextjs/convex/_generated/server.js +93 -0
  14. package/templates/base-nextjs/convex/auth/adapter/index.ts +230 -0
  15. package/templates/base-nextjs/convex/auth/adapter/utils.ts +547 -0
  16. package/templates/base-nextjs/convex/auth/api.ts +8 -0
  17. package/templates/base-nextjs/convex/auth/config.ts +10 -0
  18. package/templates/base-nextjs/convex/auth/db.ts +303 -0
  19. package/templates/base-nextjs/convex/auth/index.ts +14 -0
  20. package/templates/base-nextjs/convex/auth/options.ts +63 -0
  21. package/templates/base-nextjs/convex/auth/plugins/index.ts +20 -0
  22. package/templates/base-nextjs/convex/auth/sessions.ts +60 -0
  23. package/templates/base-nextjs/convex/auth.config.ts +7 -0
  24. package/templates/base-nextjs/convex/convex.config.ts +5 -0
  25. package/templates/base-nextjs/convex/http.ts +28 -0
  26. package/templates/base-nextjs/convex/schema.ts +3 -0
  27. package/templates/base-nextjs/convex/vex/auth.ts +38 -0
  28. package/templates/base-nextjs/convex/vex/collections.ts +321 -0
  29. package/templates/base-nextjs/convex/vex/firstUser.ts +134 -0
  30. package/templates/base-nextjs/convex/vex/helpers.ts +33 -0
  31. package/templates/base-nextjs/convex/vex/impersonation.ts +51 -0
  32. package/templates/base-nextjs/convex/vex/media.ts +246 -0
  33. package/templates/base-nextjs/convex/vex/migrate.ts +74 -0
  34. package/templates/base-nextjs/convex/vex/model/collections.ts +196 -0
  35. package/templates/base-nextjs/convex/vex/model/media.ts +87 -0
  36. package/templates/base-nextjs/convex/vex/model/versions.ts +254 -0
  37. package/templates/base-nextjs/convex/vex/previewSnapshot.ts +53 -0
  38. package/templates/base-nextjs/convex/vex/versions.ts +588 -0
  39. package/templates/base-nextjs/eslint.config.mjs +164 -0
  40. package/templates/base-nextjs/next.config.ts +10 -0
  41. package/templates/base-nextjs/package.json +67 -0
  42. package/templates/base-nextjs/postcss.config.mjs +7 -0
  43. package/templates/base-nextjs/public/favicons/favicon.ico +0 -0
  44. package/templates/base-nextjs/public/file.svg +1 -0
  45. package/templates/base-nextjs/public/globe.svg +1 -0
  46. package/templates/base-nextjs/public/next.svg +1 -0
  47. package/templates/base-nextjs/public/vercel.svg +1 -0
  48. package/templates/base-nextjs/public/window.svg +1 -0
  49. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +9 -0
  50. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
  51. package/templates/base-nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
  52. package/templates/base-nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
  53. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +9 -0
  54. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
  55. package/templates/base-nextjs/src/app/(frontend)/layout.tsx +14 -0
  56. package/templates/base-nextjs/src/app/(frontend)/page.tsx +116 -0
  57. package/templates/base-nextjs/src/app/admin/AdminLayoutWrapper.tsx +33 -0
  58. package/templates/base-nextjs/src/app/admin/AdminPageWrapper.tsx +38 -0
  59. package/templates/base-nextjs/src/app/admin/RichTextFieldWithMedia.tsx +101 -0
  60. package/templates/base-nextjs/src/app/admin/[[...path]]/page.tsx +15 -0
  61. package/templates/base-nextjs/src/app/admin/layout.tsx +33 -0
  62. package/templates/base-nextjs/src/app/api/auth/[...all]/route.ts +6 -0
  63. package/templates/base-nextjs/src/app/favicon.ico +0 -0
  64. package/templates/base-nextjs/src/app/globals.css +130 -0
  65. package/templates/base-nextjs/src/app/layout.tsx +30 -0
  66. package/templates/base-nextjs/src/auth/client.tsx +41 -0
  67. package/templates/base-nextjs/src/auth/permissions.ts +102 -0
  68. package/templates/base-nextjs/src/auth/server.ts +16 -0
  69. package/templates/base-nextjs/src/auth/serverUtils.ts +64 -0
  70. package/templates/base-nextjs/src/auth/types.ts +3 -0
  71. package/templates/base-nextjs/src/components/component-example.tsx +474 -0
  72. package/templates/base-nextjs/src/components/example.tsx +52 -0
  73. package/templates/base-nextjs/src/components/providers/client.tsx +9 -0
  74. package/templates/base-nextjs/src/components/providers/convex.tsx +32 -0
  75. package/templates/base-nextjs/src/components/providers/server.tsx +15 -0
  76. package/templates/base-nextjs/src/components/providers/theme.tsx +11 -0
  77. package/templates/base-nextjs/src/components/ui/alert-dialog.tsx +162 -0
  78. package/templates/base-nextjs/src/components/ui/badge.tsx +52 -0
  79. package/templates/base-nextjs/src/components/ui/button.tsx +60 -0
  80. package/templates/base-nextjs/src/components/ui/card.tsx +92 -0
  81. package/templates/base-nextjs/src/components/ui/combobox.tsx +271 -0
  82. package/templates/base-nextjs/src/components/ui/dialog.tsx +135 -0
  83. package/templates/base-nextjs/src/components/ui/dropdown-menu.tsx +246 -0
  84. package/templates/base-nextjs/src/components/ui/field.tsx +224 -0
  85. package/templates/base-nextjs/src/components/ui/input-group.tsx +146 -0
  86. package/templates/base-nextjs/src/components/ui/input.tsx +20 -0
  87. package/templates/base-nextjs/src/components/ui/label.tsx +20 -0
  88. package/templates/base-nextjs/src/components/ui/select.tsx +189 -0
  89. package/templates/base-nextjs/src/components/ui/separator.tsx +21 -0
  90. package/templates/base-nextjs/src/components/ui/textarea.tsx +18 -0
  91. package/templates/base-nextjs/src/components/ui/theme-toggle.tsx +67 -0
  92. package/templates/base-nextjs/src/db/constants/auth.ts +8 -0
  93. package/templates/base-nextjs/src/db/constants/index.ts +44 -0
  94. package/templates/base-nextjs/src/db/types.ts +6 -0
  95. package/templates/base-nextjs/src/env.mjs +48 -0
  96. package/templates/base-nextjs/src/lib/utils.ts +6 -0
  97. package/templates/base-nextjs/src/proxy.ts +23 -0
  98. package/templates/base-nextjs/src/vexcms/access.ts +28 -0
  99. package/templates/base-nextjs/src/vexcms/auth.ts +4 -0
  100. package/templates/base-nextjs/src/vexcms/collections/index.ts +1 -0
  101. package/templates/base-nextjs/src/vexcms/collections/media.ts +15 -0
  102. package/templates/base-nextjs/src/vexcms/collections/users.ts +46 -0
  103. package/templates/base-nextjs/tsconfig.json +60 -0
  104. package/templates/base-nextjs/vex.config.ts +23 -0
  105. package/templates/marketing-site/convex/pages.ts +46 -0
  106. package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +58 -0
  107. package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +69 -0
  108. package/templates/marketing-site/src/app/admin/AdminLayoutWrapper.tsx +71 -0
  109. package/templates/marketing-site/src/db/constants/index.ts +52 -0
  110. package/templates/marketing-site/src/vexcms/collections/footers.ts +28 -0
  111. package/templates/marketing-site/src/vexcms/collections/headers.ts +35 -0
  112. package/templates/marketing-site/src/vexcms/collections/index.ts +7 -0
  113. package/templates/marketing-site/src/vexcms/collections/pages.ts +40 -0
  114. package/templates/marketing-site/src/vexcms/collections/site_settings.ts +31 -0
  115. package/templates/marketing-site/src/vexcms/collections/themes.ts +37 -0
  116. package/templates/marketing-site/vex.config.ts +32 -0
@@ -0,0 +1,33 @@
1
+ import { SidebarTrigger } from "@vexcms/ui"
2
+ import { sanitizeConfigForClient } from "@vexcms/core"
3
+
4
+ import config from "~/../vex.config"
5
+ import { getCurrentUser } from "~/auth/serverUtils"
6
+ import { AdminLayoutWrapper } from "./AdminLayoutWrapper"
7
+
8
+ const clientConfig = sanitizeConfigForClient(config)
9
+
10
+ export default async function AdminLayout({ children }: { children: React.ReactNode }) {
11
+ const user = await getCurrentUser()
12
+
13
+ const permissionUser = user
14
+ ? {
15
+ id: user.id,
16
+ roles: Array.isArray(user.role) ? user.role : [user.role].filter(Boolean),
17
+ name: user.name,
18
+ email: user.email,
19
+ avatar: user.image,
20
+ }
21
+ : undefined
22
+
23
+ return (
24
+ <AdminLayoutWrapper
25
+ config={clientConfig}
26
+ user={user ? { name: user.name, avatar: user.image, email: user.email } : undefined}
27
+ permissionUser={permissionUser}
28
+ >
29
+ <SidebarTrigger />
30
+ <div className="flex flex-col flex-1 min-h-0 overflow-hidden">{children}</div>
31
+ </AdminLayoutWrapper>
32
+ )
33
+ }
@@ -0,0 +1,6 @@
1
+ import { handler } from "~/auth/server"
2
+
3
+ export const dynamic = "force-dynamic"
4
+ export const runtime = "nodejs"
5
+
6
+ export const { GET, POST } = handler
@@ -0,0 +1,130 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @source "../../node_modules/@vexcms/ui/dist/**/*.js";
6
+ @source "../../node_modules/@vexcms/admin-next/dist/**/*.js";
7
+ @source "../../node_modules/@vexcms/admin-next/src/**/*.tsx";
8
+
9
+ @custom-variant dark (&:is(.dark *));
10
+
11
+ @theme inline {
12
+ --color-background: var(--background);
13
+ --color-foreground: var(--foreground);
14
+ --font-sans: var(--font-sans);
15
+ --font-mono: var(--font-geist-mono);
16
+ --color-sidebar-ring: var(--sidebar-ring);
17
+ --color-sidebar-border: var(--sidebar-border);
18
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
19
+ --color-sidebar-accent: var(--sidebar-accent);
20
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
21
+ --color-sidebar-primary: var(--sidebar-primary);
22
+ --color-sidebar-foreground: var(--sidebar-foreground);
23
+ --color-sidebar: var(--sidebar);
24
+ --color-chart-5: var(--chart-5);
25
+ --color-chart-4: var(--chart-4);
26
+ --color-chart-3: var(--chart-3);
27
+ --color-chart-2: var(--chart-2);
28
+ --color-chart-1: var(--chart-1);
29
+ --color-ring: var(--ring);
30
+ --color-input: var(--input);
31
+ --color-border: var(--border);
32
+ --color-destructive: var(--destructive);
33
+ --color-accent-foreground: var(--accent-foreground);
34
+ --color-accent: var(--accent);
35
+ --color-muted-foreground: var(--muted-foreground);
36
+ --color-muted: var(--muted);
37
+ --color-secondary-foreground: var(--secondary-foreground);
38
+ --color-secondary: var(--secondary);
39
+ --color-primary-foreground: var(--primary-foreground);
40
+ --color-primary: var(--primary);
41
+ --color-popover-foreground: var(--popover-foreground);
42
+ --color-popover: var(--popover);
43
+ --color-card-foreground: var(--card-foreground);
44
+ --color-card: var(--card);
45
+ --radius-sm: calc(var(--radius) - 4px);
46
+ --radius-md: calc(var(--radius) - 2px);
47
+ --radius-lg: var(--radius);
48
+ --radius-xl: calc(var(--radius) + 4px);
49
+ --radius-2xl: calc(var(--radius) + 8px);
50
+ --radius-3xl: calc(var(--radius) + 12px);
51
+ --radius-4xl: calc(var(--radius) + 16px);
52
+ }
53
+
54
+ :root {
55
+ --radius: 0.5rem;
56
+ --background: oklch(1 0 0);
57
+ --foreground: oklch(0.145 0.004 240);
58
+ --card: oklch(1 0 0);
59
+ --card-foreground: oklch(0.145 0.004 240);
60
+ --popover: oklch(1 0 0);
61
+ --popover-foreground: oklch(0.145 0.004 240);
62
+ --primary: oklch(0.205 0.003 240);
63
+ --primary-foreground: oklch(0.985 0 0);
64
+ --secondary: oklch(0.965 0.002 240);
65
+ --secondary-foreground: oklch(0.205 0.003 240);
66
+ --muted: oklch(0.965 0.002 240);
67
+ --muted-foreground: oklch(0.556 0.002 240);
68
+ --accent: oklch(0.965 0.002 240);
69
+ --accent-foreground: oklch(0.205 0.003 240);
70
+ --destructive: oklch(0.577 0.184 27.3);
71
+ --border: oklch(0.922 0.003 240);
72
+ --input: oklch(0.922 0.003 240);
73
+ --ring: oklch(0.708 0.003 240);
74
+ --chart-1: oklch(0.646 0.222 41.116);
75
+ --chart-2: oklch(0.6 0.118 184.714);
76
+ --chart-3: oklch(0.398 0.07 227.392);
77
+ --chart-4: oklch(0.828 0.189 84.429);
78
+ --chart-5: oklch(0.769 0.188 70.08);
79
+ --sidebar: oklch(0.985 0 0);
80
+ --sidebar-foreground: oklch(0.145 0.004 240);
81
+ --sidebar-primary: oklch(0.205 0.003 240);
82
+ --sidebar-primary-foreground: oklch(0.985 0 0);
83
+ --sidebar-accent: oklch(0.965 0.002 240);
84
+ --sidebar-accent-foreground: oklch(0.205 0.003 240);
85
+ --sidebar-border: oklch(0.922 0.003 240);
86
+ --sidebar-ring: oklch(0.708 0.003 240);
87
+ }
88
+
89
+ .dark {
90
+ --background: oklch(0.145 0.004 240);
91
+ --foreground: oklch(0.985 0 0);
92
+ --card: oklch(0.145 0.004 240);
93
+ --card-foreground: oklch(0.985 0 0);
94
+ --popover: oklch(0.145 0.004 240);
95
+ --popover-foreground: oklch(0.985 0 0);
96
+ --primary: oklch(0.985 0 0);
97
+ --primary-foreground: oklch(0.205 0.003 240);
98
+ --secondary: oklch(0.269 0.006 240);
99
+ --secondary-foreground: oklch(0.985 0 0);
100
+ --muted: oklch(0.269 0.006 240);
101
+ --muted-foreground: oklch(0.708 0.003 240);
102
+ --accent: oklch(0.269 0.006 240);
103
+ --accent-foreground: oklch(0.985 0 0);
104
+ --destructive: oklch(0.396 0.141 25.768);
105
+ --border: oklch(0.269 0.006 240);
106
+ --input: oklch(0.269 0.006 240);
107
+ --ring: oklch(0.439 0.003 240);
108
+ --chart-1: oklch(0.488 0.243 264.376);
109
+ --chart-2: oklch(0.696 0.17 162.48);
110
+ --chart-3: oklch(0.769 0.188 70.08);
111
+ --chart-4: oklch(0.627 0.265 303.9);
112
+ --chart-5: oklch(0.645 0.246 16.439);
113
+ --sidebar: oklch(0.205 0.003 240);
114
+ --sidebar-foreground: oklch(0.985 0 0);
115
+ --sidebar-primary: oklch(0.488 0.243 264.376);
116
+ --sidebar-primary-foreground: oklch(0.985 0 0);
117
+ --sidebar-accent: oklch(0.269 0.006 240);
118
+ --sidebar-accent-foreground: oklch(0.985 0 0);
119
+ --sidebar-border: oklch(0.269 0.006 240);
120
+ --sidebar-ring: oklch(0.439 0.003 240);
121
+ }
122
+
123
+ @layer base {
124
+ * {
125
+ @apply border-border outline-ring/50;
126
+ }
127
+ body {
128
+ @apply bg-background text-foreground;
129
+ }
130
+ }
@@ -0,0 +1,30 @@
1
+ import type { Metadata } from "next"
2
+ import { Geist, Geist_Mono, Inter } from "next/font/google"
3
+
4
+ import ClientProviders from "~/components/providers/client"
5
+ import ServerProviders from "~/components/providers/server"
6
+
7
+ import "./globals.css"
8
+
9
+ const inter = Inter({ subsets: ["latin"], variable: "--font-sans" })
10
+ const geistSans = Geist({ subsets: ["latin"], variable: "--font-geist-sans" })
11
+ const geistMono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono" })
12
+
13
+ export const metadata: Metadata = {
14
+ description: "Test application for Vex CMS development",
15
+ title: "Vex CMS Test App",
16
+ }
17
+
18
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
19
+ return (
20
+ <html lang="en" suppressHydrationWarning className={`${inter.variable} ${geistSans.variable} ${geistMono.variable}`}>
21
+ <body className="antialiased">
22
+ <ServerProviders>
23
+ <ClientProviders>
24
+ {children}
25
+ </ClientProviders>
26
+ </ServerProviders>
27
+ </body>
28
+ </html>
29
+ )
30
+ }
@@ -0,0 +1,41 @@
1
+ import type { ReactNode } from "react"
2
+
3
+ import { convexClient } from "@convex-dev/better-auth/client/plugins"
4
+ import { AuthUIProvider } from "@daveyplate/better-auth-ui"
5
+ import { adminClient, apiKeyClient } from "better-auth/client/plugins"
6
+ import { createAuthClient } from "better-auth/react"
7
+ import Link from "next/link"
8
+ import { useRouter } from "next/navigation"
9
+
10
+ import { env } from "~/env.mjs"
11
+
12
+ export const authClient = createAuthClient({
13
+ basePath: "/api/auth",
14
+ baseURL: env.NEXT_PUBLIC_SITE_URL,
15
+ plugins: [adminClient(), apiKeyClient(), convexClient()],
16
+ })
17
+
18
+ export const { signIn, signOut, useSession } = authClient
19
+
20
+ // copied from code example, unsure if this is actually useful or not
21
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises, @typescript-eslint/no-empty-function
22
+ authClient.$store.listen("$sessionSignal", async () => { })
23
+
24
+ export default function BetterAuthClientProvider({ children }: { children: ReactNode }) {
25
+ const router = useRouter()
26
+ return (
27
+ <AuthUIProvider
28
+ authClient={authClient}
29
+ /* {{EMAIL_PASSWORD_CREDENTIALS}} */
30
+ // {{OAUTH_UI_PROVIDERS}}
31
+ Link={Link}
32
+ navigate={router.push}
33
+ onSessionChange={() => {
34
+ router.refresh()
35
+ }}
36
+ replace={router.replace}
37
+ >
38
+ {children}
39
+ </AuthUIProvider>
40
+ )
41
+ }
@@ -0,0 +1,102 @@
1
+ import type { User } from "~/db/types"
2
+
3
+ import { TABLE_SLUG_USERS, USER_ROLES, type UserRole } from "~/db/constants"
4
+
5
+ export const AUTH_ACTIONS = {
6
+ create: "create",
7
+ delete: "delete",
8
+ read: "read",
9
+ update: "update",
10
+ } as const
11
+ export type AuthAction = (typeof AUTH_ACTIONS)[keyof typeof AUTH_ACTIONS]
12
+
13
+ export const RESOURCES = {
14
+ users: TABLE_SLUG_USERS,
15
+ } as const
16
+ export type Permissions = {
17
+ [TABLE_SLUG_USERS]: {
18
+ action: AuthAction
19
+ dataType: User
20
+ }
21
+ }
22
+
23
+ export type Resource = (typeof RESOURCES)[keyof typeof RESOURCES]
24
+
25
+ export type RolesWithPermissions = Record<
26
+ UserRole,
27
+ Partial<{
28
+ [Key in keyof Permissions]: Partial<Record<Permissions[Key]["action"], PermissionCheck<Key>>>
29
+ }>
30
+ >
31
+
32
+ type PermissionCheck<Key extends keyof Permissions> =
33
+ | (({ data, user }: { data: Permissions[Key]["dataType"]; user: User }) => boolean)
34
+ | boolean
35
+
36
+ // type PermissionDataTypeExcluding<K extends keyof Permissions> = Permissions[Exclude<keyof Permissions, K>]["dataType"];
37
+ // usage: T extends PermissionDataTypeExcluding<"orgs" | "users">
38
+
39
+ const ROLES = {
40
+ [USER_ROLES.admin]: {
41
+ [TABLE_SLUG_USERS]: {
42
+ create: true,
43
+ delete: true,
44
+ read: true,
45
+ update: true,
46
+ },
47
+ },
48
+ [USER_ROLES.editor]: {
49
+ [TABLE_SLUG_USERS]: {
50
+ read: true,
51
+ },
52
+ },
53
+ [USER_ROLES.author]: {
54
+ [TABLE_SLUG_USERS]: {
55
+ read: ({ data: targetUser, user }) => user._id === targetUser._id,
56
+ update: ({ data: targetUser, user }) => user._id === targetUser._id,
57
+ },
58
+ },
59
+ [USER_ROLES.member]: {
60
+ [TABLE_SLUG_USERS]: {
61
+ read: ({ data: targetUser, user }) => user._id === targetUser._id,
62
+ },
63
+ },
64
+ [USER_ROLES.user]: {
65
+ [TABLE_SLUG_USERS]: {
66
+ create: false,
67
+ delete: ({ data: targetUser, user }) => user._id === targetUser._id,
68
+ read: ({ data: targetUser, user }) => user._id === targetUser._id,
69
+ update: ({ data: targetUser, user }) => user._id === targetUser._id,
70
+ },
71
+ },
72
+ } as const satisfies RolesWithPermissions
73
+
74
+ export function hasPermission<Resource extends keyof Permissions>({
75
+ action,
76
+ data,
77
+ resource,
78
+ user,
79
+ }: {
80
+ action: Permissions[Resource]["action"]
81
+ data?: Permissions[Resource]["dataType"]
82
+ resource: Resource
83
+ user?: null | User
84
+ }): boolean {
85
+ if (!user?.role) {
86
+ return false
87
+ }
88
+
89
+ return user.role.some((role) => {
90
+ const permission = (ROLES as RolesWithPermissions)[role as UserRole][resource]?.[action]
91
+
92
+ if (!permission) {
93
+ return false
94
+ }
95
+
96
+ if (typeof permission === "boolean") {
97
+ return permission
98
+ }
99
+
100
+ return data != null && permission({ data, user })
101
+ })
102
+ }
@@ -0,0 +1,16 @@
1
+ import { convexBetterAuthNextJs } from "@convex-dev/better-auth/nextjs"
2
+
3
+ import { env } from "~/env.mjs"
4
+
5
+ export const {
6
+ fetchAuthAction,
7
+ fetchAuthMutation,
8
+ fetchAuthQuery,
9
+ getToken,
10
+ handler,
11
+ isAuthenticated,
12
+ preloadAuthQuery,
13
+ } = convexBetterAuthNextJs({
14
+ convexSiteUrl: env.NEXT_PUBLIC_CONVEX_SITE_URL,
15
+ convexUrl: env.NEXT_PUBLIC_CONVEX_URL,
16
+ })
@@ -0,0 +1,64 @@
1
+ import { api } from "@convex/_generated/api"
2
+ import { fetchQuery } from "convex/nextjs"
3
+ import { cookies } from "next/headers"
4
+
5
+ export async function getSessionToken() {
6
+ const cookieStore = await cookies()
7
+ const sessionToken = cookieStore.get("better-auth.session_token")?.value.split(".")[0]
8
+ if (!sessionToken) {
9
+ console.error("Error getting session token")
10
+ return null
11
+ }
12
+ return sessionToken
13
+ }
14
+
15
+ /**
16
+ * Get current user session in Next.js server components/actions
17
+ * Validates the Better Auth session token from cookies and fetches user data
18
+ */
19
+ export const getCurrentUser = async () => {
20
+ try {
21
+ const sessionToken = await getSessionToken()
22
+
23
+ if (!sessionToken) {
24
+ return null
25
+ }
26
+
27
+ const session = await fetchQuery(api.auth.sessions.getSessionWithUser, {
28
+ sessionToken,
29
+ })
30
+ if (!session?.user) {
31
+ return null
32
+ }
33
+ return session.user
34
+ } catch (error) {
35
+ console.error("Error getting current user:", error)
36
+ return null
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get full session (user + session data) in Next.js server components/actions
42
+ */
43
+ export async function getSession() {
44
+ try {
45
+ const sessionToken = await getSessionToken()
46
+
47
+ if (!sessionToken) {
48
+ console.log("no session token")
49
+ return null
50
+ }
51
+
52
+ const session = await fetchQuery(api.auth.sessions.getSessionWithUser, {
53
+ sessionToken,
54
+ })
55
+ if (!session?.user) {
56
+ console.log("no session")
57
+ return null
58
+ }
59
+ return session
60
+ } catch (error) {
61
+ console.error("Error getting session:", error)
62
+ return null
63
+ }
64
+ }
@@ -0,0 +1,3 @@
1
+ import type { getSession } from "./serverUtils"
2
+
3
+ export type ServerAuthContext = Awaited<ReturnType<typeof getSession>>