@donkeylabs/cli 1.1.16 → 1.1.18
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/package.json +1 -1
- package/src/commands/generate.ts +153 -3
- package/templates/sveltekit-app/package.json +3 -3
- package/templates/sveltekit-app/src/lib/permissions.ts +213 -0
- package/templates/sveltekit-app/src/routes/+page.server.ts +1 -1
- package/templates/sveltekit-app/src/routes/workflows/+page.server.ts +1 -1
- package/templates/sveltekit-app/src/server/index.ts +46 -1
- package/templates/sveltekit-app/src/server/plugins/auth/auth.test.ts +377 -0
- package/templates/sveltekit-app/src/server/plugins/auth/index.ts +7 -7
- package/templates/sveltekit-app/src/server/plugins/auth/schema.ts +65 -0
- package/templates/sveltekit-app/src/server/plugins/email/email.test.ts +369 -0
- package/templates/sveltekit-app/src/server/plugins/email/schema.ts +24 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/index.ts +1048 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/001_create_tenants.ts +63 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/002_create_roles.ts +90 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/migrations/003_create_resource_grants.ts +50 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/permissions.test.ts +566 -0
- package/templates/sveltekit-app/src/server/plugins/permissions/schema.ts +67 -0
- package/templates/sveltekit-app/src/server/plugins/workflow-demo/index.ts +3 -2
- package/templates/sveltekit-app/src/server/routes/auth/handlers/login.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/logout.handler.ts +5 -8
- package/templates/sveltekit-app/src/server/routes/auth/handlers/me.handler.ts +4 -7
- package/templates/sveltekit-app/src/server/routes/auth/handlers/refresh.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/register.handler.ts +4 -6
- package/templates/sveltekit-app/src/server/routes/auth/handlers/update-profile.handler.ts +5 -8
- package/templates/sveltekit-app/src/server/routes/auth/index.ts +6 -7
- package/templates/sveltekit-app/src/server/routes/example/handlers/greet.handler.ts +3 -5
- package/templates/sveltekit-app/src/server/routes/permissions/index.ts +248 -0
- package/templates/sveltekit-app/src/server/routes/tenants/index.ts +339 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Plugin Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for authentication with multiple strategies:
|
|
5
|
+
* - session: Stateful database sessions
|
|
6
|
+
* - jwt: Stateless JWT tokens
|
|
7
|
+
* - refresh-token: Access + refresh token pattern
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
11
|
+
import { createTestHarness } from "@donkeylabs/server";
|
|
12
|
+
import { authPlugin } from "./index";
|
|
13
|
+
|
|
14
|
+
// Helper to create test harness with auth plugin
|
|
15
|
+
async function createAuthTestHarness(config: Parameters<typeof authPlugin>[0] = {}) {
|
|
16
|
+
const harness = await createTestHarness(authPlugin(config));
|
|
17
|
+
return { ...harness, auth: harness.manager.getServices().auth };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ==========================================
|
|
21
|
+
// Session Strategy Tests
|
|
22
|
+
// ==========================================
|
|
23
|
+
describe("Auth Plugin - Session Strategy", () => {
|
|
24
|
+
let harness: Awaited<ReturnType<typeof createAuthTestHarness>>;
|
|
25
|
+
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
harness = await createAuthTestHarness({ strategy: "session" });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(async () => {
|
|
31
|
+
await harness.db.destroy();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should register a new user", async () => {
|
|
35
|
+
const result = await harness.auth.register({
|
|
36
|
+
email: "test@example.com",
|
|
37
|
+
password: "password123",
|
|
38
|
+
name: "Test User",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result.user).toBeDefined();
|
|
42
|
+
expect(result.user.email).toBe("test@example.com");
|
|
43
|
+
expect(result.user.name).toBe("Test User");
|
|
44
|
+
expect(result.tokens.accessToken).toBeDefined();
|
|
45
|
+
expect(result.tokens.expiresIn).toBeGreaterThan(0);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should prevent duplicate email registration", async () => {
|
|
49
|
+
await harness.auth.register({
|
|
50
|
+
email: "test@example.com",
|
|
51
|
+
password: "password123",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await expect(
|
|
55
|
+
harness.auth.register({
|
|
56
|
+
email: "test@example.com",
|
|
57
|
+
password: "differentpassword",
|
|
58
|
+
})
|
|
59
|
+
).rejects.toThrow();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should login with correct credentials", async () => {
|
|
63
|
+
await harness.auth.register({
|
|
64
|
+
email: "test@example.com",
|
|
65
|
+
password: "password123",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const result = await harness.auth.login({
|
|
69
|
+
email: "test@example.com",
|
|
70
|
+
password: "password123",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result.user.email).toBe("test@example.com");
|
|
74
|
+
expect(result.tokens.accessToken).toBeDefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should reject login with wrong password", async () => {
|
|
78
|
+
await harness.auth.register({
|
|
79
|
+
email: "test@example.com",
|
|
80
|
+
password: "password123",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await expect(
|
|
84
|
+
harness.auth.login({
|
|
85
|
+
email: "test@example.com",
|
|
86
|
+
password: "wrongpassword",
|
|
87
|
+
})
|
|
88
|
+
).rejects.toThrow();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should reject login for non-existent user", async () => {
|
|
92
|
+
await expect(
|
|
93
|
+
harness.auth.login({
|
|
94
|
+
email: "nonexistent@example.com",
|
|
95
|
+
password: "password123",
|
|
96
|
+
})
|
|
97
|
+
).rejects.toThrow();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("should validate session token", async () => {
|
|
101
|
+
const { tokens } = await harness.auth.register({
|
|
102
|
+
email: "test@example.com",
|
|
103
|
+
password: "password123",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const user = await harness.auth.validateToken(tokens.accessToken);
|
|
107
|
+
expect(user).toBeDefined();
|
|
108
|
+
expect(user?.email).toBe("test@example.com");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should invalidate session on logout", async () => {
|
|
112
|
+
const { tokens } = await harness.auth.register({
|
|
113
|
+
email: "test@example.com",
|
|
114
|
+
password: "password123",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await harness.auth.logout(tokens.accessToken);
|
|
118
|
+
|
|
119
|
+
const user = await harness.auth.validateToken(tokens.accessToken);
|
|
120
|
+
expect(user).toBeNull();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should return null for invalid token", async () => {
|
|
124
|
+
const user = await harness.auth.validateToken("invalid-token");
|
|
125
|
+
expect(user).toBeNull();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should get user by ID", async () => {
|
|
129
|
+
const { user: created } = await harness.auth.register({
|
|
130
|
+
email: "test@example.com",
|
|
131
|
+
password: "password123",
|
|
132
|
+
name: "Test User",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const user = await harness.auth.getUserById(created.id);
|
|
136
|
+
expect(user).toBeDefined();
|
|
137
|
+
expect(user?.email).toBe("test@example.com");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("should update user profile", async () => {
|
|
141
|
+
const { user: created } = await harness.auth.register({
|
|
142
|
+
email: "test@example.com",
|
|
143
|
+
password: "password123",
|
|
144
|
+
name: "Original Name",
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const updated = await harness.auth.updateProfile(created.id, {
|
|
148
|
+
name: "Updated Name",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(updated.name).toBe("Updated Name");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// ==========================================
|
|
156
|
+
// JWT Strategy Tests
|
|
157
|
+
// ==========================================
|
|
158
|
+
describe("Auth Plugin - JWT Strategy", () => {
|
|
159
|
+
let harness: Awaited<ReturnType<typeof createAuthTestHarness>>;
|
|
160
|
+
|
|
161
|
+
beforeEach(async () => {
|
|
162
|
+
harness = await createAuthTestHarness({
|
|
163
|
+
strategy: "jwt",
|
|
164
|
+
jwt: { secret: "test-secret-key-at-least-32-chars!" },
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
afterEach(async () => {
|
|
169
|
+
await harness.db.destroy();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("should register and return JWT token", async () => {
|
|
173
|
+
const result = await harness.auth.register({
|
|
174
|
+
email: "test@example.com",
|
|
175
|
+
password: "password123",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
expect(result.tokens.accessToken).toBeDefined();
|
|
179
|
+
// JWT tokens are longer and have 3 parts
|
|
180
|
+
expect(result.tokens.accessToken.split(".").length).toBe(3);
|
|
181
|
+
expect(result.tokens.refreshToken).toBeUndefined();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("should validate JWT token", async () => {
|
|
185
|
+
const { tokens } = await harness.auth.register({
|
|
186
|
+
email: "test@example.com",
|
|
187
|
+
password: "password123",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const user = await harness.auth.validateToken(tokens.accessToken);
|
|
191
|
+
expect(user).toBeDefined();
|
|
192
|
+
expect(user?.email).toBe("test@example.com");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should reject invalid JWT token", async () => {
|
|
196
|
+
const user = await harness.auth.validateToken("invalid.jwt.token");
|
|
197
|
+
expect(user).toBeNull();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should login and return JWT", async () => {
|
|
201
|
+
await harness.auth.register({
|
|
202
|
+
email: "test@example.com",
|
|
203
|
+
password: "password123",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const result = await harness.auth.login({
|
|
207
|
+
email: "test@example.com",
|
|
208
|
+
password: "password123",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
expect(result.tokens.accessToken.split(".").length).toBe(3);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// ==========================================
|
|
216
|
+
// Refresh Token Strategy Tests
|
|
217
|
+
// ==========================================
|
|
218
|
+
describe("Auth Plugin - Refresh Token Strategy", () => {
|
|
219
|
+
let harness: Awaited<ReturnType<typeof createAuthTestHarness>>;
|
|
220
|
+
|
|
221
|
+
beforeEach(async () => {
|
|
222
|
+
harness = await createAuthTestHarness({
|
|
223
|
+
strategy: "refresh-token",
|
|
224
|
+
jwt: { secret: "test-secret-key-at-least-32-chars!" },
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
afterEach(async () => {
|
|
229
|
+
await harness.db.destroy();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("should return both access and refresh tokens", async () => {
|
|
233
|
+
const result = await harness.auth.register({
|
|
234
|
+
email: "test@example.com",
|
|
235
|
+
password: "password123",
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
expect(result.tokens.accessToken).toBeDefined();
|
|
239
|
+
expect(result.tokens.refreshToken).toBeDefined();
|
|
240
|
+
expect(result.tokens.accessToken.split(".").length).toBe(3);
|
|
241
|
+
expect(result.tokens.refreshToken!.split(".").length).toBe(3);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should validate access token", async () => {
|
|
245
|
+
const { tokens } = await harness.auth.register({
|
|
246
|
+
email: "test@example.com",
|
|
247
|
+
password: "password123",
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const user = await harness.auth.validateToken(tokens.accessToken);
|
|
251
|
+
expect(user).toBeDefined();
|
|
252
|
+
expect(user?.email).toBe("test@example.com");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should refresh tokens with refresh token", async () => {
|
|
256
|
+
const { tokens: initial } = await harness.auth.register({
|
|
257
|
+
email: "test@example.com",
|
|
258
|
+
password: "password123",
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Small delay to ensure different token timestamps
|
|
262
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
263
|
+
|
|
264
|
+
const refreshed = await harness.auth.refresh(initial.refreshToken!);
|
|
265
|
+
|
|
266
|
+
expect(refreshed.accessToken).toBeDefined();
|
|
267
|
+
// Verify we got a new valid token (structure check)
|
|
268
|
+
expect(refreshed.accessToken.split(".").length).toBe(3);
|
|
269
|
+
expect(refreshed.expiresIn).toBeGreaterThan(0);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("should reject invalid refresh token", async () => {
|
|
273
|
+
await expect(
|
|
274
|
+
harness.auth.refresh("invalid.refresh.token")
|
|
275
|
+
).rejects.toThrow();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("should invalidate refresh token on logout", async () => {
|
|
279
|
+
const { tokens } = await harness.auth.register({
|
|
280
|
+
email: "test@example.com",
|
|
281
|
+
password: "password123",
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await harness.auth.logout(tokens.refreshToken!);
|
|
285
|
+
|
|
286
|
+
await expect(
|
|
287
|
+
harness.auth.refresh(tokens.refreshToken!)
|
|
288
|
+
).rejects.toThrow();
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// ==========================================
|
|
293
|
+
// Configuration Tests
|
|
294
|
+
// ==========================================
|
|
295
|
+
describe("Auth Plugin - Configuration", () => {
|
|
296
|
+
it("should use session strategy by default", async () => {
|
|
297
|
+
const harness = await createAuthTestHarness({});
|
|
298
|
+
expect(harness.auth.getStrategy()).toBe("session");
|
|
299
|
+
await harness.db.destroy();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("should respect custom strategy", async () => {
|
|
303
|
+
const harness = await createAuthTestHarness({
|
|
304
|
+
strategy: "jwt",
|
|
305
|
+
jwt: { secret: "test-secret-key-at-least-32-chars!" },
|
|
306
|
+
});
|
|
307
|
+
expect(harness.auth.getStrategy()).toBe("jwt");
|
|
308
|
+
await harness.db.destroy();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("should throw if JWT secret missing for jwt strategy", async () => {
|
|
312
|
+
await expect(
|
|
313
|
+
createAuthTestHarness({ strategy: "jwt" })
|
|
314
|
+
).rejects.toThrow(/jwt.secret/);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("should throw if JWT secret missing for refresh-token strategy", async () => {
|
|
318
|
+
await expect(
|
|
319
|
+
createAuthTestHarness({ strategy: "refresh-token" })
|
|
320
|
+
).rejects.toThrow(/jwt.secret/);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("should return cookie config", async () => {
|
|
324
|
+
const harness = await createAuthTestHarness({
|
|
325
|
+
cookie: { name: "myauth", httpOnly: true, secure: false },
|
|
326
|
+
});
|
|
327
|
+
const config = harness.auth.getCookieConfig();
|
|
328
|
+
expect(config.name).toBe("myauth");
|
|
329
|
+
expect(config.httpOnly).toBe(true);
|
|
330
|
+
expect(config.secure).toBe(false);
|
|
331
|
+
await harness.db.destroy();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// ==========================================
|
|
336
|
+
// Edge Cases
|
|
337
|
+
// ==========================================
|
|
338
|
+
describe("Auth Plugin - Edge Cases", () => {
|
|
339
|
+
let harness: Awaited<ReturnType<typeof createAuthTestHarness>>;
|
|
340
|
+
|
|
341
|
+
beforeEach(async () => {
|
|
342
|
+
harness = await createAuthTestHarness({ strategy: "session" });
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
afterEach(async () => {
|
|
346
|
+
await harness.db.destroy();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should handle email case-insensitively", async () => {
|
|
350
|
+
await harness.auth.register({
|
|
351
|
+
email: "Test@Example.COM",
|
|
352
|
+
password: "password123",
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const result = await harness.auth.login({
|
|
356
|
+
email: "test@example.com",
|
|
357
|
+
password: "password123",
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(result.user.email).toBe("test@example.com");
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should handle registration without name", async () => {
|
|
364
|
+
const result = await harness.auth.register({
|
|
365
|
+
email: "test@example.com",
|
|
366
|
+
password: "password123",
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
expect(result.user.name).toBeNull();
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("should return null for non-existent user ID", async () => {
|
|
373
|
+
const user = await harness.auth.getUserById("non-existent-id");
|
|
374
|
+
expect(user).toBeNull();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
});
|
|
@@ -475,7 +475,7 @@ export const authPlugin = createPlugin
|
|
|
475
475
|
.executeTakeFirst();
|
|
476
476
|
|
|
477
477
|
if (existing) {
|
|
478
|
-
throw ctx.errors.
|
|
478
|
+
throw ctx.core.errors.BadRequest();
|
|
479
479
|
}
|
|
480
480
|
|
|
481
481
|
const passwordHash = await Bun.password.hash(password, {
|
|
@@ -527,12 +527,12 @@ export const authPlugin = createPlugin
|
|
|
527
527
|
.executeTakeFirst();
|
|
528
528
|
|
|
529
529
|
if (!dbUser) {
|
|
530
|
-
throw ctx.errors.
|
|
530
|
+
throw ctx.core.errors.Unauthorized();
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
const valid = await Bun.password.verify(password, dbUser.password_hash);
|
|
534
534
|
if (!valid) {
|
|
535
|
-
throw ctx.errors.
|
|
535
|
+
throw ctx.core.errors.Unauthorized();
|
|
536
536
|
}
|
|
537
537
|
|
|
538
538
|
const user: AuthUser = {
|
|
@@ -558,7 +558,7 @@ export const authPlugin = createPlugin
|
|
|
558
558
|
|
|
559
559
|
const payload = await verifyJWT(refreshToken, jwtSecret);
|
|
560
560
|
if (!payload || payload.type !== "refresh") {
|
|
561
|
-
throw ctx.errors.
|
|
561
|
+
throw ctx.core.errors.Unauthorized();
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
// Verify refresh token exists in DB (not revoked)
|
|
@@ -577,7 +577,7 @@ export const authPlugin = createPlugin
|
|
|
577
577
|
}
|
|
578
578
|
|
|
579
579
|
if (!validToken) {
|
|
580
|
-
throw ctx.errors.
|
|
580
|
+
throw ctx.core.errors.Unauthorized();
|
|
581
581
|
}
|
|
582
582
|
|
|
583
583
|
// Get user
|
|
@@ -588,7 +588,7 @@ export const authPlugin = createPlugin
|
|
|
588
588
|
.executeTakeFirst();
|
|
589
589
|
|
|
590
590
|
if (!user) {
|
|
591
|
-
throw ctx.errors.
|
|
591
|
+
throw ctx.core.errors.Unauthorized();
|
|
592
592
|
}
|
|
593
593
|
|
|
594
594
|
// Create new access token (keep same refresh token)
|
|
@@ -691,7 +691,7 @@ export const authPlugin = createPlugin
|
|
|
691
691
|
.executeTakeFirst();
|
|
692
692
|
|
|
693
693
|
if (existing) {
|
|
694
|
-
throw ctx.errors.
|
|
694
|
+
throw ctx.core.errors.BadRequest();
|
|
695
695
|
}
|
|
696
696
|
|
|
697
697
|
updates.email = data.email.toLowerCase();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was generated by kysely-codegen.
|
|
3
|
+
* Please do not edit it manually.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ColumnType } from "kysely";
|
|
7
|
+
|
|
8
|
+
export type Generated<T> = T extends ColumnType<infer S, infer I, infer U>
|
|
9
|
+
? ColumnType<S, I | undefined, U>
|
|
10
|
+
: ColumnType<T, T | undefined, T>;
|
|
11
|
+
|
|
12
|
+
export interface PasskeyChallenges {
|
|
13
|
+
challenge: string;
|
|
14
|
+
created_at: Generated<string>;
|
|
15
|
+
expires_at: string;
|
|
16
|
+
id: string | null;
|
|
17
|
+
type: string;
|
|
18
|
+
user_id: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Passkeys {
|
|
22
|
+
backed_up: Generated<number>;
|
|
23
|
+
counter: Generated<number>;
|
|
24
|
+
created_at: Generated<string>;
|
|
25
|
+
credential_id: string;
|
|
26
|
+
device_type: string | null;
|
|
27
|
+
id: string | null;
|
|
28
|
+
last_used_at: string | null;
|
|
29
|
+
name: string | null;
|
|
30
|
+
public_key: string;
|
|
31
|
+
transports: string | null;
|
|
32
|
+
user_id: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RefreshTokens {
|
|
36
|
+
created_at: Generated<string>;
|
|
37
|
+
expires_at: string;
|
|
38
|
+
id: string | null;
|
|
39
|
+
token_hash: string;
|
|
40
|
+
user_id: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface Sessions {
|
|
44
|
+
created_at: Generated<string>;
|
|
45
|
+
expires_at: string;
|
|
46
|
+
id: string | null;
|
|
47
|
+
user_id: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface Users {
|
|
51
|
+
created_at: Generated<string>;
|
|
52
|
+
email: string;
|
|
53
|
+
id: string | null;
|
|
54
|
+
name: string | null;
|
|
55
|
+
password_hash: string;
|
|
56
|
+
updated_at: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface DB {
|
|
60
|
+
passkey_challenges: PasskeyChallenges;
|
|
61
|
+
passkeys: Passkeys;
|
|
62
|
+
refresh_tokens: RefreshTokens;
|
|
63
|
+
sessions: Sessions;
|
|
64
|
+
users: Users;
|
|
65
|
+
}
|