@onmax/nuxt-better-auth 0.0.2-alpha.2 → 0.0.2-alpha.21
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/dist/module.json +1 -1
- package/dist/module.mjs +470 -184
- package/dist/runtime/adapters/convex.d.ts +111 -0
- package/dist/runtime/adapters/convex.js +213 -0
- package/dist/runtime/app/components/BetterAuthState.vue +1 -0
- package/dist/runtime/app/composables/useUserSession.d.ts +13 -11
- package/dist/runtime/app/composables/useUserSession.js +13 -12
- package/dist/runtime/app/middleware/auth.global.js +7 -1
- package/dist/runtime/app/plugins/session.client.js +1 -2
- package/dist/runtime/app/plugins/session.server.js +2 -1
- package/dist/runtime/config.d.ts +18 -5
- package/dist/runtime/config.js +7 -2
- package/dist/runtime/server/api/_better-auth/accounts.get.js +1 -1
- package/dist/runtime/server/api/_better-auth/config.get.d.ts +9 -9
- package/dist/runtime/server/api/_better-auth/config.get.js +3 -2
- package/dist/runtime/server/api/_better-auth/sessions.delete.js +1 -1
- package/dist/runtime/server/api/_better-auth/sessions.get.js +11 -2
- package/dist/runtime/server/api/_better-auth/users.get.js +1 -1
- package/dist/runtime/server/api/auth/[...all].js +1 -1
- package/dist/runtime/server/utils/auth.d.ts +5 -8
- package/dist/runtime/server/utils/auth.js +66 -17
- package/dist/runtime/server/utils/session.js +2 -1
- package/dist/runtime/types/augment.d.ts +1 -1
- package/dist/runtime/types.d.ts +1 -1
- package/package.json +36 -11
package/dist/module.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { existsSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addTypeTemplate, updateTemplates, addServerImportsDir, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, extendPages } from '@nuxt/kit';
|
|
4
|
+
import { defineNuxtModule, createResolver, hasNuxtModule, addTemplate, addTypeTemplate, updateTemplates, addServerImportsDir, addServerImports, addServerScanDir, addServerHandler, addImportsDir, addPlugin, addComponentsDir, installModule, extendPages } from '@nuxt/kit';
|
|
4
5
|
import { consola as consola$1 } from 'consola';
|
|
5
6
|
import { defu } from 'defu';
|
|
6
|
-
import { join } from 'pathe';
|
|
7
|
+
import { join, dirname } from 'pathe';
|
|
7
8
|
import { toRouteMatcher, createRouter } from 'radix3';
|
|
9
|
+
import { isCI, isTest } from 'std-env';
|
|
10
|
+
import { generateDrizzleSchema as generateDrizzleSchema$1 } from '@better-auth/cli/api';
|
|
11
|
+
import { getAuthTables } from 'better-auth/db';
|
|
8
12
|
export { defineClientAuth, defineServerAuth } from '../dist/runtime/config.js';
|
|
9
13
|
|
|
14
|
+
const version = "0.0.2-alpha.21";
|
|
15
|
+
|
|
10
16
|
function setupDevTools(nuxt) {
|
|
11
17
|
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
12
18
|
tabs.push({
|
|
@@ -22,178 +28,363 @@ function setupDevTools(nuxt) {
|
|
|
22
28
|
});
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
const tableDefinitions = Object.entries(tables).map(([tableName, table]) => generateTable(tableName, table, dialect, tables)).join("\n\n");
|
|
28
|
-
return `${imports}
|
|
29
|
-
|
|
30
|
-
${tableDefinitions}
|
|
31
|
-
`;
|
|
32
|
-
}
|
|
33
|
-
function getImports(dialect) {
|
|
34
|
-
switch (dialect) {
|
|
35
|
-
case "sqlite":
|
|
36
|
-
return `import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'`;
|
|
37
|
-
case "postgresql":
|
|
38
|
-
return `import { boolean, pgTable, text, timestamp, integer } from 'drizzle-orm/pg-core'`;
|
|
39
|
-
case "mysql":
|
|
40
|
-
return `import { boolean, int, mysqlTable, text, timestamp, varchar } from 'drizzle-orm/mysql-core'`;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function generateTable(tableName, table, dialect, allTables) {
|
|
44
|
-
const tableFunc = dialect === "sqlite" ? "sqliteTable" : dialect === "postgresql" ? "pgTable" : "mysqlTable";
|
|
45
|
-
const dbTableName = table.modelName || tableName;
|
|
46
|
-
const fields = Object.entries(table.fields).map(([fieldName, field]) => generateField(fieldName, field, dialect, allTables)).join(",\n ");
|
|
47
|
-
const idField = generateIdField(dialect);
|
|
48
|
-
return `export const ${tableName} = ${tableFunc}('${dbTableName}', {
|
|
49
|
-
${idField},
|
|
50
|
-
${fields}
|
|
51
|
-
})`;
|
|
52
|
-
}
|
|
53
|
-
function generateIdField(dialect) {
|
|
54
|
-
switch (dialect) {
|
|
55
|
-
case "sqlite":
|
|
56
|
-
case "postgresql":
|
|
57
|
-
return `id: text('id').primaryKey()`;
|
|
58
|
-
case "mysql":
|
|
59
|
-
return `id: varchar('id', { length: 36 }).primaryKey()`;
|
|
60
|
-
}
|
|
31
|
+
function dialectToProvider(dialect) {
|
|
32
|
+
return dialect === "postgresql" ? "pg" : dialect;
|
|
61
33
|
}
|
|
62
|
-
function
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
function getFieldType(type, dialect, fieldName) {
|
|
87
|
-
const normalizedType = Array.isArray(type) ? "string" : type;
|
|
88
|
-
switch (dialect) {
|
|
89
|
-
case "sqlite":
|
|
90
|
-
return getSqliteType(normalizedType, fieldName);
|
|
91
|
-
case "postgresql":
|
|
92
|
-
return getPostgresType(normalizedType, fieldName);
|
|
93
|
-
case "mysql":
|
|
94
|
-
return getMysqlType(normalizedType, fieldName);
|
|
34
|
+
async function generateDrizzleSchema(authOptions, dialect, schemaOptions) {
|
|
35
|
+
const provider = dialectToProvider(dialect);
|
|
36
|
+
const options = {
|
|
37
|
+
...authOptions,
|
|
38
|
+
advanced: {
|
|
39
|
+
...authOptions.advanced,
|
|
40
|
+
database: {
|
|
41
|
+
...authOptions.advanced?.database,
|
|
42
|
+
...schemaOptions?.useUuid && { generateId: "uuid" }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const adapter = {
|
|
47
|
+
id: "drizzle",
|
|
48
|
+
options: {
|
|
49
|
+
provider,
|
|
50
|
+
camelCase: schemaOptions?.casing !== "snake_case",
|
|
51
|
+
adapterConfig: { usePlural: schemaOptions?.usePlural ?? false }
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const result = await generateDrizzleSchema$1({ adapter, options });
|
|
55
|
+
if (!result.code) {
|
|
56
|
+
throw new Error(`Schema generation returned empty result for ${dialect}`);
|
|
95
57
|
}
|
|
58
|
+
return result.code;
|
|
96
59
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
60
|
+
const convexIndexFields = {
|
|
61
|
+
account: ["accountId", ["accountId", "providerId"], ["providerId", "userId"]],
|
|
62
|
+
rateLimit: ["key"],
|
|
63
|
+
session: ["expiresAt", ["expiresAt", "userId"]],
|
|
64
|
+
verification: ["expiresAt", "identifier"],
|
|
65
|
+
user: [["email", "name"], "name", "userId"],
|
|
66
|
+
passkey: ["credentialID"],
|
|
67
|
+
oauthConsent: [["clientId", "userId"]]
|
|
68
|
+
};
|
|
69
|
+
function getConvexSpecialFields(tables) {
|
|
70
|
+
return Object.fromEntries(
|
|
71
|
+
Object.entries(tables).map(([key, table]) => {
|
|
72
|
+
const fields = Object.fromEntries(
|
|
73
|
+
Object.entries(table.fields).map(([fieldKey, field]) => [
|
|
74
|
+
field.fieldName ?? fieldKey,
|
|
75
|
+
{
|
|
76
|
+
...field.sortable ? { sortable: true } : {},
|
|
77
|
+
...field.unique ? { unique: true } : {},
|
|
78
|
+
...field.references ? { references: field.references } : {}
|
|
79
|
+
}
|
|
80
|
+
]).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true)
|
|
81
|
+
);
|
|
82
|
+
return [key, fields];
|
|
83
|
+
}).filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true)
|
|
84
|
+
);
|
|
110
85
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
86
|
+
function getMergedConvexIndexFields(tables) {
|
|
87
|
+
return Object.fromEntries(
|
|
88
|
+
Object.entries(tables).map(([key, table]) => {
|
|
89
|
+
const manualIndexes = convexIndexFields[key]?.map((index) => {
|
|
90
|
+
return typeof index === "string" ? table.fields[index]?.fieldName ?? index : index.map((i) => table.fields[i]?.fieldName ?? i);
|
|
91
|
+
}) || [];
|
|
92
|
+
const specialFieldsObj = getConvexSpecialFields(tables);
|
|
93
|
+
const specialFieldIndexes = Object.keys(specialFieldsObj[key] || {}).filter(
|
|
94
|
+
(index) => !manualIndexes.some((m) => Array.isArray(m) ? m[0] === index : m === index)
|
|
95
|
+
);
|
|
96
|
+
return [key, manualIndexes.concat(specialFieldIndexes)];
|
|
97
|
+
})
|
|
98
|
+
);
|
|
124
99
|
}
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
async function generateConvexSchema(authOptions) {
|
|
101
|
+
const tables = getAuthTables(authOptions);
|
|
102
|
+
let code = `/**
|
|
103
|
+
* Auto-generated Better Auth tables for Convex.
|
|
104
|
+
* Import these tables in your convex/schema.ts:
|
|
105
|
+
*
|
|
106
|
+
* import { defineSchema } from 'convex/server'
|
|
107
|
+
* import { authTables } from './_generated/auth-tables'
|
|
108
|
+
*
|
|
109
|
+
* export default defineSchema({ ...authTables, ...yourTables })
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
import { defineTable } from 'convex/server'
|
|
113
|
+
import { v } from 'convex/values'
|
|
114
|
+
|
|
115
|
+
export const authTables = {
|
|
116
|
+
`;
|
|
117
|
+
const getType = (_name, field) => {
|
|
118
|
+
const type = field.type;
|
|
119
|
+
const typeMap = {
|
|
120
|
+
"string": "v.string()",
|
|
121
|
+
"boolean": "v.boolean()",
|
|
122
|
+
"number": "v.number()",
|
|
123
|
+
"date": "v.number()",
|
|
124
|
+
"json": "v.string()",
|
|
125
|
+
"number[]": "v.array(v.number())",
|
|
126
|
+
"string[]": "v.array(v.string())"
|
|
127
|
+
};
|
|
128
|
+
return typeMap[type];
|
|
129
|
+
};
|
|
130
|
+
for (const tableKey in tables) {
|
|
131
|
+
const table = tables[tableKey];
|
|
132
|
+
const modelName = table.modelName;
|
|
133
|
+
const fields = Object.fromEntries(Object.entries(table.fields).filter(([key]) => key !== "id"));
|
|
134
|
+
const indexes = getMergedConvexIndexFields(tables)[tableKey]?.map((index) => {
|
|
135
|
+
const indexArray = Array.isArray(index) ? index.sort() : [index];
|
|
136
|
+
const indexName = indexArray.join("_");
|
|
137
|
+
return `.index('${indexName}', ${JSON.stringify(indexArray)})`;
|
|
138
|
+
}) || [];
|
|
139
|
+
const schema = `${modelName}: defineTable({
|
|
140
|
+
${Object.keys(fields).map((field) => {
|
|
141
|
+
const attr = fields[field];
|
|
142
|
+
const type = getType(field, attr);
|
|
143
|
+
const optional = (fieldSchema) => attr.required ? fieldSchema : `v.optional(v.union(v.null(), ${fieldSchema}))`;
|
|
144
|
+
return ` ${attr.fieldName ?? field}: ${optional(type)},`;
|
|
145
|
+
}).join("\n")}
|
|
146
|
+
})${indexes.length > 0 ? `
|
|
147
|
+
${indexes.join("\n ")}` : ""},
|
|
148
|
+
`;
|
|
149
|
+
code += ` ${schema}`;
|
|
137
150
|
}
|
|
151
|
+
code += `}
|
|
152
|
+
`;
|
|
153
|
+
return code;
|
|
138
154
|
}
|
|
139
|
-
async function loadUserAuthConfig(configPath) {
|
|
155
|
+
async function loadUserAuthConfig(configPath, throwOnError = false) {
|
|
140
156
|
const { createJiti } = await import('jiti');
|
|
141
|
-
const
|
|
157
|
+
const { defineServerAuth } = await import('../dist/runtime/config.js');
|
|
158
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
|
|
159
|
+
if (!globalThis.defineServerAuth) {
|
|
160
|
+
defineServerAuth._count = 0;
|
|
161
|
+
globalThis.defineServerAuth = defineServerAuth;
|
|
162
|
+
}
|
|
163
|
+
globalThis.defineServerAuth._count++;
|
|
142
164
|
try {
|
|
143
165
|
const mod = await jiti.import(configPath);
|
|
144
|
-
const configFn = mod.default
|
|
166
|
+
const configFn = mod.default;
|
|
145
167
|
if (typeof configFn === "function") {
|
|
146
168
|
return configFn({ runtimeConfig: {}, db: null });
|
|
147
169
|
}
|
|
170
|
+
consola$1.warn("[@onmax/nuxt-better-auth] auth.config.ts does not export default. Expected: export default defineServerAuth(...)");
|
|
171
|
+
if (throwOnError) {
|
|
172
|
+
throw new Error("auth.config.ts must export default defineServerAuth(...)");
|
|
173
|
+
}
|
|
148
174
|
return {};
|
|
149
175
|
} catch (error) {
|
|
176
|
+
if (throwOnError) {
|
|
177
|
+
throw new Error(`Failed to load auth config: ${error instanceof Error ? error.message : error}`);
|
|
178
|
+
}
|
|
150
179
|
consola$1.error("[@onmax/nuxt-better-auth] Failed to load auth config for schema generation. Schema may be incomplete:", error);
|
|
151
180
|
return {};
|
|
181
|
+
} finally {
|
|
182
|
+
globalThis.defineServerAuth._count--;
|
|
183
|
+
if (!globalThis.defineServerAuth._count) {
|
|
184
|
+
globalThis.defineServerAuth = void 0;
|
|
185
|
+
}
|
|
152
186
|
}
|
|
153
187
|
}
|
|
154
188
|
|
|
189
|
+
function getHubDialect(hub) {
|
|
190
|
+
if (!hub?.db)
|
|
191
|
+
return void 0;
|
|
192
|
+
if (typeof hub.db === "string")
|
|
193
|
+
return hub.db;
|
|
194
|
+
if (typeof hub.db === "object" && hub.db !== null)
|
|
195
|
+
return hub.db.dialect;
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
function getHubCasing(hub) {
|
|
199
|
+
if (!hub?.db || typeof hub.db !== "object" || hub.db === null)
|
|
200
|
+
return void 0;
|
|
201
|
+
return hub.db.casing;
|
|
202
|
+
}
|
|
155
203
|
const consola = consola$1.withTag("nuxt-better-auth");
|
|
204
|
+
function resolveConvexUrl(nuxt) {
|
|
205
|
+
const convexConfig = nuxt.options.convex;
|
|
206
|
+
if (convexConfig?.url)
|
|
207
|
+
return convexConfig.url;
|
|
208
|
+
const runtimeUrl = nuxt.options.runtimeConfig.public?.convex?.url;
|
|
209
|
+
if (runtimeUrl)
|
|
210
|
+
return runtimeUrl;
|
|
211
|
+
if (process.env.CONVEX_URL)
|
|
212
|
+
return process.env.CONVEX_URL;
|
|
213
|
+
if (process.env.NUXT_PUBLIC_CONVEX_URL)
|
|
214
|
+
return process.env.NUXT_PUBLIC_CONVEX_URL;
|
|
215
|
+
return "";
|
|
216
|
+
}
|
|
217
|
+
const generateSecret = () => randomBytes(32).toString("hex");
|
|
218
|
+
function readEnvFile(rootDir) {
|
|
219
|
+
const envPath = join(rootDir, ".env");
|
|
220
|
+
return existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
|
|
221
|
+
}
|
|
222
|
+
function hasEnvSecret(rootDir) {
|
|
223
|
+
const match = readEnvFile(rootDir).match(/^BETTER_AUTH_SECRET=(.+)$/m);
|
|
224
|
+
return !!match && !!match[1] && match[1].trim().length > 0;
|
|
225
|
+
}
|
|
226
|
+
function appendSecretToEnv(rootDir, secret) {
|
|
227
|
+
const envPath = join(rootDir, ".env");
|
|
228
|
+
let content = readEnvFile(rootDir);
|
|
229
|
+
if (content.length > 0 && !content.endsWith("\n"))
|
|
230
|
+
content += "\n";
|
|
231
|
+
content += `BETTER_AUTH_SECRET=${secret}
|
|
232
|
+
`;
|
|
233
|
+
writeFileSync(envPath, content, "utf-8");
|
|
234
|
+
}
|
|
235
|
+
async function promptForSecret(rootDir) {
|
|
236
|
+
if (process.env.BETTER_AUTH_SECRET || hasEnvSecret(rootDir))
|
|
237
|
+
return void 0;
|
|
238
|
+
if (isCI || isTest) {
|
|
239
|
+
const secret2 = generateSecret();
|
|
240
|
+
appendSecretToEnv(rootDir, secret2);
|
|
241
|
+
consola.info("Generated BETTER_AUTH_SECRET and added to .env (CI mode)");
|
|
242
|
+
return secret2;
|
|
243
|
+
}
|
|
244
|
+
consola.box("BETTER_AUTH_SECRET is required for authentication.\nThis will be appended to your .env file.");
|
|
245
|
+
const choice = await consola.prompt("How do you want to set it?", {
|
|
246
|
+
type: "select",
|
|
247
|
+
options: [
|
|
248
|
+
{ label: "Generate for me", value: "generate", hint: "uses crypto.randomBytes(32)" },
|
|
249
|
+
{ label: "Enter manually", value: "paste" },
|
|
250
|
+
{ label: "Skip", value: "skip", hint: "will fail in production" }
|
|
251
|
+
],
|
|
252
|
+
cancel: "null"
|
|
253
|
+
});
|
|
254
|
+
if (typeof choice === "symbol" || choice === "skip") {
|
|
255
|
+
consola.warn("Skipping BETTER_AUTH_SECRET. Auth will fail without it in production.");
|
|
256
|
+
return void 0;
|
|
257
|
+
}
|
|
258
|
+
let secret;
|
|
259
|
+
if (choice === "generate") {
|
|
260
|
+
secret = generateSecret();
|
|
261
|
+
} else {
|
|
262
|
+
const input = await consola.prompt("Paste your secret (min 32 chars):", { type: "text", cancel: "null" });
|
|
263
|
+
if (typeof input === "symbol" || !input || input.length < 32) {
|
|
264
|
+
consola.warn("Invalid secret. Skipping.");
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
secret = input;
|
|
268
|
+
}
|
|
269
|
+
const preview = `${secret.slice(0, 8)}...${secret.slice(-4)}`;
|
|
270
|
+
const confirm = await consola.prompt(`Add to .env:
|
|
271
|
+
BETTER_AUTH_SECRET=${preview}
|
|
272
|
+
Proceed?`, { type: "confirm", initial: true, cancel: "null" });
|
|
273
|
+
if (typeof confirm === "symbol" || !confirm) {
|
|
274
|
+
consola.info("Cancelled. Secret not written.");
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
277
|
+
appendSecretToEnv(rootDir, secret);
|
|
278
|
+
consola.success("Added BETTER_AUTH_SECRET to .env");
|
|
279
|
+
return secret;
|
|
280
|
+
}
|
|
156
281
|
const module$1 = defineNuxtModule({
|
|
157
|
-
meta: { name: "@onmax/nuxt-better-auth", configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
|
|
282
|
+
meta: { name: "@onmax/nuxt-better-auth", version, configKey: "auth", compatibility: { nuxt: ">=3.0.0" } },
|
|
158
283
|
defaults: {
|
|
284
|
+
clientOnly: false,
|
|
159
285
|
serverConfig: "server/auth.config",
|
|
160
286
|
clientConfig: "app/auth.config",
|
|
161
287
|
redirects: { login: "/login", guest: "/" },
|
|
162
288
|
secondaryStorage: false
|
|
163
289
|
},
|
|
290
|
+
async onInstall(nuxt) {
|
|
291
|
+
const generatedSecret = await promptForSecret(nuxt.options.rootDir);
|
|
292
|
+
if (generatedSecret)
|
|
293
|
+
process.env.BETTER_AUTH_SECRET = generatedSecret;
|
|
294
|
+
const serverPath = join(nuxt.options.rootDir, "server/auth.config.ts");
|
|
295
|
+
const clientPath = join(nuxt.options.srcDir, "auth.config.ts");
|
|
296
|
+
const serverTemplate = `import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
|
|
297
|
+
|
|
298
|
+
export default defineServerAuth({
|
|
299
|
+
emailAndPassword: { enabled: true },
|
|
300
|
+
})
|
|
301
|
+
`;
|
|
302
|
+
const clientTemplate = `import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
|
|
303
|
+
|
|
304
|
+
export default defineClientAuth({})
|
|
305
|
+
`;
|
|
306
|
+
if (!existsSync(serverPath)) {
|
|
307
|
+
await mkdir(dirname(serverPath), { recursive: true });
|
|
308
|
+
await writeFile(serverPath, serverTemplate);
|
|
309
|
+
consola.success("Created server/auth.config.ts");
|
|
310
|
+
}
|
|
311
|
+
if (!existsSync(clientPath)) {
|
|
312
|
+
await mkdir(dirname(clientPath), { recursive: true });
|
|
313
|
+
await writeFile(clientPath, clientTemplate);
|
|
314
|
+
const relativePath = clientPath.replace(`${nuxt.options.rootDir}/`, "");
|
|
315
|
+
consola.success(`Created ${relativePath}`);
|
|
316
|
+
}
|
|
317
|
+
},
|
|
164
318
|
async setup(options, nuxt) {
|
|
165
319
|
const resolver = createResolver(import.meta.url);
|
|
320
|
+
if (options.clientConfig === "app/auth.config") {
|
|
321
|
+
const srcDirRelative = nuxt.options.srcDir.replace(`${nuxt.options.rootDir}/`, "");
|
|
322
|
+
options.clientConfig = srcDirRelative === nuxt.options.srcDir ? "auth.config" : `${srcDirRelative}/auth.config`;
|
|
323
|
+
}
|
|
324
|
+
const clientOnly = options.clientOnly;
|
|
166
325
|
const serverConfigFile = options.serverConfig;
|
|
167
326
|
const clientConfigFile = options.clientConfig;
|
|
168
327
|
const serverConfigPath = resolver.resolve(nuxt.options.rootDir, serverConfigFile);
|
|
169
328
|
const clientConfigPath = resolver.resolve(nuxt.options.rootDir, clientConfigFile);
|
|
170
329
|
const serverConfigExists = existsSync(`${serverConfigPath}.ts`) || existsSync(`${serverConfigPath}.js`);
|
|
171
330
|
const clientConfigExists = existsSync(`${clientConfigPath}.ts`) || existsSync(`${clientConfigPath}.js`);
|
|
172
|
-
if (!serverConfigExists)
|
|
173
|
-
throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts -
|
|
331
|
+
if (!clientOnly && !serverConfigExists)
|
|
332
|
+
throw new Error(`[nuxt-better-auth] Missing ${serverConfigFile}.ts - export default defineServerAuth(...)`);
|
|
174
333
|
if (!clientConfigExists)
|
|
175
|
-
throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export
|
|
334
|
+
throw new Error(`[nuxt-better-auth] Missing ${clientConfigFile}.ts - export default defineClientAuth(...)`);
|
|
176
335
|
const hasNuxtHub = hasNuxtModule("@nuxthub/core", nuxt);
|
|
177
336
|
const hub = hasNuxtHub ? nuxt.options.hub : void 0;
|
|
178
|
-
const hasHubDb = hasNuxtHub && !!hub?.db;
|
|
337
|
+
const hasHubDb = !clientOnly && hasNuxtHub && !!hub?.db;
|
|
338
|
+
const hasConvex = hasNuxtModule("nuxt-convex", nuxt);
|
|
339
|
+
const convexUrl = resolveConvexUrl(nuxt);
|
|
340
|
+
const hasConvexDb = !clientOnly && hasConvex && !!convexUrl && !hasHubDb;
|
|
341
|
+
if (hasConvexDb) {
|
|
342
|
+
consola.info("Detected Convex - using Convex HTTP adapter for Better Auth database");
|
|
343
|
+
}
|
|
179
344
|
let secondaryStorageEnabled = options.secondaryStorage ?? false;
|
|
180
|
-
if (secondaryStorageEnabled &&
|
|
345
|
+
if (secondaryStorageEnabled && clientOnly) {
|
|
346
|
+
consola.warn("secondaryStorage is not available in clientOnly mode. Disabling.");
|
|
347
|
+
secondaryStorageEnabled = false;
|
|
348
|
+
} else if (secondaryStorageEnabled && (!hasNuxtHub || !hub?.kv)) {
|
|
181
349
|
consola.warn("secondaryStorage requires @nuxthub/core with hub.kv: true. Disabling.");
|
|
182
350
|
secondaryStorageEnabled = false;
|
|
183
351
|
}
|
|
184
352
|
nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
|
|
185
353
|
nuxt.options.runtimeConfig.public.auth = defu(nuxt.options.runtimeConfig.public.auth, {
|
|
186
354
|
redirects: { login: options.redirects?.login ?? "/login", guest: options.redirects?.guest ?? "/" },
|
|
187
|
-
useDatabase: hasHubDb
|
|
188
|
-
|
|
189
|
-
nuxt.options.runtimeConfig.betterAuthSecret ||= process.env.BETTER_AUTH_SECRET || process.env.NUXT_BETTER_AUTH_SECRET || "";
|
|
190
|
-
nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
|
|
191
|
-
secondaryStorage: secondaryStorageEnabled
|
|
355
|
+
useDatabase: hasHubDb || hasConvexDb,
|
|
356
|
+
clientOnly
|
|
192
357
|
});
|
|
358
|
+
if (clientOnly) {
|
|
359
|
+
const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || nuxt.options.runtimeConfig.public.siteUrl;
|
|
360
|
+
if (!siteUrl) {
|
|
361
|
+
consola.warn("clientOnly mode: NUXT_PUBLIC_SITE_URL should be set to your frontend URL");
|
|
362
|
+
}
|
|
363
|
+
consola.info("clientOnly mode enabled - server utilities (serverAuth, getUserSession, requireUserSession) are not available");
|
|
364
|
+
}
|
|
365
|
+
if (!clientOnly) {
|
|
366
|
+
const currentSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
367
|
+
nuxt.options.runtimeConfig.betterAuthSecret = currentSecret || process.env.BETTER_AUTH_SECRET || "";
|
|
368
|
+
const betterAuthSecret = nuxt.options.runtimeConfig.betterAuthSecret;
|
|
369
|
+
if (!nuxt.options.dev && !nuxt.options._prepare && !betterAuthSecret) {
|
|
370
|
+
throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET is required in production. Set BETTER_AUTH_SECRET or NUXT_BETTER_AUTH_SECRET environment variable.");
|
|
371
|
+
}
|
|
372
|
+
if (betterAuthSecret && betterAuthSecret.length < 32) {
|
|
373
|
+
throw new Error("[nuxt-better-auth] BETTER_AUTH_SECRET must be at least 32 characters for security");
|
|
374
|
+
}
|
|
375
|
+
nuxt.options.runtimeConfig.auth = defu(nuxt.options.runtimeConfig.auth, {
|
|
376
|
+
secondaryStorage: secondaryStorageEnabled
|
|
377
|
+
});
|
|
378
|
+
}
|
|
193
379
|
nuxt.options.alias["#nuxt-better-auth"] = resolver.resolve("./runtime/types/augment");
|
|
194
|
-
|
|
380
|
+
if (!clientOnly)
|
|
381
|
+
nuxt.options.alias["#auth/server"] = serverConfigPath;
|
|
195
382
|
nuxt.options.alias["#auth/client"] = clientConfigPath;
|
|
196
|
-
|
|
383
|
+
if (!clientOnly) {
|
|
384
|
+
if (secondaryStorageEnabled && !nuxt.options.alias["hub:kv"]) {
|
|
385
|
+
throw new Error("[nuxt-better-auth] hub:kv not found. Ensure @nuxthub/core is loaded before this module and hub.kv is enabled.");
|
|
386
|
+
}
|
|
387
|
+
const secondaryStorageCode = secondaryStorageEnabled ? `import { kv } from '@nuxthub/kv'
|
|
197
388
|
export function createSecondaryStorage() {
|
|
198
389
|
return {
|
|
199
390
|
get: async (key) => kv.get(\`_auth:\${key}\`),
|
|
@@ -201,21 +392,47 @@ export function createSecondaryStorage() {
|
|
|
201
392
|
delete: async (key) => kv.del(\`_auth:\${key}\`),
|
|
202
393
|
}
|
|
203
394
|
}` : `export function createSecondaryStorage() { return undefined }`;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
395
|
+
const secondaryStorageTemplate = addTemplate({ filename: "better-auth/secondary-storage.mjs", getContents: () => secondaryStorageCode, write: true });
|
|
396
|
+
nuxt.options.alias["#auth/secondary-storage"] = secondaryStorageTemplate.dst;
|
|
397
|
+
if (hasHubDb && !nuxt.options.alias["hub:db"]) {
|
|
398
|
+
throw new Error("[nuxt-better-auth] hub:db not found. Ensure @nuxthub/core is loaded before this module and hub.db is configured.");
|
|
399
|
+
}
|
|
400
|
+
const hubDialect = getHubDialect(hub) ?? "sqlite";
|
|
401
|
+
const usePlural = options.schema?.usePlural ?? false;
|
|
402
|
+
const camelCase = (options.schema?.casing ?? getHubCasing(hub)) !== "snake_case";
|
|
403
|
+
let databaseCode;
|
|
404
|
+
if (hasHubDb) {
|
|
405
|
+
databaseCode = `import { db, schema } from '@nuxthub/db'
|
|
208
406
|
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
209
|
-
const rawDialect = '${
|
|
407
|
+
const rawDialect = '${hubDialect}'
|
|
210
408
|
const dialect = rawDialect === 'postgresql' ? 'pg' : rawDialect
|
|
211
|
-
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema }) }
|
|
212
|
-
export { db }
|
|
409
|
+
export function createDatabase() { return drizzleAdapter(db, { provider: dialect, schema, usePlural: ${usePlural}, camelCase: ${camelCase} }) }
|
|
410
|
+
export { db }`;
|
|
411
|
+
} else if (hasConvexDb) {
|
|
412
|
+
nuxt.options.runtimeConfig.betterAuth = defu(
|
|
413
|
+
nuxt.options.runtimeConfig.betterAuth || {},
|
|
414
|
+
{ convexUrl }
|
|
415
|
+
);
|
|
416
|
+
databaseCode = `import { useRuntimeConfig } from '#imports'
|
|
417
|
+
import { createConvexHttpAdapter } from '@onmax/nuxt-better-auth/adapters/convex'
|
|
418
|
+
import { api } from '#convex/api'
|
|
419
|
+
|
|
420
|
+
export function createDatabase() {
|
|
421
|
+
const config = useRuntimeConfig()
|
|
422
|
+
const convexUrl = config.betterAuth?.convexUrl || config.public?.convex?.url
|
|
423
|
+
if (!convexUrl) throw new Error('[nuxt-better-auth] CONVEX_URL not configured')
|
|
424
|
+
return createConvexHttpAdapter({ url: convexUrl, api: api.auth })
|
|
425
|
+
}
|
|
213
426
|
export const db = undefined`;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
getContents: () =>
|
|
427
|
+
} else {
|
|
428
|
+
databaseCode = `export function createDatabase() { return undefined }
|
|
429
|
+
export const db = undefined`;
|
|
430
|
+
}
|
|
431
|
+
const databaseTemplate = addTemplate({ filename: "better-auth/database.mjs", getContents: () => databaseCode, write: true });
|
|
432
|
+
nuxt.options.alias["#auth/database"] = databaseTemplate.dst;
|
|
433
|
+
addTypeTemplate({
|
|
434
|
+
filename: "types/auth-secondary-storage.d.ts",
|
|
435
|
+
getContents: () => `
|
|
219
436
|
declare module '#auth/secondary-storage' {
|
|
220
437
|
interface SecondaryStorage {
|
|
221
438
|
get: (key: string) => Promise<string | null>
|
|
@@ -225,59 +442,85 @@ declare module '#auth/secondary-storage' {
|
|
|
225
442
|
export function createSecondaryStorage(): SecondaryStorage | undefined
|
|
226
443
|
}
|
|
227
444
|
`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
445
|
+
}, { nitro: true, node: true });
|
|
446
|
+
addTypeTemplate({
|
|
447
|
+
filename: "types/auth-database.d.ts",
|
|
448
|
+
getContents: () => `
|
|
232
449
|
declare module '#auth/database' {
|
|
233
450
|
import type { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
234
451
|
export function createDatabase(): ReturnType<typeof drizzleAdapter> | undefined
|
|
235
452
|
export const db: unknown
|
|
236
453
|
}
|
|
237
454
|
`
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${resolver.resolve("./runtime/types")}'
|
|
244
|
-
`
|
|
245
|
-
});
|
|
246
|
-
addTypeTemplate({
|
|
247
|
-
filename: "types/nuxt-better-auth-infer.d.ts",
|
|
248
|
-
getContents: () => `
|
|
249
|
-
import type { InferUser, InferSession } from 'better-auth'
|
|
455
|
+
}, { nitro: true, node: true });
|
|
456
|
+
addTypeTemplate({
|
|
457
|
+
filename: "types/nuxt-better-auth-infer.d.ts",
|
|
458
|
+
getContents: () => `
|
|
459
|
+
import type { InferUser, InferSession, InferPluginTypes } from 'better-auth'
|
|
250
460
|
import type { RuntimeConfig } from 'nuxt/schema'
|
|
251
|
-
import type
|
|
461
|
+
import type createServerAuth from '${serverConfigPath}'
|
|
252
462
|
|
|
253
|
-
type _Config = ReturnType<typeof
|
|
463
|
+
type _Config = ReturnType<typeof createServerAuth>
|
|
254
464
|
|
|
255
465
|
declare module '#nuxt-better-auth' {
|
|
256
466
|
interface AuthUser extends InferUser<_Config> {}
|
|
257
|
-
interface AuthSession
|
|
467
|
+
interface AuthSession extends InferSession<_Config> {}
|
|
258
468
|
interface ServerAuthContext {
|
|
259
469
|
runtimeConfig: RuntimeConfig
|
|
260
|
-
${hasHubDb ? `db: typeof import('
|
|
470
|
+
${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : ""}
|
|
261
471
|
}
|
|
472
|
+
type PluginTypes = InferPluginTypes<_Config>
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Augment the config module to use the extended ServerAuthContext
|
|
476
|
+
interface _AugmentedServerAuthContext {
|
|
477
|
+
runtimeConfig: RuntimeConfig
|
|
478
|
+
${hasHubDb ? `db: typeof import('@nuxthub/db')['db']` : "db: unknown"}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
declare module '@onmax/nuxt-better-auth/config' {
|
|
482
|
+
import type { BetterAuthOptions } from 'better-auth'
|
|
483
|
+
type ServerAuthConfig = Omit<BetterAuthOptions, 'database' | 'secret' | 'baseURL'>
|
|
484
|
+
export function defineServerAuth<T extends ServerAuthConfig>(config: T | ((ctx: _AugmentedServerAuthContext) => T)): (ctx: _AugmentedServerAuthContext) => T
|
|
262
485
|
}
|
|
263
486
|
`
|
|
264
|
-
|
|
487
|
+
}, { nuxt: true, nitro: true, node: true });
|
|
488
|
+
addTypeTemplate({
|
|
489
|
+
filename: "types/nuxt-better-auth-nitro.d.ts",
|
|
490
|
+
getContents: () => `
|
|
491
|
+
declare module 'nitropack' {
|
|
492
|
+
interface NitroRouteRules {
|
|
493
|
+
auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
|
|
494
|
+
}
|
|
495
|
+
interface NitroRouteConfig {
|
|
496
|
+
auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
declare module 'nitropack/types' {
|
|
500
|
+
interface NitroRouteRules {
|
|
501
|
+
auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
|
|
502
|
+
}
|
|
503
|
+
interface NitroRouteConfig {
|
|
504
|
+
auth?: import('${resolver.resolve("./runtime/types")}').AuthMeta
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
export {}
|
|
508
|
+
`
|
|
509
|
+
}, { nuxt: true, nitro: true, node: true });
|
|
510
|
+
}
|
|
265
511
|
addTypeTemplate({
|
|
266
|
-
filename: "types/nuxt-better-auth
|
|
512
|
+
filename: "types/nuxt-better-auth.d.ts",
|
|
267
513
|
getContents: () => `
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
export type AppAuthClient = ReturnType<typeof createAppAuthClient>
|
|
271
|
-
}
|
|
514
|
+
export * from '${resolver.resolve("./runtime/types/augment")}'
|
|
515
|
+
export type { AuthMeta, AuthMode, AuthRouteRules, UserMatch, RequireSessionOptions, Auth, InferUser, InferSession } from '${resolver.resolve("./runtime/types")}'
|
|
272
516
|
`
|
|
273
517
|
});
|
|
274
518
|
addTypeTemplate({
|
|
275
|
-
filename: "types/nuxt-better-auth-
|
|
519
|
+
filename: "types/nuxt-better-auth-client.d.ts",
|
|
276
520
|
getContents: () => `
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
521
|
+
import type createAppAuthClient from '${clientConfigPath}'
|
|
522
|
+
declare module '#nuxt-better-auth' {
|
|
523
|
+
export type AppAuthClient = ReturnType<typeof createAppAuthClient>
|
|
281
524
|
}
|
|
282
525
|
`
|
|
283
526
|
});
|
|
@@ -286,21 +529,31 @@ declare module 'nitropack/types' {
|
|
|
286
529
|
await updateTemplates({ filter: (t) => t.filename.includes("nuxt-better-auth") });
|
|
287
530
|
}
|
|
288
531
|
});
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
532
|
+
if (!clientOnly) {
|
|
533
|
+
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
534
|
+
addServerImports([{ name: "defineServerAuth", from: resolver.resolve("./runtime/config") }]);
|
|
535
|
+
addServerScanDir(resolver.resolve("./runtime/server/middleware"));
|
|
536
|
+
addServerHandler({ route: "/api/auth/**", handler: resolver.resolve("./runtime/server/api/auth/[...all]") });
|
|
537
|
+
}
|
|
292
538
|
addImportsDir(resolver.resolve("./runtime/app/composables"));
|
|
293
539
|
addImportsDir(resolver.resolve("./runtime/utils"));
|
|
294
|
-
|
|
540
|
+
if (!clientOnly)
|
|
541
|
+
addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.server"), mode: "server" });
|
|
295
542
|
addPlugin({ src: resolver.resolve("./runtime/app/plugins/session.client"), mode: "client" });
|
|
296
543
|
addComponentsDir({ path: resolver.resolve("./runtime/app/components") });
|
|
297
544
|
nuxt.hook("app:resolve", (app) => {
|
|
298
545
|
app.middleware.push({ name: "auth", path: resolver.resolve("./runtime/app/middleware/auth.global"), global: true });
|
|
299
546
|
});
|
|
300
547
|
if (hasHubDb) {
|
|
301
|
-
await setupBetterAuthSchema(nuxt, serverConfigPath);
|
|
548
|
+
await setupBetterAuthSchema(nuxt, serverConfigPath, options);
|
|
302
549
|
}
|
|
303
|
-
if (
|
|
550
|
+
if (hasConvexDb) {
|
|
551
|
+
await setupConvexAuthSchema(nuxt, serverConfigPath);
|
|
552
|
+
}
|
|
553
|
+
const isProduction = process.env.NODE_ENV === "production" || !nuxt.options.dev;
|
|
554
|
+
if (!isProduction && !clientOnly) {
|
|
555
|
+
if (!hasNuxtModule("@nuxt/ui"))
|
|
556
|
+
await installModule("@nuxt/ui");
|
|
304
557
|
setupDevTools(nuxt);
|
|
305
558
|
addServerHandler({ route: "/api/_better-auth/config", method: "get", handler: resolver.resolve("./runtime/server/api/_better-auth/config.get") });
|
|
306
559
|
if (hasHubDb) {
|
|
@@ -333,37 +586,70 @@ declare module 'nitropack/types' {
|
|
|
333
586
|
});
|
|
334
587
|
}
|
|
335
588
|
});
|
|
336
|
-
async function setupBetterAuthSchema(nuxt, serverConfigPath) {
|
|
589
|
+
async function setupBetterAuthSchema(nuxt, serverConfigPath, options) {
|
|
337
590
|
const hub = nuxt.options.hub;
|
|
338
|
-
const dialect =
|
|
591
|
+
const dialect = getHubDialect(hub);
|
|
339
592
|
if (!dialect || !["sqlite", "postgresql", "mysql"].includes(dialect)) {
|
|
340
593
|
consola.warn(`Unsupported database dialect: ${dialect}`);
|
|
341
594
|
return;
|
|
342
595
|
}
|
|
596
|
+
const isProduction = !nuxt.options.dev;
|
|
343
597
|
try {
|
|
344
598
|
const configFile = `${serverConfigPath}.ts`;
|
|
345
|
-
const userConfig = await loadUserAuthConfig(configFile);
|
|
599
|
+
const userConfig = await loadUserAuthConfig(configFile, isProduction);
|
|
346
600
|
const extendedConfig = {};
|
|
347
601
|
await nuxt.callHook("better-auth:config:extend", extendedConfig);
|
|
348
602
|
const plugins = [...userConfig.plugins || [], ...extendedConfig.plugins || []];
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
603
|
+
const authOptions = {
|
|
604
|
+
...userConfig,
|
|
605
|
+
plugins,
|
|
606
|
+
secondaryStorage: options.secondaryStorage ? { get: async (_key) => null, set: async (_key, _value, _ttl) => {
|
|
607
|
+
}, delete: async (_key) => {
|
|
608
|
+
} } : void 0
|
|
609
|
+
};
|
|
610
|
+
const hubCasing = getHubCasing(hub);
|
|
611
|
+
const schemaOptions = { ...options.schema, useUuid: userConfig.advanced?.database?.generateId === "uuid", casing: options.schema?.casing ?? hubCasing };
|
|
612
|
+
const schemaCode = await generateDrizzleSchema(authOptions, dialect, schemaOptions);
|
|
352
613
|
const schemaDir = join(nuxt.options.buildDir, "better-auth");
|
|
353
614
|
const schemaPath = join(schemaDir, `schema.${dialect}.ts`);
|
|
354
615
|
await mkdir(schemaDir, { recursive: true });
|
|
355
616
|
await writeFile(schemaPath, schemaCode);
|
|
356
617
|
addTemplate({ filename: `better-auth/schema.${dialect}.ts`, getContents: () => schemaCode, write: true });
|
|
357
|
-
consola.info(`Generated ${dialect} schema
|
|
618
|
+
consola.info(`Generated ${dialect} schema`);
|
|
358
619
|
} catch (error) {
|
|
620
|
+
if (isProduction) {
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
359
623
|
consola.error("Failed to generate schema:", error);
|
|
360
624
|
}
|
|
361
|
-
|
|
625
|
+
const nuxtWithHubHooks = nuxt;
|
|
626
|
+
nuxtWithHubHooks.hook("hub:db:schema:extend", ({ paths, dialect: hookDialect }) => {
|
|
362
627
|
const schemaPath = join(nuxt.options.buildDir, "better-auth", `schema.${hookDialect}.ts`);
|
|
363
628
|
if (existsSync(schemaPath)) {
|
|
364
|
-
paths.
|
|
629
|
+
paths.unshift(schemaPath);
|
|
365
630
|
}
|
|
366
631
|
});
|
|
367
632
|
}
|
|
633
|
+
async function setupConvexAuthSchema(nuxt, serverConfigPath) {
|
|
634
|
+
const isProduction = !nuxt.options.dev;
|
|
635
|
+
try {
|
|
636
|
+
const configFile = `${serverConfigPath}.ts`;
|
|
637
|
+
const userConfig = await loadUserAuthConfig(configFile, isProduction);
|
|
638
|
+
const authOptions = { ...userConfig };
|
|
639
|
+
const schemaCode = await generateConvexSchema(authOptions);
|
|
640
|
+
const schemaDir = join(nuxt.options.buildDir, "better-auth");
|
|
641
|
+
const schemaPath = join(schemaDir, "auth-tables.convex.ts");
|
|
642
|
+
await mkdir(schemaDir, { recursive: true });
|
|
643
|
+
await writeFile(schemaPath, schemaCode);
|
|
644
|
+
addTemplate({ filename: "better-auth/auth-tables.convex.ts", getContents: () => schemaCode, write: true });
|
|
645
|
+
nuxt.options.alias["#auth/convex-schema"] = schemaPath;
|
|
646
|
+
consola.info("Generated Convex auth schema at .nuxt/better-auth/auth-tables.convex.ts");
|
|
647
|
+
} catch (error) {
|
|
648
|
+
if (isProduction) {
|
|
649
|
+
throw error;
|
|
650
|
+
}
|
|
651
|
+
consola.error("Failed to generate Convex schema:", error);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
368
654
|
|
|
369
655
|
export { module$1 as default };
|