better-auth 0.2.3-beta.8 → 0.2.4
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/access.js +125 -1
- package/dist/adapters.d.ts +1 -2
- package/dist/adapters.js +1155 -14
- package/dist/api.js +2458 -4
- package/dist/cli.js +1010 -3
- package/dist/client/plugins.js +527 -2
- package/dist/client.js +382 -1
- package/dist/index.js +3670 -5
- package/dist/next-js.js +34 -1
- package/dist/node.js +8 -1
- package/dist/plugins.js +5529 -4
- package/dist/react.js +393 -1
- package/dist/social.js +586 -2
- package/dist/solid-start.js +13 -1
- package/dist/solid.js +389 -1
- package/dist/svelte-kit.js +34 -1
- package/dist/svelte.js +380 -1
- package/dist/utils.js +453 -2
- package/dist/vue.js +389 -1
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,1012 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as ye}from"commander";import"dotenv/config";import{Command as le}from"commander";import{loadConfig as I}from"c12";import{createConsola as _}from"consola";var A=_({formatOptions:{date:!1,colors:!0,compact:!0},defaults:{tag:"Better Auth"}}),H=e=>({log:(...t)=>{!e?.disabled&&A.log("",...t)},error:(...t)=>{!e?.disabled&&A.error("",...t)},warn:(...t)=>{!e?.disabled&&A.warn("",...t)},info:(...t)=>{!e?.disabled&&A.info("",...t)},debug:(...t)=>{!e?.disabled&&A.debug("",...t)},box:(...t)=>{!e?.disabled&&A.box("",...t)},success:(...t)=>{!e?.disabled&&A.success("",...t)},break:(...t)=>{!e?.disabled&&console.log(`
|
|
3
|
-
`)}}),c=H();import J from"path";import Y from"@babel/preset-typescript";import Z from"@babel/preset-react";var w=["auth.ts","auth.tsx"];w=[...w,...w.map(e=>`lib/${e}`),...w.map(e=>`utils/${e}`)];w=[...w,...w.map(e=>`src/${e}`)];var E={transformOptions:{babel:{presets:[[Y,{isTSX:!0,allExtensions:!0}],[Z,{runtime:"automatic"}]]}},extensions:[".ts",".tsx",".js",".jsx"]};async function B({cwd:e,configPath:t}){try{let a=null;if(t){let{config:r}=await I({configFile:J.join(e,t),dotenv:!0,jitiOptions:E});!r.auth&&!r.default&&(c.error("[#better-auth]: Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth."),process.exit(1)),a=r.auth?.options||r.default?.options||null}if(!a)for(let r of w)try{let{config:i}=await I({configFile:r,jitiOptions:E});if(Object.keys(i).length>0){a=i.auth?.options||i.default?.options||null,a||(c.error("[#better-auth]: Couldn't read your auth config."),c.break(),c.info("[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth."),process.exit(1));break}}catch(i){c.error(i),process.exit(1)}return a}catch(a){c.error("Error while reading your auth config.",a),process.exit(1)}}import{z as M}from"zod";import{existsSync as de}from"fs";import ce from"path";import{Kysely as ee}from"kysely";import{MysqlDialect as $,PostgresDialect as P,SqliteDialect as L}from"kysely";var g=class extends Error{constructor(t,a,r){super(t),this.name="BetterAuthError",this.message=t,this.cause=a}};var te=async e=>{if(!e.database)return;if("createDriver"in e.database)return e.database;let t;if("provider"in e.database){let a=e.database.provider,r=e.database?.url?.trim();if(a==="postgres"){let o=(await import("pg").catch(s=>{throw new g("Please install `pg` to use postgres database")})).Pool;t=new P({pool:new o({connectionString:r})})}if(a==="mysql")try{let{createPool:i}=await import("mysql2/promise").catch(n=>{throw new g("Please install `mysql2` to use mysql database")}),o=new URL(r),s=i({host:o.hostname,user:o.username,password:o.password,database:o.pathname.split("/")[1],port:Number(o.port)});t=new $({pool:s})}catch(i){if(i instanceof TypeError)throw new g("Invalid database URL")}if(a==="sqlite")try{let i=await import("better-sqlite3").catch(n=>{throw new g("Please install `better-sqlite3` to use sqlite database")}),o=i.default||i;if(!o)throw new g("Failed to import better-sqlite3. Please ensure `better-sqlite3` is properly installed.");let s=new o(r);t=new L({database:s})}catch(i){throw console.error(i),new g("Failed to initialize SQLite. Please ensure `better-sqlite3` is properly installed.")}}return t},T=async e=>{let t=await te(e);return t&&new ee({dialect:t})},F=e=>{if("provider"in e.database)return e.database.provider;if("dialect"in e.database){if(e.database.dialect instanceof P)return"postgres";if(e.database.dialect instanceof $)return"mysql";if(e.database.dialect instanceof L)return"sqlite"}return"sqlite"};import ue from"ora";import N from"chalk";import fe from"prompts";import"kysely";var k=e=>{let t=e.plugins?.reduce((l,d)=>{let f=d.schema;if(!f)return l;for(let[u,m]of Object.entries(f))l[u]={fields:{...l[u]?.fields,...m.fields},tableName:u};return l},{}),a=e.rateLimit?.storage==="database",r={rateLimit:{tableName:e.rateLimit?.tableName||"rateLimit",fields:{key:{type:"string"},count:{type:"number"},lastRequest:{type:"number"}}}},{user:i,session:o,account:s,...n}=t||{};return{user:{tableName:e.user?.modelName||"user",fields:{name:{type:"string",required:!0},email:{type:"string",unique:!0,required:!0},emailVerified:{type:"boolean",defaultValue:()=>!1,required:!0},image:{type:"string",required:!1},createdAt:{type:"date",defaultValue:()=>new Date,required:!0},updatedAt:{type:"date",defaultValue:()=>new Date,required:!0},...i?.fields},order:0},session:{tableName:e.session?.modelName||"session",fields:{expiresAt:{type:"date",required:!0},ipAddress:{type:"string",required:!1},userAgent:{type:"string",required:!1},userId:{type:"string",references:{model:"user",field:"id",onDelete:"cascade"},required:!0},...o?.fields},order:1},account:{tableName:e.account?.modelName||"account",fields:{accountId:{type:"string",required:!0},providerId:{type:"string",required:!0},userId:{type:"string",references:{model:"user",field:"id",onDelete:"cascade"},required:!0},accessToken:{type:"string",required:!1},refreshToken:{type:"string",required:!1},idToken:{type:"string",required:!1},expiresAt:{type:"date",required:!1},password:{type:"string",required:!1},...s?.fields},order:2},...n,...a?r:{}}};function re(e){return e.plugins?.flatMap(a=>Object.keys(a.schema||{}).map(r=>{let o=(a.schema||{})[r];if(!o?.disableMigration)return{tableName:r,fields:o?.fields}}).filter(r=>r!==void 0))||[]}function v(e){let t=k(e),a=re(e);return[t.user,t.session,t.account,...a].reduce((i,o)=>(i[o.tableName]={fields:{...i[o.tableName]?.fields,...o.fields}},i),{})}var ae={string:["character varying","text"],number:["int4","integer","bigint","smallint","numeric","real","double precision"],boolean:["bool","boolean"],date:["timestamp","date"]},oe={string:["varchar","text"],number:["integer","int","bigint","smallint","decimal","float","double"],boolean:["boolean"],date:["date","datetime"]},ie={string:["TEXT"],number:["INTEGER","REAL"],boolean:["INTEGER","BOOLEAN"],date:["DATE","INTEGER"]},se={postgres:ae,mysql:oe,sqlite:ie};function ne(e,t,a){return se[a][t].map(s=>s.toLowerCase()).includes(e.toLowerCase())}async function D(e){let t=v(e),a=F(e),r=await T(e);r||(c.error("Invalid database configuration."),process.exit(1));let i=await r.introspection.getTables(),o=[],s=[];for(let[u,m]of Object.entries(t)){let h=i.find(y=>y.name===u);if(!h){let y=o.findIndex(O=>O.table===u),p={table:u,fields:m.fields,order:m.order||1/0},q=o.findIndex(O=>(O.order||1/0)>p.order);q===-1?y===-1?o.push(p):o[y].fields={...o[y].fields,...m.fields}:o.splice(q,0,p);continue}let b={};for(let[y,p]of Object.entries(m.fields)){let q=h.columns.find(O=>O.name===y);if(!q){b[y]=p;continue}ne(q.dataType,p.type,a)||c.warn(`Field ${y} in table ${u} has a different type in the database. Expected ${p.type} but got ${q.dataType}.`)}Object.keys(b).length>0&&s.push({table:u,fields:b,order:m.order||1/0})}let n=[];function l(u){let m={string:"text",boolean:"boolean",number:"integer",date:"date"};return a==="mysql"&&u==="string"?"varchar(255)":m[u]}if(s.length)for(let u of s)for(let[m,h]of Object.entries(u.fields)){let b=l(h.type),y=r.schema.alterTable(u.table).addColumn(m,b,p=>(p=h.required!==!1?p.notNull():p,h.references&&(p=p.references(`${h.references.model}.${h.references.field}`)),p));n.push(y)}if(o.length)for(let u of o){let m=r.schema.createTable(u.table).addColumn("id",l("string"),h=>h.primaryKey());for(let[h,b]of Object.entries(u.fields)){let y=l(b.type);m=m.addColumn(h,y,p=>(p=b.required!==!1?p.notNull():p,b.references&&(p=p.references(`${b.references.model}.${b.references.field}`)),b.unique&&(p=p.unique()),p))}n.push(m)}async function d(){for(let u of n)await u.execute()}async function f(){return n.map(m=>m.compile().sql).join(`;
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { Command as Command3 } from "commander";
|
|
5
|
+
import "dotenv/config";
|
|
6
|
+
|
|
7
|
+
// src/cli/commands/migrate.ts
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/cli/get-config.ts
|
|
11
|
+
import { loadConfig } from "c12";
|
|
12
|
+
|
|
13
|
+
// src/utils/logger.ts
|
|
14
|
+
import { createConsola } from "consola";
|
|
15
|
+
var consola = createConsola({
|
|
16
|
+
formatOptions: {
|
|
17
|
+
date: false,
|
|
18
|
+
colors: true,
|
|
19
|
+
compact: true
|
|
20
|
+
},
|
|
21
|
+
defaults: {
|
|
22
|
+
tag: "Better Auth"
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
var createLogger = (options) => {
|
|
26
|
+
return {
|
|
27
|
+
log: (...args) => {
|
|
28
|
+
!options?.disabled && consola.log("", ...args);
|
|
29
|
+
},
|
|
30
|
+
error: (...args) => {
|
|
31
|
+
!options?.disabled && consola.error("", ...args);
|
|
32
|
+
},
|
|
33
|
+
warn: (...args) => {
|
|
34
|
+
!options?.disabled && consola.warn("", ...args);
|
|
35
|
+
},
|
|
36
|
+
info: (...args) => {
|
|
37
|
+
!options?.disabled && consola.info("", ...args);
|
|
38
|
+
},
|
|
39
|
+
debug: (...args) => {
|
|
40
|
+
!options?.disabled && consola.debug("", ...args);
|
|
41
|
+
},
|
|
42
|
+
box: (...args) => {
|
|
43
|
+
!options?.disabled && consola.box("", ...args);
|
|
44
|
+
},
|
|
45
|
+
success: (...args) => {
|
|
46
|
+
!options?.disabled && consola.success("", ...args);
|
|
47
|
+
},
|
|
48
|
+
break: (...args) => {
|
|
49
|
+
!options?.disabled && console.log("\n");
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
var logger = createLogger();
|
|
54
|
+
|
|
55
|
+
// src/cli/get-config.ts
|
|
56
|
+
import path from "path";
|
|
57
|
+
import babelPresetTypescript from "@babel/preset-typescript";
|
|
58
|
+
import babelPresetReact from "@babel/preset-react";
|
|
59
|
+
var possiblePaths = ["auth.ts", "auth.tsx"];
|
|
60
|
+
possiblePaths = [
|
|
61
|
+
...possiblePaths,
|
|
62
|
+
...possiblePaths.map((it) => `lib/${it}`),
|
|
63
|
+
...possiblePaths.map((it) => `utils/${it}`)
|
|
64
|
+
];
|
|
65
|
+
possiblePaths = [...possiblePaths, ...possiblePaths.map((it) => `src/${it}`)];
|
|
66
|
+
var jitiOptions = {
|
|
67
|
+
transformOptions: {
|
|
68
|
+
babel: {
|
|
69
|
+
presets: [
|
|
70
|
+
[babelPresetTypescript, { isTSX: true, allExtensions: true }],
|
|
71
|
+
[babelPresetReact, { runtime: "automatic" }]
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
extensions: [".ts", ".tsx", ".js", ".jsx"]
|
|
76
|
+
};
|
|
77
|
+
async function getConfig({
|
|
78
|
+
cwd,
|
|
79
|
+
configPath
|
|
80
|
+
}) {
|
|
81
|
+
try {
|
|
82
|
+
let configFile = null;
|
|
83
|
+
if (configPath) {
|
|
84
|
+
const { config } = await loadConfig({
|
|
85
|
+
configFile: path.join(cwd, configPath),
|
|
86
|
+
dotenv: true,
|
|
87
|
+
jitiOptions
|
|
88
|
+
});
|
|
89
|
+
if (!config.auth && !config.default) {
|
|
90
|
+
logger.error(
|
|
91
|
+
"[#better-auth]: Couldn't read your auth config. Make sure to default export your auth instance or to export as a variable named auth."
|
|
92
|
+
);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
configFile = config.auth?.options || config.default?.options || null;
|
|
96
|
+
}
|
|
97
|
+
if (!configFile) {
|
|
98
|
+
for (const possiblePath of possiblePaths) {
|
|
99
|
+
try {
|
|
100
|
+
const { config } = await loadConfig({
|
|
101
|
+
configFile: possiblePath,
|
|
102
|
+
jitiOptions
|
|
103
|
+
});
|
|
104
|
+
const hasConfig = Object.keys(config).length > 0;
|
|
105
|
+
if (hasConfig) {
|
|
106
|
+
configFile = config.auth?.options || config.default?.options || null;
|
|
107
|
+
if (!configFile) {
|
|
108
|
+
logger.error("[#better-auth]: Couldn't read your auth config.");
|
|
109
|
+
logger.break();
|
|
110
|
+
logger.info(
|
|
111
|
+
"[#better-auth]: Make sure to default export your auth instance or to export as a variable named auth."
|
|
112
|
+
);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
} catch (e) {
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return configFile;
|
|
123
|
+
} catch (e) {
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/cli/commands/migrate.ts
|
|
129
|
+
import { z } from "zod";
|
|
130
|
+
import { existsSync } from "fs";
|
|
131
|
+
import path2 from "path";
|
|
132
|
+
|
|
133
|
+
// src/adapters/kysely-adapter/dialect.ts
|
|
134
|
+
import { Kysely } from "kysely";
|
|
135
|
+
import {
|
|
136
|
+
MysqlDialect,
|
|
137
|
+
PostgresDialect,
|
|
138
|
+
SqliteDialect
|
|
139
|
+
} from "kysely";
|
|
140
|
+
|
|
141
|
+
// src/error/better-auth-error.ts
|
|
142
|
+
var BetterAuthError = class extends Error {
|
|
143
|
+
constructor(message, cause, docsLink) {
|
|
144
|
+
super(message);
|
|
145
|
+
this.name = "BetterAuthError";
|
|
146
|
+
this.message = message;
|
|
147
|
+
this.cause = cause;
|
|
148
|
+
this.stack = "";
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var MissingDependencyError = class extends BetterAuthError {
|
|
152
|
+
constructor(pkgName) {
|
|
153
|
+
super(
|
|
154
|
+
`The package "${pkgName}" is required. Make sure it is installed.`,
|
|
155
|
+
pkgName
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/adapters/kysely-adapter/dialect.ts
|
|
161
|
+
import "execa";
|
|
162
|
+
import "prompts";
|
|
163
|
+
|
|
164
|
+
// src/cli/utils/get-package-manager.ts
|
|
165
|
+
import { detect } from "@antfu/ni";
|
|
166
|
+
|
|
167
|
+
// src/adapters/kysely-adapter/dialect.ts
|
|
168
|
+
import "ora";
|
|
169
|
+
var getDialect = async (config, isCli) => {
|
|
170
|
+
if (!config.database) {
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
if ("createDriver" in config.database) {
|
|
174
|
+
return config.database;
|
|
175
|
+
}
|
|
176
|
+
let dialect = void 0;
|
|
177
|
+
if ("provider" in config.database) {
|
|
178
|
+
const provider = config.database.provider;
|
|
179
|
+
const connectionString = config.database?.url?.trim();
|
|
180
|
+
if (provider === "postgres") {
|
|
181
|
+
const pg = await import("pg").catch(async (e) => {
|
|
182
|
+
throw new MissingDependencyError("pg");
|
|
183
|
+
});
|
|
184
|
+
const Pool = pg.default?.Pool || pg.Pool;
|
|
185
|
+
const pool = new Pool({
|
|
186
|
+
connectionString
|
|
187
|
+
});
|
|
188
|
+
dialect = new PostgresDialect({
|
|
189
|
+
pool
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (provider === "mysql") {
|
|
193
|
+
try {
|
|
194
|
+
const { createPool } = await import("mysql2/promise").catch(
|
|
195
|
+
async (e) => {
|
|
196
|
+
throw new MissingDependencyError("mysql2");
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
const params = new URL(connectionString);
|
|
200
|
+
const pool = createPool({
|
|
201
|
+
host: params.hostname,
|
|
202
|
+
user: params.username,
|
|
203
|
+
password: params.password,
|
|
204
|
+
database: params.pathname.split("/")[1],
|
|
205
|
+
port: Number(params.port)
|
|
206
|
+
});
|
|
207
|
+
dialect = new MysqlDialect({ pool });
|
|
208
|
+
} catch (e) {
|
|
209
|
+
if (e instanceof TypeError) {
|
|
210
|
+
throw new BetterAuthError("Invalid database URL");
|
|
211
|
+
}
|
|
212
|
+
throw e;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (provider === "sqlite") {
|
|
216
|
+
try {
|
|
217
|
+
const database = await import("better-sqlite3").catch(async (e) => {
|
|
218
|
+
throw new MissingDependencyError("better-sqlite3");
|
|
219
|
+
});
|
|
220
|
+
const Database = database.default || database;
|
|
221
|
+
const db = new Database(connectionString);
|
|
222
|
+
dialect = new SqliteDialect({
|
|
223
|
+
database: db
|
|
224
|
+
});
|
|
225
|
+
} catch (e) {
|
|
226
|
+
console.error(e);
|
|
227
|
+
throw new BetterAuthError(
|
|
228
|
+
"Failed to initialize SQLite. Make sure `better-sqlite3` is properly installed."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return dialect;
|
|
234
|
+
};
|
|
235
|
+
var createKyselyAdapter = async (config, isCli) => {
|
|
236
|
+
const dialect = await getDialect(config, isCli);
|
|
237
|
+
if (!dialect) {
|
|
238
|
+
return dialect;
|
|
239
|
+
}
|
|
240
|
+
const db = new Kysely({
|
|
241
|
+
dialect
|
|
242
|
+
});
|
|
243
|
+
return db;
|
|
244
|
+
};
|
|
245
|
+
var getDatabaseType = (config) => {
|
|
246
|
+
if ("provider" in config.database) {
|
|
247
|
+
return config.database.provider;
|
|
248
|
+
}
|
|
249
|
+
if ("dialect" in config.database) {
|
|
250
|
+
if (config.database.dialect instanceof PostgresDialect) {
|
|
251
|
+
return "postgres";
|
|
252
|
+
}
|
|
253
|
+
if (config.database.dialect instanceof MysqlDialect) {
|
|
254
|
+
return "mysql";
|
|
255
|
+
}
|
|
256
|
+
if (config.database.dialect instanceof SqliteDialect) {
|
|
257
|
+
return "sqlite";
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return "sqlite";
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/cli/commands/migrate.ts
|
|
264
|
+
import ora3 from "ora";
|
|
265
|
+
import chalk from "chalk";
|
|
266
|
+
import prompts3 from "prompts";
|
|
267
|
+
|
|
268
|
+
// src/cli/utils/get-migration.ts
|
|
269
|
+
import "kysely";
|
|
270
|
+
|
|
271
|
+
// src/db/get-tables.ts
|
|
272
|
+
var getAuthTables = (options) => {
|
|
273
|
+
const pluginSchema = options.plugins?.reduce(
|
|
274
|
+
(acc, plugin) => {
|
|
275
|
+
const schema = plugin.schema;
|
|
276
|
+
if (!schema) return acc;
|
|
277
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
278
|
+
acc[key] = {
|
|
279
|
+
fields: {
|
|
280
|
+
...acc[key]?.fields,
|
|
281
|
+
...value.fields
|
|
282
|
+
},
|
|
283
|
+
tableName: key
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return acc;
|
|
287
|
+
},
|
|
288
|
+
{}
|
|
289
|
+
);
|
|
290
|
+
const shouldAddRateLimitTable = options.rateLimit?.storage === "database";
|
|
291
|
+
const rateLimitTable = {
|
|
292
|
+
rateLimit: {
|
|
293
|
+
tableName: options.rateLimit?.tableName || "rateLimit",
|
|
294
|
+
fields: {
|
|
295
|
+
key: {
|
|
296
|
+
type: "string"
|
|
297
|
+
},
|
|
298
|
+
count: {
|
|
299
|
+
type: "number"
|
|
300
|
+
},
|
|
301
|
+
lastRequest: {
|
|
302
|
+
type: "number"
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const { user, session, account, ...pluginTables } = pluginSchema || {};
|
|
308
|
+
return {
|
|
309
|
+
user: {
|
|
310
|
+
tableName: options.user?.modelName || "user",
|
|
311
|
+
fields: {
|
|
312
|
+
name: {
|
|
313
|
+
type: "string",
|
|
314
|
+
required: true
|
|
315
|
+
},
|
|
316
|
+
email: {
|
|
317
|
+
type: "string",
|
|
318
|
+
unique: true,
|
|
319
|
+
required: true
|
|
320
|
+
},
|
|
321
|
+
emailVerified: {
|
|
322
|
+
type: "boolean",
|
|
323
|
+
defaultValue: () => false,
|
|
324
|
+
required: true
|
|
325
|
+
},
|
|
326
|
+
image: {
|
|
327
|
+
type: "string",
|
|
328
|
+
required: false
|
|
329
|
+
},
|
|
330
|
+
createdAt: {
|
|
331
|
+
type: "date",
|
|
332
|
+
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
333
|
+
required: true
|
|
334
|
+
},
|
|
335
|
+
updatedAt: {
|
|
336
|
+
type: "date",
|
|
337
|
+
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
338
|
+
required: true
|
|
339
|
+
},
|
|
340
|
+
...user?.fields
|
|
341
|
+
},
|
|
342
|
+
order: 0
|
|
343
|
+
},
|
|
344
|
+
session: {
|
|
345
|
+
tableName: options.session?.modelName || "session",
|
|
346
|
+
fields: {
|
|
347
|
+
expiresAt: {
|
|
348
|
+
type: "date",
|
|
349
|
+
required: true
|
|
350
|
+
},
|
|
351
|
+
ipAddress: {
|
|
352
|
+
type: "string",
|
|
353
|
+
required: false
|
|
354
|
+
},
|
|
355
|
+
userAgent: {
|
|
356
|
+
type: "string",
|
|
357
|
+
required: false
|
|
358
|
+
},
|
|
359
|
+
userId: {
|
|
360
|
+
type: "string",
|
|
361
|
+
references: {
|
|
362
|
+
model: "user",
|
|
363
|
+
field: "id",
|
|
364
|
+
onDelete: "cascade"
|
|
365
|
+
},
|
|
366
|
+
required: true
|
|
367
|
+
},
|
|
368
|
+
...session?.fields
|
|
369
|
+
},
|
|
370
|
+
order: 1
|
|
371
|
+
},
|
|
372
|
+
account: {
|
|
373
|
+
tableName: options.account?.modelName || "account",
|
|
374
|
+
fields: {
|
|
375
|
+
accountId: {
|
|
376
|
+
type: "string",
|
|
377
|
+
required: true
|
|
378
|
+
},
|
|
379
|
+
providerId: {
|
|
380
|
+
type: "string",
|
|
381
|
+
required: true
|
|
382
|
+
},
|
|
383
|
+
userId: {
|
|
384
|
+
type: "string",
|
|
385
|
+
references: {
|
|
386
|
+
model: "user",
|
|
387
|
+
field: "id",
|
|
388
|
+
onDelete: "cascade"
|
|
389
|
+
},
|
|
390
|
+
required: true
|
|
391
|
+
},
|
|
392
|
+
accessToken: {
|
|
393
|
+
type: "string",
|
|
394
|
+
required: false
|
|
395
|
+
},
|
|
396
|
+
refreshToken: {
|
|
397
|
+
type: "string",
|
|
398
|
+
required: false
|
|
399
|
+
},
|
|
400
|
+
idToken: {
|
|
401
|
+
type: "string",
|
|
402
|
+
required: false
|
|
403
|
+
},
|
|
404
|
+
expiresAt: {
|
|
405
|
+
type: "date",
|
|
406
|
+
required: false
|
|
407
|
+
},
|
|
408
|
+
password: {
|
|
409
|
+
type: "string",
|
|
410
|
+
required: false
|
|
411
|
+
},
|
|
412
|
+
...account?.fields
|
|
413
|
+
},
|
|
414
|
+
order: 2
|
|
415
|
+
},
|
|
416
|
+
...pluginTables,
|
|
417
|
+
...shouldAddRateLimitTable ? rateLimitTable : {}
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
// src/cli/utils/get-schema.ts
|
|
422
|
+
function getPluginTable(config) {
|
|
423
|
+
const pluginsMigrations = config.plugins?.flatMap(
|
|
424
|
+
(plugin) => Object.keys(plugin.schema || {}).map((key) => {
|
|
425
|
+
const schema = plugin.schema || {};
|
|
426
|
+
const table = schema[key];
|
|
427
|
+
if (table?.disableMigration) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
tableName: key,
|
|
432
|
+
fields: table?.fields
|
|
433
|
+
};
|
|
434
|
+
}).filter((value) => value !== void 0)
|
|
435
|
+
) || [];
|
|
436
|
+
return pluginsMigrations;
|
|
437
|
+
}
|
|
438
|
+
function getSchema(config) {
|
|
439
|
+
const baseSchema = getAuthTables(config);
|
|
440
|
+
const pluginSchema = getPluginTable(config);
|
|
441
|
+
const schema = [
|
|
442
|
+
baseSchema.user,
|
|
443
|
+
baseSchema.session,
|
|
444
|
+
baseSchema.account,
|
|
445
|
+
...pluginSchema
|
|
446
|
+
].reduce((acc, curr) => {
|
|
447
|
+
acc[curr.tableName] = {
|
|
448
|
+
fields: {
|
|
449
|
+
...acc[curr.tableName]?.fields,
|
|
450
|
+
...curr.fields
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
return acc;
|
|
454
|
+
}, {});
|
|
455
|
+
return schema;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/cli/utils/get-migration.ts
|
|
459
|
+
var postgresMap = {
|
|
460
|
+
string: ["character varying", "text"],
|
|
461
|
+
number: [
|
|
462
|
+
"int4",
|
|
463
|
+
"integer",
|
|
464
|
+
"bigint",
|
|
465
|
+
"smallint",
|
|
466
|
+
"numeric",
|
|
467
|
+
"real",
|
|
468
|
+
"double precision"
|
|
469
|
+
],
|
|
470
|
+
boolean: ["bool", "boolean"],
|
|
471
|
+
date: ["timestamp", "date"]
|
|
472
|
+
};
|
|
473
|
+
var mysqlMap = {
|
|
474
|
+
string: ["varchar", "text"],
|
|
475
|
+
number: [
|
|
476
|
+
"integer",
|
|
477
|
+
"int",
|
|
478
|
+
"bigint",
|
|
479
|
+
"smallint",
|
|
480
|
+
"decimal",
|
|
481
|
+
"float",
|
|
482
|
+
"double"
|
|
483
|
+
],
|
|
484
|
+
boolean: ["boolean"],
|
|
485
|
+
date: ["date", "datetime"]
|
|
486
|
+
};
|
|
487
|
+
var sqliteMap = {
|
|
488
|
+
string: ["TEXT"],
|
|
489
|
+
number: ["INTEGER", "REAL"],
|
|
490
|
+
boolean: ["INTEGER", "BOOLEAN"],
|
|
491
|
+
// 0 or 1
|
|
492
|
+
date: ["DATE", "INTEGER"]
|
|
493
|
+
};
|
|
494
|
+
var map = {
|
|
495
|
+
postgres: postgresMap,
|
|
496
|
+
mysql: mysqlMap,
|
|
497
|
+
sqlite: sqliteMap
|
|
498
|
+
};
|
|
499
|
+
function matchType(columnDataType, fieldType, dbType) {
|
|
500
|
+
const types = map[dbType];
|
|
501
|
+
const type = types[fieldType].map((t) => t.toLowerCase());
|
|
502
|
+
const matches = type.includes(columnDataType.toLowerCase());
|
|
503
|
+
return matches;
|
|
504
|
+
}
|
|
505
|
+
async function getMigrations(config) {
|
|
506
|
+
const betterAuthSchema = getSchema(config);
|
|
507
|
+
const dbType = getDatabaseType(config);
|
|
508
|
+
const db = await createKyselyAdapter(config);
|
|
509
|
+
if (!db) {
|
|
510
|
+
logger.error("Invalid database configuration.");
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
const tableMetadata = await db.introspection.getTables();
|
|
514
|
+
const toBeCreated = [];
|
|
515
|
+
const toBeAdded = [];
|
|
516
|
+
for (const [key, value] of Object.entries(betterAuthSchema)) {
|
|
517
|
+
const table = tableMetadata.find((t) => t.name === key);
|
|
518
|
+
if (!table) {
|
|
519
|
+
const tIndex = toBeCreated.findIndex((t) => t.table === key);
|
|
520
|
+
const tableData = {
|
|
521
|
+
table: key,
|
|
522
|
+
fields: value.fields,
|
|
523
|
+
order: value.order || Infinity
|
|
524
|
+
};
|
|
525
|
+
const insertIndex = toBeCreated.findIndex(
|
|
526
|
+
(t) => (t.order || Infinity) > tableData.order
|
|
527
|
+
);
|
|
528
|
+
if (insertIndex === -1) {
|
|
529
|
+
if (tIndex === -1) {
|
|
530
|
+
toBeCreated.push(tableData);
|
|
531
|
+
} else {
|
|
532
|
+
toBeCreated[tIndex].fields = {
|
|
533
|
+
...toBeCreated[tIndex].fields,
|
|
534
|
+
...value.fields
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
toBeCreated.splice(insertIndex, 0, tableData);
|
|
539
|
+
}
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
let toBeAddedFields = {};
|
|
543
|
+
for (const [fieldName, field] of Object.entries(value.fields)) {
|
|
544
|
+
const column = table.columns.find((c) => c.name === fieldName);
|
|
545
|
+
if (!column) {
|
|
546
|
+
toBeAddedFields[fieldName] = field;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (matchType(column.dataType, field.type, dbType)) {
|
|
550
|
+
continue;
|
|
551
|
+
} else {
|
|
552
|
+
logger.warn(
|
|
553
|
+
`Field ${fieldName} in table ${key} has a different type in the database. Expected ${field.type} but got ${column.dataType}.`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (Object.keys(toBeAddedFields).length > 0) {
|
|
558
|
+
toBeAdded.push({
|
|
559
|
+
table: key,
|
|
560
|
+
fields: toBeAddedFields,
|
|
561
|
+
order: value.order || Infinity
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
const migrations = [];
|
|
566
|
+
function getType(type) {
|
|
567
|
+
const typeMap = {
|
|
568
|
+
string: "text",
|
|
569
|
+
boolean: "boolean",
|
|
570
|
+
number: "integer",
|
|
571
|
+
date: "date"
|
|
572
|
+
};
|
|
573
|
+
if (dbType === "mysql" && type === "string") {
|
|
574
|
+
return "varchar(255)";
|
|
575
|
+
}
|
|
576
|
+
return typeMap[type];
|
|
577
|
+
}
|
|
578
|
+
if (toBeAdded.length) {
|
|
579
|
+
for (const table of toBeAdded) {
|
|
580
|
+
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
581
|
+
const type = getType(field.type);
|
|
582
|
+
const exec = db.schema.alterTable(table.table).addColumn(fieldName, type, (col) => {
|
|
583
|
+
col = field.required !== false ? col.notNull() : col;
|
|
584
|
+
if (field.references) {
|
|
585
|
+
col = col.references(
|
|
586
|
+
`${field.references.model}.${field.references.field}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
return col;
|
|
590
|
+
});
|
|
591
|
+
migrations.push(exec);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
if (toBeCreated.length) {
|
|
596
|
+
for (const table of toBeCreated) {
|
|
597
|
+
let dbT = db.schema.createTable(table.table).addColumn("id", getType("string"), (col) => col.primaryKey());
|
|
598
|
+
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
599
|
+
const type = getType(field.type);
|
|
600
|
+
dbT = dbT.addColumn(fieldName, type, (col) => {
|
|
601
|
+
col = field.required !== false ? col.notNull() : col;
|
|
602
|
+
if (field.references) {
|
|
603
|
+
col = col.references(
|
|
604
|
+
`${field.references.model}.${field.references.field}`
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
if (field.unique) {
|
|
608
|
+
col = col.unique();
|
|
609
|
+
}
|
|
610
|
+
return col;
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
migrations.push(dbT);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
async function runMigrations() {
|
|
617
|
+
for (const migration of migrations) {
|
|
618
|
+
await migration.execute();
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async function compileMigrations() {
|
|
622
|
+
const compiled = migrations.map((m) => m.compile().sql);
|
|
623
|
+
return compiled.join(";\n\n");
|
|
624
|
+
}
|
|
625
|
+
return { toBeCreated, toBeAdded, runMigrations, compileMigrations };
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// src/cli/utils/install-dep.ts
|
|
629
|
+
import ora2 from "ora";
|
|
630
|
+
import prompts2 from "prompts";
|
|
631
|
+
import { execa as execa2 } from "execa";
|
|
632
|
+
|
|
633
|
+
// src/cli/commands/migrate.ts
|
|
634
|
+
var migrate = new Command("migrate").option(
|
|
635
|
+
"-c, --cwd <cwd>",
|
|
636
|
+
"the working directory. defaults to the current directory.",
|
|
637
|
+
process.cwd()
|
|
638
|
+
).option(
|
|
639
|
+
"--config <config>",
|
|
640
|
+
"the path to the configuration file. defaults to the first configuration file found."
|
|
641
|
+
).option("--y", "").action(async (opts) => {
|
|
642
|
+
const options = z.object({
|
|
643
|
+
cwd: z.string(),
|
|
644
|
+
config: z.string().optional()
|
|
645
|
+
}).parse(opts);
|
|
646
|
+
const cwd = path2.resolve(options.cwd);
|
|
647
|
+
if (!existsSync(cwd)) {
|
|
648
|
+
logger.error(`The directory "${cwd}" does not exist.`);
|
|
649
|
+
process.exit(1);
|
|
650
|
+
}
|
|
651
|
+
const config = await getConfig({
|
|
652
|
+
cwd,
|
|
653
|
+
configPath: options.config
|
|
654
|
+
});
|
|
655
|
+
if (!config) {
|
|
656
|
+
logger.error(
|
|
657
|
+
"No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag."
|
|
658
|
+
);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
const db = await createKyselyAdapter(config, true).catch((e) => {
|
|
662
|
+
logger.error(e.message);
|
|
663
|
+
process.exit(1);
|
|
664
|
+
});
|
|
665
|
+
if (!db) {
|
|
666
|
+
logger.error("Invalid database configuration.");
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}
|
|
669
|
+
const spinner = ora3("preparing migration...").start();
|
|
670
|
+
const { toBeAdded, toBeCreated, runMigrations } = await getMigrations(config);
|
|
671
|
+
if (!toBeAdded.length && !toBeCreated.length) {
|
|
672
|
+
spinner.stop();
|
|
673
|
+
logger.success("\u{1F680} No migrations needed.");
|
|
674
|
+
process.exit(0);
|
|
675
|
+
}
|
|
676
|
+
spinner.stop();
|
|
677
|
+
logger.info(`\u{1F511} The migration will affect the following:`);
|
|
678
|
+
for (const table of [...toBeCreated, ...toBeAdded]) {
|
|
679
|
+
logger.info(
|
|
680
|
+
"->",
|
|
681
|
+
chalk.magenta(Object.keys(table.fields).join(", ")),
|
|
682
|
+
chalk.white("fields on"),
|
|
683
|
+
chalk.yellow(`${table.table}`),
|
|
684
|
+
chalk.white("table.")
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
const { migrate: migrate2 } = await prompts3({
|
|
688
|
+
type: "confirm",
|
|
689
|
+
name: "migrate",
|
|
690
|
+
message: "Are you sure you want to run these migrations?",
|
|
691
|
+
initial: false
|
|
692
|
+
});
|
|
693
|
+
if (!migrate2) {
|
|
694
|
+
logger.info("Migration cancelled.");
|
|
695
|
+
process.exit(0);
|
|
696
|
+
}
|
|
697
|
+
spinner?.start("migrating...");
|
|
698
|
+
await runMigrations();
|
|
699
|
+
spinner.stop();
|
|
700
|
+
logger.success("\u{1F680} migration was completed successfully!");
|
|
701
|
+
process.exit(0);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// src/cli/commands/generate.ts
|
|
705
|
+
import { Command as Command2 } from "commander";
|
|
706
|
+
import { z as z3 } from "zod";
|
|
707
|
+
import { existsSync as existsSync2 } from "fs";
|
|
708
|
+
import path3 from "path";
|
|
709
|
+
import ora4 from "ora";
|
|
710
|
+
import prompts4 from "prompts";
|
|
711
|
+
|
|
712
|
+
// src/utils/cookies.ts
|
|
713
|
+
import { TimeSpan } from "oslo";
|
|
714
|
+
|
|
715
|
+
// src/utils/id.ts
|
|
716
|
+
import { alphabet, generateRandomString } from "oslo/crypto";
|
|
717
|
+
|
|
718
|
+
// src/utils/state.ts
|
|
719
|
+
import { generateState as generateStateOAuth } from "oslo/oauth2";
|
|
720
|
+
import { z as z2 } from "zod";
|
|
721
|
+
|
|
722
|
+
// src/adapters/kysely-adapter/index.ts
|
|
723
|
+
function convertWhere(w) {
|
|
724
|
+
if (!w)
|
|
725
|
+
return {
|
|
726
|
+
and: null,
|
|
727
|
+
or: null
|
|
728
|
+
};
|
|
729
|
+
const and = w?.filter((w2) => w2.connector === "AND" || !w2.connector).reduce(
|
|
730
|
+
(acc, w2) => ({
|
|
731
|
+
...acc,
|
|
732
|
+
[w2.field]: w2.value
|
|
733
|
+
}),
|
|
734
|
+
{}
|
|
735
|
+
);
|
|
736
|
+
const or = w?.filter((w2) => w2.connector === "OR").reduce(
|
|
737
|
+
(acc, w2) => ({
|
|
738
|
+
...acc,
|
|
739
|
+
[w2.field]: w2.value
|
|
740
|
+
}),
|
|
741
|
+
{}
|
|
742
|
+
);
|
|
743
|
+
return {
|
|
744
|
+
and: Object.keys(and).length ? and : null,
|
|
745
|
+
or: Object.keys(or).length ? or : null
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function transformTo(val, fields, transform) {
|
|
749
|
+
for (const key in val) {
|
|
750
|
+
if (val[key] === 0 && fields[key]?.type === "boolean" && transform?.boolean) {
|
|
751
|
+
val[key] = false;
|
|
752
|
+
}
|
|
753
|
+
if (val[key] === 1 && fields[key]?.type === "boolean" && transform?.boolean) {
|
|
754
|
+
val[key] = true;
|
|
755
|
+
}
|
|
756
|
+
if (fields[key]?.type === "date") {
|
|
757
|
+
if (!(val[key] instanceof Date)) {
|
|
758
|
+
val[key] = new Date(val[key]);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return val;
|
|
763
|
+
}
|
|
764
|
+
function transformFrom(val, transform) {
|
|
765
|
+
for (const key in val) {
|
|
766
|
+
if (typeof val[key] === "boolean" && transform?.boolean) {
|
|
767
|
+
val[key] = val[key] ? 1 : 0;
|
|
768
|
+
}
|
|
769
|
+
if (val[key] instanceof Date) {
|
|
770
|
+
val[key] = val[key].toISOString();
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return val;
|
|
774
|
+
}
|
|
775
|
+
var kyselyAdapter = (db, config) => {
|
|
776
|
+
return {
|
|
777
|
+
id: "kysely",
|
|
778
|
+
async create(data) {
|
|
779
|
+
let { model, data: val, select } = data;
|
|
780
|
+
if (config?.transform) {
|
|
781
|
+
val = transformFrom(val, config.transform);
|
|
782
|
+
}
|
|
783
|
+
let res = await db.insertInto(model).values(val).returningAll().executeTakeFirst();
|
|
784
|
+
if (config?.transform) {
|
|
785
|
+
const schema = config.transform.schema[model];
|
|
786
|
+
res = schema ? transformTo(val, schema, config.transform) : res;
|
|
787
|
+
}
|
|
788
|
+
if (select?.length) {
|
|
789
|
+
const data2 = res ? select.reduce((acc, cur) => {
|
|
790
|
+
if (res?.[cur]) {
|
|
791
|
+
return {
|
|
792
|
+
...acc,
|
|
793
|
+
[cur]: res[cur]
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
return acc;
|
|
797
|
+
}, {}) : null;
|
|
798
|
+
res = data2;
|
|
799
|
+
}
|
|
800
|
+
return res;
|
|
801
|
+
},
|
|
802
|
+
async findOne(data) {
|
|
803
|
+
const { model, where, select } = data;
|
|
804
|
+
const { and, or } = convertWhere(where);
|
|
805
|
+
let query = db.selectFrom(model).selectAll();
|
|
806
|
+
if (or) {
|
|
807
|
+
query = query.where((eb) => eb.or(or));
|
|
808
|
+
}
|
|
809
|
+
if (and) {
|
|
810
|
+
query = query.where((eb) => eb.and(and));
|
|
811
|
+
}
|
|
812
|
+
let res = await query.executeTakeFirst();
|
|
813
|
+
if (select?.length) {
|
|
814
|
+
const data2 = res ? select.reduce((acc, cur) => {
|
|
815
|
+
if (res?.[cur]) {
|
|
816
|
+
return {
|
|
817
|
+
...acc,
|
|
818
|
+
[cur]: res[cur]
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
return acc;
|
|
822
|
+
}, {}) : null;
|
|
823
|
+
res = data2;
|
|
824
|
+
}
|
|
825
|
+
if (config?.transform) {
|
|
826
|
+
const schema = config.transform.schema[model];
|
|
827
|
+
res = res && schema ? transformTo(res, schema, config.transform) : res;
|
|
828
|
+
return res || null;
|
|
829
|
+
}
|
|
830
|
+
return res || null;
|
|
831
|
+
},
|
|
832
|
+
async findMany(data) {
|
|
833
|
+
const { model, where } = data;
|
|
834
|
+
let query = db.selectFrom(model);
|
|
835
|
+
const { and, or } = convertWhere(where);
|
|
836
|
+
if (and) {
|
|
837
|
+
query = query.where((eb) => eb.and(and));
|
|
838
|
+
}
|
|
839
|
+
if (or) {
|
|
840
|
+
query = query.where((eb) => eb.or(or));
|
|
841
|
+
}
|
|
842
|
+
const res = await query.selectAll().execute();
|
|
843
|
+
if (config?.transform) {
|
|
844
|
+
const schema = config.transform.schema[model];
|
|
845
|
+
return schema ? res.map((v) => transformTo(v, schema, config.transform)) : res;
|
|
846
|
+
}
|
|
847
|
+
return res;
|
|
848
|
+
},
|
|
849
|
+
async update(data) {
|
|
850
|
+
let { model, where, update: val } = data;
|
|
851
|
+
const { and, or } = convertWhere(where);
|
|
852
|
+
if (config?.transform) {
|
|
853
|
+
val = transformFrom(val, config.transform);
|
|
854
|
+
}
|
|
855
|
+
let query = db.updateTable(model).set(val);
|
|
856
|
+
if (and) {
|
|
857
|
+
query = query.where((eb) => eb.and(and));
|
|
858
|
+
}
|
|
859
|
+
if (or) {
|
|
860
|
+
query = query.where((eb) => eb.or(or));
|
|
861
|
+
}
|
|
862
|
+
const res = await query.returningAll().executeTakeFirst() || null;
|
|
863
|
+
if (config?.transform) {
|
|
864
|
+
const schema = config.transform.schema[model];
|
|
865
|
+
return schema ? transformTo(res, schema, config.transform) : res;
|
|
866
|
+
}
|
|
867
|
+
return res;
|
|
868
|
+
},
|
|
869
|
+
async delete(data) {
|
|
870
|
+
const { model, where } = data;
|
|
871
|
+
const { and, or } = convertWhere(where);
|
|
872
|
+
let query = db.deleteFrom(model);
|
|
873
|
+
if (and) {
|
|
874
|
+
query = query.where((eb) => eb.and(and));
|
|
875
|
+
}
|
|
876
|
+
if (or) {
|
|
877
|
+
query = query.where((eb) => eb.or(or));
|
|
878
|
+
}
|
|
879
|
+
await query.execute();
|
|
880
|
+
},
|
|
881
|
+
async createSchema(options) {
|
|
882
|
+
const { compileMigrations } = await getMigrations(options);
|
|
883
|
+
const migrations = await compileMigrations();
|
|
884
|
+
return {
|
|
885
|
+
code: migrations,
|
|
886
|
+
fileName: `./better-auth_migrations/${(/* @__PURE__ */ new Date()).toISOString()}.sql`
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
};
|
|
891
|
+
|
|
892
|
+
// src/db/utils.ts
|
|
893
|
+
async function getAdapter(options, isCli) {
|
|
894
|
+
if (!options.database) {
|
|
895
|
+
throw new BetterAuthError("Database configuration is required");
|
|
896
|
+
}
|
|
897
|
+
if ("create" in options.database) {
|
|
898
|
+
return options.database;
|
|
899
|
+
}
|
|
900
|
+
const db = await createKyselyAdapter(options, isCli);
|
|
901
|
+
if (!db) {
|
|
902
|
+
throw new BetterAuthError("Failed to initialize database adapter");
|
|
903
|
+
}
|
|
904
|
+
const tables = getAuthTables(options);
|
|
905
|
+
let schema = {};
|
|
906
|
+
for (const table of Object.values(tables)) {
|
|
907
|
+
schema[table.tableName] = table.fields;
|
|
908
|
+
}
|
|
909
|
+
return kyselyAdapter(db, {
|
|
910
|
+
transform: {
|
|
911
|
+
schema,
|
|
912
|
+
date: true,
|
|
913
|
+
boolean: getDatabaseType(options) === "sqlite"
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/cli/commands/generate.ts
|
|
919
|
+
import fs from "fs/promises";
|
|
920
|
+
import chalk2 from "chalk";
|
|
921
|
+
var generate = new Command2("generate").option(
|
|
922
|
+
"-c, --cwd <cwd>",
|
|
923
|
+
"the working directory. defaults to the current directory.",
|
|
924
|
+
process.cwd()
|
|
925
|
+
).option(
|
|
926
|
+
"--config <config>",
|
|
927
|
+
"the path to the configuration file. defaults to the first configuration file found."
|
|
928
|
+
).option("--out <output>", "the file to output to the generated schema").option("--y", "").action(async (opts) => {
|
|
929
|
+
const options = z3.object({
|
|
930
|
+
cwd: z3.string(),
|
|
931
|
+
config: z3.string().optional(),
|
|
932
|
+
out: z3.string().optional()
|
|
933
|
+
}).parse(opts);
|
|
934
|
+
const cwd = path3.resolve(options.cwd);
|
|
935
|
+
if (!existsSync2(cwd)) {
|
|
936
|
+
logger.error(`The directory "${cwd}" does not exist.`);
|
|
937
|
+
process.exit(1);
|
|
938
|
+
}
|
|
939
|
+
const config = await getConfig({
|
|
940
|
+
cwd,
|
|
941
|
+
configPath: options.config
|
|
942
|
+
});
|
|
943
|
+
if (!config) {
|
|
944
|
+
logger.error(
|
|
945
|
+
"No configuration file found. Add a `auth.ts` file to your project or pass the path to the configuration file using the `--config` flag."
|
|
946
|
+
);
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const adapter = await getAdapter(config, true).catch((e) => {
|
|
950
|
+
logger.error(e.message);
|
|
951
|
+
process.exit(1);
|
|
952
|
+
});
|
|
953
|
+
if (!adapter.createSchema) {
|
|
954
|
+
logger.error("The adapter does not support schema generation.");
|
|
955
|
+
process.exit(1);
|
|
956
|
+
}
|
|
957
|
+
const spinner = ora4("preparing schema...").start();
|
|
958
|
+
const { code, fileName, append } = await adapter.createSchema(
|
|
959
|
+
config,
|
|
960
|
+
options.out
|
|
961
|
+
);
|
|
962
|
+
spinner.stop();
|
|
963
|
+
if (!code) {
|
|
964
|
+
logger.success("Your schema is already up to date.");
|
|
965
|
+
process.exit(0);
|
|
966
|
+
}
|
|
967
|
+
if (append) {
|
|
968
|
+
const { append: append2 } = await prompts4({
|
|
969
|
+
type: "confirm",
|
|
970
|
+
name: "append",
|
|
971
|
+
message: `The file ${fileName} already exists. Do you want to ${chalk2.yellow(
|
|
972
|
+
"append"
|
|
973
|
+
)} the schema to the file?`
|
|
974
|
+
});
|
|
975
|
+
if (append2) {
|
|
976
|
+
await fs.appendFile(path3.join(cwd, fileName), code);
|
|
977
|
+
logger.success(`\u{1F680} schema was appended successfully!`);
|
|
978
|
+
process.exit(0);
|
|
979
|
+
} else {
|
|
980
|
+
logger.error("Schema generation aborted.");
|
|
981
|
+
process.exit(1);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
const { confirm } = await prompts4({
|
|
985
|
+
type: "confirm",
|
|
986
|
+
name: "confirm",
|
|
987
|
+
message: `Do you want to generate the schema to ${chalk2.yellow(
|
|
988
|
+
fileName
|
|
989
|
+
)}?`
|
|
990
|
+
});
|
|
991
|
+
if (!confirm) {
|
|
992
|
+
logger.error("Schema generation aborted.");
|
|
993
|
+
process.exit(1);
|
|
994
|
+
}
|
|
995
|
+
const dirExist = existsSync2(path3.dirname(path3.join(cwd, fileName)));
|
|
996
|
+
if (!dirExist) {
|
|
997
|
+
await fs.mkdir(path3.dirname(path3.join(cwd, fileName)), {
|
|
998
|
+
recursive: true
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
await fs.writeFile(options.out || path3.join(cwd, fileName), code);
|
|
1002
|
+
logger.success(`\u{1F680} schema was generated successfully!`);
|
|
1003
|
+
process.exit(0);
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// src/cli/index.ts
|
|
1007
|
+
async function main() {
|
|
1008
|
+
const program = new Command3().name("better-auth");
|
|
1009
|
+
program.addCommand(migrate).addCommand(generate).version("0.0.1").description("Better Auth CLI");
|
|
1010
|
+
program.parse();
|
|
1011
|
+
}
|
|
1012
|
+
main();
|