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