@holeauth/adapter-drizzle 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/mysql/index.cjs +285 -0
- package/dist/mysql/index.cjs.map +1 -0
- package/dist/mysql/index.d.cts +602 -0
- package/dist/mysql/index.d.ts +602 -0
- package/dist/mysql/index.js +282 -0
- package/dist/mysql/index.js.map +1 -0
- package/dist/pg/index.cjs +289 -0
- package/dist/pg/index.cjs.map +1 -0
- package/dist/pg/index.d.cts +609 -0
- package/dist/pg/index.d.ts +609 -0
- package/dist/pg/index.js +286 -0
- package/dist/pg/index.js.map +1 -0
- package/dist/sqlite/index.cjs +271 -0
- package/dist/sqlite/index.cjs.map +1 -0
- package/dist/sqlite/index.d.cts +602 -0
- package/dist/sqlite/index.d.ts +602 -0
- package/dist/sqlite/index.js +268 -0
- package/dist/sqlite/index.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { mysqlTable, varchar, text, timestamp, index, uniqueIndex, primaryKey, json } from 'drizzle-orm/mysql-core';
|
|
2
|
+
import { relations, eq, and, sql } from 'drizzle-orm';
|
|
3
|
+
|
|
4
|
+
// src/mysql/index.ts
|
|
5
|
+
function createHoleauthTables(opts) {
|
|
6
|
+
const { usersTable, prefix = "holeauth_" } = opts;
|
|
7
|
+
const p = (s) => `${prefix}${s}`;
|
|
8
|
+
const sessions = mysqlTable(
|
|
9
|
+
p("session"),
|
|
10
|
+
{
|
|
11
|
+
id: varchar("id", { length: 191 }).primaryKey(),
|
|
12
|
+
userId: varchar("user_id", { length: 191 }).notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
|
13
|
+
familyId: varchar("family_id", { length: 191 }).notNull(),
|
|
14
|
+
refreshTokenHash: varchar("refresh_token_hash", { length: 191 }).notNull(),
|
|
15
|
+
expiresAt: timestamp("expires_at", { fsp: 3 }).notNull(),
|
|
16
|
+
createdAt: timestamp("created_at", { fsp: 3 }).notNull().defaultNow(),
|
|
17
|
+
revokedAt: timestamp("revoked_at", { fsp: 3 }),
|
|
18
|
+
userAgent: text("user_agent"),
|
|
19
|
+
ip: varchar("ip", { length: 64 })
|
|
20
|
+
},
|
|
21
|
+
(t) => ({
|
|
22
|
+
familyIdx: index(`${p("session")}_family_idx`).on(t.familyId),
|
|
23
|
+
hashIdx: uniqueIndex(`${p("session")}_hash_idx`).on(t.refreshTokenHash),
|
|
24
|
+
userIdx: index(`${p("session")}_user_idx`).on(t.userId)
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
const accounts = mysqlTable(
|
|
28
|
+
p("account"),
|
|
29
|
+
{
|
|
30
|
+
id: varchar("id", { length: 191 }).primaryKey(),
|
|
31
|
+
userId: varchar("user_id", { length: 191 }).notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
|
32
|
+
provider: varchar("provider", { length: 191 }).notNull(),
|
|
33
|
+
providerAccountId: varchar("provider_account_id", { length: 191 }).notNull(),
|
|
34
|
+
email: varchar("email", { length: 320 }),
|
|
35
|
+
accessToken: text("access_token"),
|
|
36
|
+
refreshToken: text("refresh_token"),
|
|
37
|
+
expiresAt: timestamp("expires_at", { fsp: 3 }),
|
|
38
|
+
tokenType: varchar("token_type", { length: 64 }),
|
|
39
|
+
scope: text("scope"),
|
|
40
|
+
idToken: text("id_token")
|
|
41
|
+
},
|
|
42
|
+
(t) => ({
|
|
43
|
+
providerIdx: uniqueIndex(`${p("account")}_provider_idx`).on(t.provider, t.providerAccountId),
|
|
44
|
+
userIdx: index(`${p("account")}_user_idx`).on(t.userId)
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
const verificationTokens = mysqlTable(
|
|
48
|
+
p("verification_token"),
|
|
49
|
+
{
|
|
50
|
+
identifier: varchar("identifier", { length: 320 }).notNull(),
|
|
51
|
+
token: varchar("token", { length: 191 }).notNull(),
|
|
52
|
+
expiresAt: timestamp("expires_at", { fsp: 3 }).notNull()
|
|
53
|
+
},
|
|
54
|
+
(t) => ({ pk: primaryKey({ columns: [t.identifier, t.token] }) })
|
|
55
|
+
);
|
|
56
|
+
const auditLog = mysqlTable(
|
|
57
|
+
p("audit_log"),
|
|
58
|
+
{
|
|
59
|
+
id: varchar("id", { length: 191 }).primaryKey(),
|
|
60
|
+
type: varchar("type", { length: 64 }).notNull(),
|
|
61
|
+
userId: varchar("user_id", { length: 191 }).references(() => usersTable.id, { onDelete: "set null" }),
|
|
62
|
+
sessionId: varchar("session_id", { length: 191 }),
|
|
63
|
+
at: timestamp("at", { fsp: 3 }).notNull().defaultNow(),
|
|
64
|
+
ip: varchar("ip", { length: 64 }),
|
|
65
|
+
userAgent: text("user_agent"),
|
|
66
|
+
data: json("data")
|
|
67
|
+
},
|
|
68
|
+
(t) => ({
|
|
69
|
+
typeIdx: index(`${p("audit_log")}_type_idx`).on(t.type),
|
|
70
|
+
userIdx: index(`${p("audit_log")}_user_idx`).on(t.userId)
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
const sessionsRelations = relations(sessions, ({ one }) => ({
|
|
74
|
+
user: one(usersTable, { fields: [sessions.userId], references: [usersTable.id] })
|
|
75
|
+
}));
|
|
76
|
+
const accountsRelations = relations(accounts, ({ one }) => ({
|
|
77
|
+
user: one(usersTable, { fields: [accounts.userId], references: [usersTable.id] })
|
|
78
|
+
}));
|
|
79
|
+
const auditLogRelations = relations(auditLog, ({ one }) => ({
|
|
80
|
+
user: one(usersTable, { fields: [auditLog.userId], references: [usersTable.id] })
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
tables: { users: usersTable, sessions, accounts, verificationTokens, auditLog },
|
|
84
|
+
relations: { sessionsRelations, accountsRelations, auditLogRelations }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function createHoleauthAdapters(opts) {
|
|
88
|
+
const { db, tables, userEmailColumn = "email", generateId = () => crypto.randomUUID() } = opts;
|
|
89
|
+
const { users, sessions, accounts, verificationTokens, auditLog } = tables;
|
|
90
|
+
const emailCol = users[userEmailColumn];
|
|
91
|
+
if (!emailCol) {
|
|
92
|
+
throw new Error(`[holeauth] usersTable missing "${userEmailColumn}" column.`);
|
|
93
|
+
}
|
|
94
|
+
const one = (rows) => rows[0];
|
|
95
|
+
const userRowToAdapter = (r) => ({
|
|
96
|
+
id: String(r.id),
|
|
97
|
+
email: String(r[userEmailColumn] ?? ""),
|
|
98
|
+
emailVerified: r.emailVerified ?? null,
|
|
99
|
+
name: r.name ?? null,
|
|
100
|
+
image: r.image ?? null,
|
|
101
|
+
passwordHash: r.passwordHash ?? null
|
|
102
|
+
});
|
|
103
|
+
const user = {
|
|
104
|
+
async getUserById(id) {
|
|
105
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
106
|
+
const row = one(rows);
|
|
107
|
+
return row ? userRowToAdapter(row) : null;
|
|
108
|
+
},
|
|
109
|
+
async getUserByEmail(email) {
|
|
110
|
+
const rows = await db.select().from(users).where(eq(emailCol, email)).limit(1);
|
|
111
|
+
const row = one(rows);
|
|
112
|
+
return row ? userRowToAdapter(row) : null;
|
|
113
|
+
},
|
|
114
|
+
async createUser(data) {
|
|
115
|
+
const id = generateId();
|
|
116
|
+
await db.insert(users).values({
|
|
117
|
+
id,
|
|
118
|
+
[userEmailColumn]: data.email,
|
|
119
|
+
emailVerified: data.emailVerified ?? null,
|
|
120
|
+
name: data.name ?? null,
|
|
121
|
+
image: data.image ?? null,
|
|
122
|
+
passwordHash: data.passwordHash ?? null
|
|
123
|
+
});
|
|
124
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
125
|
+
return userRowToAdapter(rows[0]);
|
|
126
|
+
},
|
|
127
|
+
async updateUser(id, patch) {
|
|
128
|
+
const toSet = { ...patch };
|
|
129
|
+
if ("email" in toSet) {
|
|
130
|
+
toSet[userEmailColumn] = toSet.email;
|
|
131
|
+
if (userEmailColumn !== "email") delete toSet.email;
|
|
132
|
+
}
|
|
133
|
+
await db.update(users).set(toSet).where(eq(users.id, id));
|
|
134
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
135
|
+
const row = one(rows);
|
|
136
|
+
if (!row) throw new Error(`User ${id} not found`);
|
|
137
|
+
return userRowToAdapter(row);
|
|
138
|
+
},
|
|
139
|
+
async deleteUser(id) {
|
|
140
|
+
await db.delete(users).where(eq(users.id, id));
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const sessionRowToAdapter = (r) => ({
|
|
144
|
+
id: String(r.id),
|
|
145
|
+
userId: String(r.userId),
|
|
146
|
+
familyId: String(r.familyId),
|
|
147
|
+
refreshTokenHash: String(r.refreshTokenHash),
|
|
148
|
+
expiresAt: r.expiresAt,
|
|
149
|
+
createdAt: r.createdAt,
|
|
150
|
+
revokedAt: r.revokedAt ?? null,
|
|
151
|
+
userAgent: r.userAgent ?? null,
|
|
152
|
+
ip: r.ip ?? null
|
|
153
|
+
});
|
|
154
|
+
const session = {
|
|
155
|
+
async createSession(data) {
|
|
156
|
+
await db.insert(sessions).values(data);
|
|
157
|
+
const rows = await db.select().from(sessions).where(eq(sessions.id, data.id)).limit(1);
|
|
158
|
+
return sessionRowToAdapter(rows[0]);
|
|
159
|
+
},
|
|
160
|
+
async getSession(id) {
|
|
161
|
+
const rows = await db.select().from(sessions).where(eq(sessions.id, id)).limit(1);
|
|
162
|
+
return rows[0] ? sessionRowToAdapter(rows[0]) : null;
|
|
163
|
+
},
|
|
164
|
+
async getByRefreshHash(hash) {
|
|
165
|
+
const rows = await db.select().from(sessions).where(eq(sessions.refreshTokenHash, hash)).limit(1);
|
|
166
|
+
return rows[0] ? sessionRowToAdapter(rows[0]) : null;
|
|
167
|
+
},
|
|
168
|
+
async findByFamily(familyId) {
|
|
169
|
+
const rows = await db.select().from(sessions).where(eq(sessions.familyId, familyId));
|
|
170
|
+
return rows.map(sessionRowToAdapter);
|
|
171
|
+
},
|
|
172
|
+
async deleteSession(id) {
|
|
173
|
+
await db.delete(sessions).where(eq(sessions.id, id));
|
|
174
|
+
},
|
|
175
|
+
async rotateRefresh(id, newHash, expiresAt) {
|
|
176
|
+
await db.update(sessions).set({ refreshTokenHash: newHash, expiresAt }).where(eq(sessions.id, id));
|
|
177
|
+
const rows = await db.select().from(sessions).where(eq(sessions.id, id)).limit(1);
|
|
178
|
+
const row = one(rows);
|
|
179
|
+
if (!row) throw new Error(`Session ${id} not found`);
|
|
180
|
+
return sessionRowToAdapter(row);
|
|
181
|
+
},
|
|
182
|
+
async revokeFamily(familyId) {
|
|
183
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(eq(sessions.familyId, familyId), sql`${sessions.revokedAt} IS NULL`));
|
|
184
|
+
},
|
|
185
|
+
async revokeUser(userId) {
|
|
186
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(eq(sessions.userId, userId), sql`${sessions.revokedAt} IS NULL`));
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const accountRowToAdapter = (r) => ({
|
|
190
|
+
id: String(r.id),
|
|
191
|
+
userId: String(r.userId),
|
|
192
|
+
provider: String(r.provider),
|
|
193
|
+
providerAccountId: String(r.providerAccountId),
|
|
194
|
+
email: r.email ?? null,
|
|
195
|
+
accessToken: r.accessToken ?? null,
|
|
196
|
+
refreshToken: r.refreshToken ?? null,
|
|
197
|
+
expiresAt: r.expiresAt ?? null,
|
|
198
|
+
tokenType: r.tokenType ?? null,
|
|
199
|
+
scope: r.scope ?? null,
|
|
200
|
+
idToken: r.idToken ?? null
|
|
201
|
+
});
|
|
202
|
+
const account = {
|
|
203
|
+
async linkAccount(data) {
|
|
204
|
+
const id = generateId();
|
|
205
|
+
await db.insert(accounts).values({ id, ...data });
|
|
206
|
+
const rows = await db.select().from(accounts).where(eq(accounts.id, id)).limit(1);
|
|
207
|
+
return accountRowToAdapter(rows[0]);
|
|
208
|
+
},
|
|
209
|
+
async getAccountByProvider(provider, providerAccountId) {
|
|
210
|
+
const rows = await db.select().from(accounts).where(and(eq(accounts.provider, provider), eq(accounts.providerAccountId, providerAccountId))).limit(1);
|
|
211
|
+
return rows[0] ? accountRowToAdapter(rows[0]) : null;
|
|
212
|
+
},
|
|
213
|
+
async getByProviderEmail(provider, email) {
|
|
214
|
+
const rows = await db.select().from(accounts).where(and(eq(accounts.provider, provider), eq(accounts.email, email))).limit(1);
|
|
215
|
+
return rows[0] ? accountRowToAdapter(rows[0]) : null;
|
|
216
|
+
},
|
|
217
|
+
async listByUser(userId) {
|
|
218
|
+
const rows = await db.select().from(accounts).where(eq(accounts.userId, userId));
|
|
219
|
+
return rows.map(accountRowToAdapter);
|
|
220
|
+
},
|
|
221
|
+
async unlinkAccount(id) {
|
|
222
|
+
await db.delete(accounts).where(eq(accounts.id, id));
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const vtRowToAdapter = (r) => ({
|
|
226
|
+
identifier: String(r.identifier),
|
|
227
|
+
token: String(r.token),
|
|
228
|
+
expiresAt: r.expiresAt
|
|
229
|
+
});
|
|
230
|
+
const verificationToken = {
|
|
231
|
+
async create(data) {
|
|
232
|
+
await db.insert(verificationTokens).values(data);
|
|
233
|
+
return vtRowToAdapter(data);
|
|
234
|
+
},
|
|
235
|
+
async consume(identifier, token) {
|
|
236
|
+
const rows = await db.select().from(verificationTokens).where(and(eq(verificationTokens.identifier, identifier), eq(verificationTokens.token, token))).limit(1);
|
|
237
|
+
const row = rows[0];
|
|
238
|
+
if (!row) return null;
|
|
239
|
+
await db.delete(verificationTokens).where(and(eq(verificationTokens.identifier, identifier), eq(verificationTokens.token, token)));
|
|
240
|
+
const rec = vtRowToAdapter(row);
|
|
241
|
+
return rec.expiresAt.getTime() < Date.now() ? null : rec;
|
|
242
|
+
},
|
|
243
|
+
async purgeExpired() {
|
|
244
|
+
const existing = await db.select({ identifier: verificationTokens.identifier }).from(verificationTokens).where(sql`${verificationTokens.expiresAt} < NOW()`);
|
|
245
|
+
await db.delete(verificationTokens).where(sql`${verificationTokens.expiresAt} < NOW()`);
|
|
246
|
+
return existing.length;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const auditRowToAdapter = (r) => ({
|
|
250
|
+
id: String(r.id),
|
|
251
|
+
type: String(r.type),
|
|
252
|
+
userId: r.userId ?? null,
|
|
253
|
+
sessionId: r.sessionId ?? null,
|
|
254
|
+
at: r.at,
|
|
255
|
+
ip: r.ip ?? null,
|
|
256
|
+
userAgent: r.userAgent ?? null,
|
|
257
|
+
data: r.data ?? null
|
|
258
|
+
});
|
|
259
|
+
const auditLogAdapter = {
|
|
260
|
+
async record(event) {
|
|
261
|
+
await db.insert(auditLog).values({ id: event.id ?? generateId(), ...event });
|
|
262
|
+
},
|
|
263
|
+
async list(filter) {
|
|
264
|
+
const conds = [];
|
|
265
|
+
if (filter.userId) conds.push(eq(auditLog.userId, filter.userId));
|
|
266
|
+
if (filter.type) conds.push(eq(auditLog.type, filter.type));
|
|
267
|
+
const q = db.select().from(auditLog);
|
|
268
|
+
const rows = conds.length ? await q.where(and(...conds)).limit(filter.limit ?? 100) : await q.limit(filter.limit ?? 100);
|
|
269
|
+
return rows.map(auditRowToAdapter);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const transaction = {
|
|
273
|
+
async run(fn) {
|
|
274
|
+
return db.transaction(async () => fn());
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
return { user, session, account, verificationToken, auditLog: auditLogAdapter, transaction };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export { createHoleauthAdapters, createHoleauthTables };
|
|
281
|
+
//# sourceMappingURL=index.js.map
|
|
282
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/mysql/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n mysqlTable,\n varchar,\n text,\n timestamp,\n json,\n index,\n uniqueIndex,\n primaryKey,\n type MySqlTableWithColumns,\n} from 'drizzle-orm/mysql-core';\nimport { relations, eq, and, sql } from 'drizzle-orm';\nimport type {\n AdapterUser,\n AdapterSession,\n AdapterAccount,\n AdapterVerificationToken,\n AdapterAuditEvent,\n UserAdapter,\n SessionAdapter,\n AccountAdapter,\n VerificationTokenAdapter,\n AuditLogAdapter,\n TransactionAdapter,\n} from '@holeauth/core/adapters';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MysqlUsersTable = MySqlTableWithColumns<any> & { id: any };\n\nexport interface CreateHoleauthTablesOptions<U extends MysqlUsersTable> {\n usersTable: U;\n prefix?: string;\n}\n\nexport function createHoleauthTables<U extends MysqlUsersTable>(\n opts: CreateHoleauthTablesOptions<U>,\n) {\n const { usersTable, prefix = 'holeauth_' } = opts;\n const p = (s: string) => `${prefix}${s}`;\n\n const sessions = mysqlTable(\n p('session'),\n {\n id: varchar('id', { length: 191 }).primaryKey(),\n userId: varchar('user_id', { length: 191 })\n .notNull()\n .references(() => usersTable.id, { onDelete: 'cascade' }),\n familyId: varchar('family_id', { length: 191 }).notNull(),\n refreshTokenHash: varchar('refresh_token_hash', { length: 191 }).notNull(),\n expiresAt: timestamp('expires_at', { fsp: 3 }).notNull(),\n createdAt: timestamp('created_at', { fsp: 3 }).notNull().defaultNow(),\n revokedAt: timestamp('revoked_at', { fsp: 3 }),\n userAgent: text('user_agent'),\n ip: varchar('ip', { length: 64 }),\n },\n (t) => ({\n familyIdx: index(`${p('session')}_family_idx`).on(t.familyId),\n hashIdx: uniqueIndex(`${p('session')}_hash_idx`).on(t.refreshTokenHash),\n userIdx: index(`${p('session')}_user_idx`).on(t.userId),\n }),\n );\n\n const accounts = mysqlTable(\n p('account'),\n {\n id: varchar('id', { length: 191 }).primaryKey(),\n userId: varchar('user_id', { length: 191 })\n .notNull()\n .references(() => usersTable.id, { onDelete: 'cascade' }),\n provider: varchar('provider', { length: 191 }).notNull(),\n providerAccountId: varchar('provider_account_id', { length: 191 }).notNull(),\n email: varchar('email', { length: 320 }),\n accessToken: text('access_token'),\n refreshToken: text('refresh_token'),\n expiresAt: timestamp('expires_at', { fsp: 3 }),\n tokenType: varchar('token_type', { length: 64 }),\n scope: text('scope'),\n idToken: text('id_token'),\n },\n (t) => ({\n providerIdx: uniqueIndex(`${p('account')}_provider_idx`).on(t.provider, t.providerAccountId),\n userIdx: index(`${p('account')}_user_idx`).on(t.userId),\n }),\n );\n\n const verificationTokens = mysqlTable(\n p('verification_token'),\n {\n identifier: varchar('identifier', { length: 320 }).notNull(),\n token: varchar('token', { length: 191 }).notNull(),\n expiresAt: timestamp('expires_at', { fsp: 3 }).notNull(),\n },\n (t) => ({ pk: primaryKey({ columns: [t.identifier, t.token] }) }),\n );\n\n const auditLog = mysqlTable(\n p('audit_log'),\n {\n id: varchar('id', { length: 191 }).primaryKey(),\n type: varchar('type', { length: 64 }).notNull(),\n userId: varchar('user_id', { length: 191 }).references(() => usersTable.id, { onDelete: 'set null' }),\n sessionId: varchar('session_id', { length: 191 }),\n at: timestamp('at', { fsp: 3 }).notNull().defaultNow(),\n ip: varchar('ip', { length: 64 }),\n userAgent: text('user_agent'),\n data: json('data'),\n },\n (t) => ({\n typeIdx: index(`${p('audit_log')}_type_idx`).on(t.type),\n userIdx: index(`${p('audit_log')}_user_idx`).on(t.userId),\n }),\n );\n\n const sessionsRelations = relations(sessions, ({ one }) => ({\n user: one(usersTable, { fields: [sessions.userId], references: [usersTable.id] }),\n }));\n const accountsRelations = relations(accounts, ({ one }) => ({\n user: one(usersTable, { fields: [accounts.userId], references: [usersTable.id] }),\n }));\n const auditLogRelations = relations(auditLog, ({ one }) => ({\n user: one(usersTable, { fields: [auditLog.userId], references: [usersTable.id] }),\n }));\n\n return {\n tables: { users: usersTable, sessions, accounts, verificationTokens, auditLog },\n relations: { sessionsRelations, accountsRelations, auditLogRelations },\n };\n}\n\ntype HoleauthTables<U extends MysqlUsersTable> = ReturnType<\n typeof createHoleauthTables<U>\n>['tables'];\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type MysqlDb = any;\n\nexport interface CreateHoleauthAdaptersOptions<U extends MysqlUsersTable> {\n db: MysqlDb;\n tables: HoleauthTables<U>;\n userEmailColumn?: string;\n generateId?: () => string;\n}\n\nexport interface HoleauthAdapterBundle {\n user: UserAdapter;\n session: SessionAdapter;\n account: AccountAdapter;\n verificationToken: VerificationTokenAdapter;\n auditLog: AuditLogAdapter;\n transaction: TransactionAdapter;\n}\n\nexport function createHoleauthAdapters<U extends MysqlUsersTable>(\n opts: CreateHoleauthAdaptersOptions<U>,\n): HoleauthAdapterBundle {\n const { db, tables, userEmailColumn = 'email', generateId = () => crypto.randomUUID() } = opts;\n const { users, sessions, accounts, verificationTokens, auditLog } = tables;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const emailCol = (users as any)[userEmailColumn];\n if (!emailCol) {\n throw new Error(`[holeauth] usersTable missing \"${userEmailColumn}\" column.`);\n }\n\n const one = <T>(rows: T[]): T | undefined => rows[0];\n\n const userRowToAdapter = (r: Record<string, unknown>): AdapterUser => ({\n id: String(r.id),\n email: String(r[userEmailColumn] ?? ''),\n emailVerified: (r.emailVerified as Date | null | undefined) ?? null,\n name: (r.name as string | null | undefined) ?? null,\n image: (r.image as string | null | undefined) ?? null,\n passwordHash: (r.passwordHash as string | null | undefined) ?? null,\n });\n\n const user: UserAdapter = {\n async getUserById(id) {\n const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);\n const row = one(rows);\n return row ? userRowToAdapter(row as Record<string, unknown>) : null;\n },\n async getUserByEmail(email) {\n const rows = await db.select().from(users).where(eq(emailCol, email)).limit(1);\n const row = one(rows);\n return row ? userRowToAdapter(row as Record<string, unknown>) : null;\n },\n async createUser(data) {\n const id = generateId();\n await db.insert(users).values({\n id,\n [userEmailColumn]: data.email,\n emailVerified: data.emailVerified ?? null,\n name: data.name ?? null,\n image: data.image ?? null,\n passwordHash: data.passwordHash ?? null,\n });\n const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);\n return userRowToAdapter(rows[0] as Record<string, unknown>);\n },\n async updateUser(id, patch) {\n const toSet: Record<string, unknown> = { ...patch };\n if ('email' in toSet) {\n toSet[userEmailColumn] = toSet.email;\n if (userEmailColumn !== 'email') delete toSet.email;\n }\n await db.update(users).set(toSet).where(eq(users.id, id));\n const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);\n const row = one(rows);\n if (!row) throw new Error(`User ${id} not found`);\n return userRowToAdapter(row as Record<string, unknown>);\n },\n async deleteUser(id) {\n await db.delete(users).where(eq(users.id, id));\n },\n };\n\n const sessionRowToAdapter = (r: Record<string, unknown>): AdapterSession => ({\n id: String(r.id),\n userId: String(r.userId),\n familyId: String(r.familyId),\n refreshTokenHash: String(r.refreshTokenHash),\n expiresAt: r.expiresAt as Date,\n createdAt: r.createdAt as Date | undefined,\n revokedAt: (r.revokedAt as Date | null | undefined) ?? null,\n userAgent: (r.userAgent as string | null | undefined) ?? null,\n ip: (r.ip as string | null | undefined) ?? null,\n });\n\n const session: SessionAdapter = {\n async createSession(data) {\n await db.insert(sessions).values(data);\n const rows = await db.select().from(sessions).where(eq(sessions.id, data.id)).limit(1);\n return sessionRowToAdapter(rows[0] as Record<string, unknown>);\n },\n async getSession(id) {\n const rows = await db.select().from(sessions).where(eq(sessions.id, id)).limit(1);\n return rows[0] ? sessionRowToAdapter(rows[0] as Record<string, unknown>) : null;\n },\n async getByRefreshHash(hash) {\n const rows = await db.select().from(sessions).where(eq(sessions.refreshTokenHash, hash)).limit(1);\n return rows[0] ? sessionRowToAdapter(rows[0] as Record<string, unknown>) : null;\n },\n async findByFamily(familyId) {\n const rows = await db.select().from(sessions).where(eq(sessions.familyId, familyId));\n return (rows as Record<string, unknown>[]).map(sessionRowToAdapter);\n },\n async deleteSession(id) {\n await db.delete(sessions).where(eq(sessions.id, id));\n },\n async rotateRefresh(id, newHash, expiresAt) {\n await db.update(sessions).set({ refreshTokenHash: newHash, expiresAt }).where(eq(sessions.id, id));\n const rows = await db.select().from(sessions).where(eq(sessions.id, id)).limit(1);\n const row = one(rows);\n if (!row) throw new Error(`Session ${id} not found`);\n return sessionRowToAdapter(row as Record<string, unknown>);\n },\n async revokeFamily(familyId) {\n await db\n .update(sessions)\n .set({ revokedAt: new Date() })\n .where(and(eq(sessions.familyId, familyId), sql`${sessions.revokedAt} IS NULL`));\n },\n async revokeUser(userId) {\n await db\n .update(sessions)\n .set({ revokedAt: new Date() })\n .where(and(eq(sessions.userId, userId), sql`${sessions.revokedAt} IS NULL`));\n },\n };\n\n const accountRowToAdapter = (r: Record<string, unknown>): AdapterAccount => ({\n id: String(r.id),\n userId: String(r.userId),\n provider: String(r.provider),\n providerAccountId: String(r.providerAccountId),\n email: (r.email as string | null | undefined) ?? null,\n accessToken: (r.accessToken as string | null | undefined) ?? null,\n refreshToken: (r.refreshToken as string | null | undefined) ?? null,\n expiresAt: (r.expiresAt as Date | null | undefined) ?? null,\n tokenType: (r.tokenType as string | null | undefined) ?? null,\n scope: (r.scope as string | null | undefined) ?? null,\n idToken: (r.idToken as string | null | undefined) ?? null,\n });\n\n const account: AccountAdapter = {\n async linkAccount(data) {\n const id = generateId();\n await db.insert(accounts).values({ id, ...data });\n const rows = await db.select().from(accounts).where(eq(accounts.id, id)).limit(1);\n return accountRowToAdapter(rows[0] as Record<string, unknown>);\n },\n async getAccountByProvider(provider, providerAccountId) {\n const rows = await db\n .select()\n .from(accounts)\n .where(and(eq(accounts.provider, provider), eq(accounts.providerAccountId, providerAccountId)))\n .limit(1);\n return rows[0] ? accountRowToAdapter(rows[0] as Record<string, unknown>) : null;\n },\n async getByProviderEmail(provider, email) {\n const rows = await db\n .select()\n .from(accounts)\n .where(and(eq(accounts.provider, provider), eq(accounts.email, email)))\n .limit(1);\n return rows[0] ? accountRowToAdapter(rows[0] as Record<string, unknown>) : null;\n },\n async listByUser(userId) {\n const rows = await db.select().from(accounts).where(eq(accounts.userId, userId));\n return (rows as Record<string, unknown>[]).map(accountRowToAdapter);\n },\n async unlinkAccount(id) {\n await db.delete(accounts).where(eq(accounts.id, id));\n },\n };\n\n const vtRowToAdapter = (r: Record<string, unknown>): AdapterVerificationToken => ({\n identifier: String(r.identifier),\n token: String(r.token),\n expiresAt: r.expiresAt as Date,\n });\n\n const verificationToken: VerificationTokenAdapter = {\n async create(data) {\n await db.insert(verificationTokens).values(data);\n return vtRowToAdapter(data as unknown as Record<string, unknown>);\n },\n async consume(identifier, token) {\n const rows = await db\n .select()\n .from(verificationTokens)\n .where(and(eq(verificationTokens.identifier, identifier), eq(verificationTokens.token, token)))\n .limit(1);\n const row = rows[0];\n if (!row) return null;\n await db\n .delete(verificationTokens)\n .where(and(eq(verificationTokens.identifier, identifier), eq(verificationTokens.token, token)));\n const rec = vtRowToAdapter(row as Record<string, unknown>);\n return rec.expiresAt.getTime() < Date.now() ? null : rec;\n },\n async purgeExpired() {\n const existing = await db\n .select({ identifier: verificationTokens.identifier })\n .from(verificationTokens)\n .where(sql`${verificationTokens.expiresAt} < NOW()`);\n await db.delete(verificationTokens).where(sql`${verificationTokens.expiresAt} < NOW()`);\n return (existing as unknown[]).length;\n },\n };\n\n const auditRowToAdapter = (r: Record<string, unknown>): AdapterAuditEvent => ({\n id: String(r.id),\n type: String(r.type),\n userId: (r.userId as string | null | undefined) ?? null,\n sessionId: (r.sessionId as string | null | undefined) ?? null,\n at: r.at as Date | undefined,\n ip: (r.ip as string | null | undefined) ?? null,\n userAgent: (r.userAgent as string | null | undefined) ?? null,\n data: (r.data as Record<string, unknown> | null | undefined) ?? null,\n });\n\n const auditLogAdapter: AuditLogAdapter = {\n async record(event) {\n await db.insert(auditLog).values({ id: event.id ?? generateId(), ...event });\n },\n async list(filter) {\n const conds = [];\n if (filter.userId) conds.push(eq(auditLog.userId, filter.userId));\n if (filter.type) conds.push(eq(auditLog.type, filter.type));\n const q = db.select().from(auditLog);\n const rows = conds.length\n ? await q.where(and(...conds)).limit(filter.limit ?? 100)\n : await q.limit(filter.limit ?? 100);\n return (rows as Record<string, unknown>[]).map(auditRowToAdapter);\n },\n };\n\n const transaction: TransactionAdapter = {\n async run(fn) {\n return db.transaction(async () => fn());\n },\n };\n\n return { user, session, account, verificationToken, auditLog: auditLogAdapter, transaction };\n}\n"]}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pgCore = require('drizzle-orm/pg-core');
|
|
4
|
+
var drizzleOrm = require('drizzle-orm');
|
|
5
|
+
|
|
6
|
+
// src/pg/index.ts
|
|
7
|
+
function createHoleauthTables(opts) {
|
|
8
|
+
const { usersTable, prefix = "holeauth_" } = opts;
|
|
9
|
+
const p = (s) => `${prefix}${s}`;
|
|
10
|
+
const sessions = pgCore.pgTable(
|
|
11
|
+
p("session"),
|
|
12
|
+
{
|
|
13
|
+
id: pgCore.text("id").primaryKey(),
|
|
14
|
+
userId: pgCore.text("user_id").notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
|
15
|
+
familyId: pgCore.text("family_id").notNull(),
|
|
16
|
+
refreshTokenHash: pgCore.text("refresh_token_hash").notNull(),
|
|
17
|
+
expiresAt: pgCore.timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(),
|
|
18
|
+
createdAt: pgCore.timestamp("created_at", { withTimezone: true, mode: "date" }).notNull().defaultNow(),
|
|
19
|
+
revokedAt: pgCore.timestamp("revoked_at", { withTimezone: true, mode: "date" }),
|
|
20
|
+
userAgent: pgCore.text("user_agent"),
|
|
21
|
+
ip: pgCore.text("ip")
|
|
22
|
+
},
|
|
23
|
+
(t) => ({
|
|
24
|
+
familyIdx: pgCore.index().on(t.familyId),
|
|
25
|
+
hashIdx: pgCore.uniqueIndex().on(t.refreshTokenHash),
|
|
26
|
+
userIdx: pgCore.index().on(t.userId)
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
const accounts = pgCore.pgTable(
|
|
30
|
+
p("account"),
|
|
31
|
+
{
|
|
32
|
+
id: pgCore.text("id").primaryKey(),
|
|
33
|
+
userId: pgCore.text("user_id").notNull().references(() => usersTable.id, { onDelete: "cascade" }),
|
|
34
|
+
provider: pgCore.text("provider").notNull(),
|
|
35
|
+
providerAccountId: pgCore.text("provider_account_id").notNull(),
|
|
36
|
+
email: pgCore.text("email"),
|
|
37
|
+
accessToken: pgCore.text("access_token"),
|
|
38
|
+
refreshToken: pgCore.text("refresh_token"),
|
|
39
|
+
expiresAt: pgCore.timestamp("expires_at", { withTimezone: true, mode: "date" }),
|
|
40
|
+
tokenType: pgCore.text("token_type"),
|
|
41
|
+
scope: pgCore.text("scope"),
|
|
42
|
+
idToken: pgCore.text("id_token")
|
|
43
|
+
},
|
|
44
|
+
(t) => ({
|
|
45
|
+
providerIdx: pgCore.uniqueIndex().on(t.provider, t.providerAccountId),
|
|
46
|
+
userIdx: pgCore.index().on(t.userId)
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
const verificationTokens = pgCore.pgTable(
|
|
50
|
+
p("verification_token"),
|
|
51
|
+
{
|
|
52
|
+
identifier: pgCore.text("identifier").notNull(),
|
|
53
|
+
token: pgCore.text("token").notNull(),
|
|
54
|
+
expiresAt: pgCore.timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull()
|
|
55
|
+
},
|
|
56
|
+
(t) => ({
|
|
57
|
+
pk: pgCore.primaryKey({ columns: [t.identifier, t.token] })
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
const auditLog = pgCore.pgTable(
|
|
61
|
+
p("audit_log"),
|
|
62
|
+
{
|
|
63
|
+
id: pgCore.text("id").primaryKey(),
|
|
64
|
+
type: pgCore.text("type").notNull(),
|
|
65
|
+
userId: pgCore.text("user_id").references(() => usersTable.id, { onDelete: "set null" }),
|
|
66
|
+
sessionId: pgCore.text("session_id"),
|
|
67
|
+
at: pgCore.timestamp("at", { withTimezone: true, mode: "date" }).notNull().defaultNow(),
|
|
68
|
+
ip: pgCore.text("ip"),
|
|
69
|
+
userAgent: pgCore.text("user_agent"),
|
|
70
|
+
data: pgCore.jsonb("data")
|
|
71
|
+
},
|
|
72
|
+
(t) => ({
|
|
73
|
+
typeIdx: pgCore.index().on(t.type),
|
|
74
|
+
userIdx: pgCore.index().on(t.userId)
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
const sessionsRelations = drizzleOrm.relations(sessions, ({ one }) => ({
|
|
78
|
+
user: one(usersTable, { fields: [sessions.userId], references: [usersTable.id] })
|
|
79
|
+
}));
|
|
80
|
+
const accountsRelations = drizzleOrm.relations(accounts, ({ one }) => ({
|
|
81
|
+
user: one(usersTable, { fields: [accounts.userId], references: [usersTable.id] })
|
|
82
|
+
}));
|
|
83
|
+
const auditLogRelations = drizzleOrm.relations(auditLog, ({ one }) => ({
|
|
84
|
+
user: one(usersTable, { fields: [auditLog.userId], references: [usersTable.id] })
|
|
85
|
+
}));
|
|
86
|
+
return {
|
|
87
|
+
tables: { users: usersTable, sessions, accounts, verificationTokens, auditLog },
|
|
88
|
+
relations: { sessionsRelations, accountsRelations, auditLogRelations }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createHoleauthAdapters(opts) {
|
|
92
|
+
const { db, tables, userEmailColumn = "email", generateId = () => crypto.randomUUID() } = opts;
|
|
93
|
+
const { users, sessions, accounts, verificationTokens, auditLog } = tables;
|
|
94
|
+
const emailCol = users[userEmailColumn];
|
|
95
|
+
if (!emailCol) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`[holeauth] usersTable has no "${userEmailColumn}" column; pass userEmailColumn to createHoleauthAdapters.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const userRowToAdapter = (row) => ({
|
|
101
|
+
id: String(row.id),
|
|
102
|
+
email: String(row[userEmailColumn] ?? ""),
|
|
103
|
+
emailVerified: row.emailVerified ?? null,
|
|
104
|
+
name: row.name ?? null,
|
|
105
|
+
image: row.image ?? null,
|
|
106
|
+
passwordHash: row.passwordHash ?? null
|
|
107
|
+
});
|
|
108
|
+
const user = {
|
|
109
|
+
async getUserById(id) {
|
|
110
|
+
const rows = await db.select().from(users).where(drizzleOrm.eq(users.id, id)).limit(1);
|
|
111
|
+
return rows[0] ? userRowToAdapter(rows[0]) : null;
|
|
112
|
+
},
|
|
113
|
+
async getUserByEmail(email) {
|
|
114
|
+
const rows = await db.select().from(users).where(drizzleOrm.eq(emailCol, email)).limit(1);
|
|
115
|
+
return rows[0] ? userRowToAdapter(rows[0]) : null;
|
|
116
|
+
},
|
|
117
|
+
async createUser(data) {
|
|
118
|
+
const id = generateId();
|
|
119
|
+
const row = {
|
|
120
|
+
id,
|
|
121
|
+
[userEmailColumn]: data.email,
|
|
122
|
+
emailVerified: data.emailVerified ?? null,
|
|
123
|
+
name: data.name ?? null,
|
|
124
|
+
image: data.image ?? null,
|
|
125
|
+
passwordHash: data.passwordHash ?? null
|
|
126
|
+
};
|
|
127
|
+
const [inserted] = await db.insert(users).values(row).returning();
|
|
128
|
+
return userRowToAdapter(inserted);
|
|
129
|
+
},
|
|
130
|
+
async updateUser(id, patch) {
|
|
131
|
+
const toSet = { ...patch };
|
|
132
|
+
if ("email" in toSet) {
|
|
133
|
+
toSet[userEmailColumn] = toSet.email;
|
|
134
|
+
if (userEmailColumn !== "email") delete toSet.email;
|
|
135
|
+
}
|
|
136
|
+
const [updated] = await db.update(users).set(toSet).where(drizzleOrm.eq(users.id, id)).returning();
|
|
137
|
+
if (!updated) throw new Error(`User ${id} not found`);
|
|
138
|
+
return userRowToAdapter(updated);
|
|
139
|
+
},
|
|
140
|
+
async deleteUser(id) {
|
|
141
|
+
await db.delete(users).where(drizzleOrm.eq(users.id, id));
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const sessionRowToAdapter = (r) => ({
|
|
145
|
+
id: String(r.id),
|
|
146
|
+
userId: String(r.userId),
|
|
147
|
+
familyId: String(r.familyId),
|
|
148
|
+
refreshTokenHash: String(r.refreshTokenHash),
|
|
149
|
+
expiresAt: r.expiresAt,
|
|
150
|
+
createdAt: r.createdAt,
|
|
151
|
+
revokedAt: r.revokedAt ?? null,
|
|
152
|
+
userAgent: r.userAgent ?? null,
|
|
153
|
+
ip: r.ip ?? null
|
|
154
|
+
});
|
|
155
|
+
const session = {
|
|
156
|
+
async createSession(data) {
|
|
157
|
+
const [row] = await db.insert(sessions).values(data).returning();
|
|
158
|
+
return sessionRowToAdapter(row);
|
|
159
|
+
},
|
|
160
|
+
async getSession(id) {
|
|
161
|
+
const rows = await db.select().from(sessions).where(drizzleOrm.eq(sessions.id, id)).limit(1);
|
|
162
|
+
return rows[0] ? sessionRowToAdapter(rows[0]) : null;
|
|
163
|
+
},
|
|
164
|
+
async getByRefreshHash(hash) {
|
|
165
|
+
const rows = await db.select().from(sessions).where(drizzleOrm.eq(sessions.refreshTokenHash, hash)).limit(1);
|
|
166
|
+
return rows[0] ? sessionRowToAdapter(rows[0]) : null;
|
|
167
|
+
},
|
|
168
|
+
async findByFamily(familyId) {
|
|
169
|
+
const rows = await db.select().from(sessions).where(drizzleOrm.eq(sessions.familyId, familyId));
|
|
170
|
+
return rows.map(sessionRowToAdapter);
|
|
171
|
+
},
|
|
172
|
+
async deleteSession(id) {
|
|
173
|
+
await db.delete(sessions).where(drizzleOrm.eq(sessions.id, id));
|
|
174
|
+
},
|
|
175
|
+
async rotateRefresh(id, newHash, expiresAt) {
|
|
176
|
+
const [row] = await db.update(sessions).set({ refreshTokenHash: newHash, expiresAt }).where(drizzleOrm.eq(sessions.id, id)).returning();
|
|
177
|
+
if (!row) throw new Error(`Session ${id} not found`);
|
|
178
|
+
return sessionRowToAdapter(row);
|
|
179
|
+
},
|
|
180
|
+
async revokeFamily(familyId) {
|
|
181
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(drizzleOrm.and(drizzleOrm.eq(sessions.familyId, familyId), drizzleOrm.sql`${sessions.revokedAt} IS NULL`));
|
|
182
|
+
},
|
|
183
|
+
async revokeUser(userId) {
|
|
184
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(drizzleOrm.and(drizzleOrm.eq(sessions.userId, userId), drizzleOrm.sql`${sessions.revokedAt} IS NULL`));
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const accountRowToAdapter = (r) => ({
|
|
188
|
+
id: String(r.id),
|
|
189
|
+
userId: String(r.userId),
|
|
190
|
+
provider: String(r.provider),
|
|
191
|
+
providerAccountId: String(r.providerAccountId),
|
|
192
|
+
email: r.email ?? null,
|
|
193
|
+
accessToken: r.accessToken ?? null,
|
|
194
|
+
refreshToken: r.refreshToken ?? null,
|
|
195
|
+
expiresAt: r.expiresAt ?? null,
|
|
196
|
+
tokenType: r.tokenType ?? null,
|
|
197
|
+
scope: r.scope ?? null,
|
|
198
|
+
idToken: r.idToken ?? null
|
|
199
|
+
});
|
|
200
|
+
const account = {
|
|
201
|
+
async linkAccount(data) {
|
|
202
|
+
const [row] = await db.insert(accounts).values({ id: generateId(), ...data }).returning();
|
|
203
|
+
return accountRowToAdapter(row);
|
|
204
|
+
},
|
|
205
|
+
async getAccountByProvider(provider, providerAccountId) {
|
|
206
|
+
const rows = await db.select().from(accounts).where(
|
|
207
|
+
drizzleOrm.and(drizzleOrm.eq(accounts.provider, provider), drizzleOrm.eq(accounts.providerAccountId, providerAccountId))
|
|
208
|
+
).limit(1);
|
|
209
|
+
return rows[0] ? accountRowToAdapter(rows[0]) : null;
|
|
210
|
+
},
|
|
211
|
+
async getByProviderEmail(provider, email) {
|
|
212
|
+
const rows = await db.select().from(accounts).where(drizzleOrm.and(drizzleOrm.eq(accounts.provider, provider), drizzleOrm.eq(accounts.email, email))).limit(1);
|
|
213
|
+
return rows[0] ? accountRowToAdapter(rows[0]) : null;
|
|
214
|
+
},
|
|
215
|
+
async listByUser(userId) {
|
|
216
|
+
const rows = await db.select().from(accounts).where(drizzleOrm.eq(accounts.userId, userId));
|
|
217
|
+
return rows.map(accountRowToAdapter);
|
|
218
|
+
},
|
|
219
|
+
async unlinkAccount(id) {
|
|
220
|
+
await db.delete(accounts).where(drizzleOrm.eq(accounts.id, id));
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const vtRowToAdapter = (r) => ({
|
|
224
|
+
identifier: String(r.identifier),
|
|
225
|
+
token: String(r.token),
|
|
226
|
+
expiresAt: r.expiresAt
|
|
227
|
+
});
|
|
228
|
+
const verificationToken = {
|
|
229
|
+
async create(data) {
|
|
230
|
+
const [row] = await db.insert(verificationTokens).values(data).returning();
|
|
231
|
+
return vtRowToAdapter(row);
|
|
232
|
+
},
|
|
233
|
+
async consume(identifier, token) {
|
|
234
|
+
const [row] = await db.delete(verificationTokens).where(
|
|
235
|
+
drizzleOrm.and(drizzleOrm.eq(verificationTokens.identifier, identifier), drizzleOrm.eq(verificationTokens.token, token))
|
|
236
|
+
).returning();
|
|
237
|
+
if (!row) return null;
|
|
238
|
+
const rec = vtRowToAdapter(row);
|
|
239
|
+
return rec.expiresAt.getTime() < Date.now() ? null : rec;
|
|
240
|
+
},
|
|
241
|
+
async purgeExpired() {
|
|
242
|
+
const res = await db.delete(verificationTokens).where(drizzleOrm.sql`${verificationTokens.expiresAt} < now()`).returning({ identifier: verificationTokens.identifier });
|
|
243
|
+
return res.length;
|
|
244
|
+
},
|
|
245
|
+
async listByIdentifierPrefix(prefix) {
|
|
246
|
+
const esc = prefix.replace(/[\\%_]/g, (c) => `\\${c}`);
|
|
247
|
+
const rows = await db.select().from(verificationTokens).where(drizzleOrm.like(verificationTokens.identifier, `${esc}%`));
|
|
248
|
+
return rows.map(vtRowToAdapter);
|
|
249
|
+
},
|
|
250
|
+
async deleteByIdentifier(identifier) {
|
|
251
|
+
const res = await db.delete(verificationTokens).where(drizzleOrm.eq(verificationTokens.identifier, identifier)).returning({ identifier: verificationTokens.identifier });
|
|
252
|
+
return res.length;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
const auditRowToAdapter = (r) => ({
|
|
256
|
+
id: String(r.id),
|
|
257
|
+
type: String(r.type),
|
|
258
|
+
userId: r.userId ?? null,
|
|
259
|
+
sessionId: r.sessionId ?? null,
|
|
260
|
+
at: r.at,
|
|
261
|
+
ip: r.ip ?? null,
|
|
262
|
+
userAgent: r.userAgent ?? null,
|
|
263
|
+
data: r.data ?? null
|
|
264
|
+
});
|
|
265
|
+
const auditLogAdapter = {
|
|
266
|
+
async record(event) {
|
|
267
|
+
await db.insert(auditLog).values({ id: event.id ?? generateId(), ...event });
|
|
268
|
+
},
|
|
269
|
+
async list(filter) {
|
|
270
|
+
const conds = [];
|
|
271
|
+
if (filter.userId) conds.push(drizzleOrm.eq(auditLog.userId, filter.userId));
|
|
272
|
+
if (filter.type) conds.push(drizzleOrm.eq(auditLog.type, filter.type));
|
|
273
|
+
const q = db.select().from(auditLog);
|
|
274
|
+
const rows = conds.length ? await q.where(drizzleOrm.and(...conds)).limit(filter.limit ?? 100) : await q.limit(filter.limit ?? 100);
|
|
275
|
+
return rows.map(auditRowToAdapter);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const transaction = {
|
|
279
|
+
async run(fn) {
|
|
280
|
+
return db.transaction(async () => fn());
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
return { user, session, account, verificationToken, auditLog: auditLogAdapter, transaction };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
exports.createHoleauthAdapters = createHoleauthAdapters;
|
|
287
|
+
exports.createHoleauthTables = createHoleauthTables;
|
|
288
|
+
//# sourceMappingURL=index.cjs.map
|
|
289
|
+
//# sourceMappingURL=index.cjs.map
|