Skip to content

Commit 16edafa

Browse files
author
Lasim
committed
feat: replace dynamic schema generation with static schema import and enhance session validation logic
1 parent 5ad059f commit 16edafa

File tree

4 files changed

+85
-49
lines changed

4 files changed

+85
-49
lines changed

cookies.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

services/backend/src/db/index.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,35 +37,17 @@ function getColumnBuilder(type: 'text' | 'integer' | 'timestamp') {
3737
}
3838

3939
function generateSchema(): AnySchema {
40-
const generatedSchema: AnySchema = {};
41-
42-
for (const [tableName, tableColumns] of Object.entries(baseTableDefinitions)) {
43-
const columns: Record<string, ReturnType<typeof tableColumns[keyof typeof tableColumns]>> = {};
44-
for (const [columnName, columnDefFunc] of Object.entries(tableColumns)) {
45-
let builderType: 'text' | 'integer' | 'timestamp' = 'text';
46-
47-
// Special handling for specific columns
48-
if (columnName === 'id' || columnName === 'user_id') {
49-
builderType = 'text'; // All IDs are text (Lucia uses string IDs)
50-
} else if (columnName === 'expires_at') {
51-
builderType = 'integer'; // Lucia uses number for expires_at
52-
} else if (columnName.toLowerCase().includes('at') || columnName.toLowerCase().includes('date')) {
53-
builderType = 'timestamp';
54-
} else if (['count', 'age', 'quantity', 'order', 'status', 'number'].some(keyword => columnName.toLowerCase().includes(keyword)) && !columnName.toLowerCase().includes('text')) {
55-
builderType = 'integer';
56-
}
57-
58-
const builder = getColumnBuilder(builderType);
59-
columns[columnName] = columnDefFunc(builder);
60-
}
61-
generatedSchema[tableName] = sqliteTable(tableName, columns);
62-
}
40+
// Import the static schema instead of generating it dynamically
41+
// This avoids SQL syntax errors caused by dynamic schema generation
42+
const staticSchema = require('./schema.sqlite');
43+
const generatedSchema = { ...staticSchema };
6344

45+
// Add plugin tables to the static schema
6446
for (const [tableName, tableColumns] of Object.entries(inputPluginTableDefinitions)) {
6547
const columns: Record<string, ReturnType<typeof tableColumns[keyof typeof tableColumns]>> = {};
66-
for (const [columnName, columnDefFunc] of Object.entries(tableColumns)) {
48+
for (const [columnName, columnDefFunc] of Object.entries(tableColumns)) {
6749
let builderType: 'text' | 'integer' | 'timestamp' = 'text';
68-
if (columnName.toLowerCase().includes('at') || columnName.toLowerCase().includes('date')) {
50+
if (columnName.toLowerCase().includes('at') || columnName.toLowerCase().includes('date')) {
6951
builderType = 'timestamp';
7052
} else if (['id', 'count', 'age', 'quantity', 'order', 'status', 'number'].some(keyword => columnName.toLowerCase().includes(keyword))) {
7153
builderType = 'integer';

services/backend/src/db/schema.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ export const usersTableColumns = {
1515
id: (columnBuilder: any) => columnBuilder('id').primaryKey(),
1616
email: (columnBuilder: any) => columnBuilder('email').notNull().unique(),
1717
name: (columnBuilder: any) => columnBuilder('name'),
18-
// Added .defaultNow() to let Drizzle handle dialect-specific default timestamp generation
19-
createdAt: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().defaultNow(),
20-
updatedAt: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().defaultNow(),
18+
// Use $defaultFn for SQLite timestamp generation
19+
createdAt: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
20+
updatedAt: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
2121
};
2222

2323
// Enum for authentication types
@@ -30,8 +30,8 @@ export const rolesTableColumns = {
3030
description: (columnBuilder: any) => columnBuilder('description'),
3131
permissions: (columnBuilder: any) => columnBuilder('permissions').notNull(), // JSON string
3232
is_system_role: (columnBuilder: any) => columnBuilder('is_system_role').notNull().default(false),
33-
created_at: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().defaultNow(),
34-
updated_at: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().defaultNow(),
33+
created_at: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
34+
updated_at: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
3535
};
3636

3737
export const authUserTableColumns = {
@@ -66,8 +66,8 @@ export const teamsTableColumns = {
6666
slug: (columnBuilder: any) => columnBuilder('slug').notNull().unique(),
6767
description: (columnBuilder: any) => columnBuilder('description'),
6868
owner_id: (columnBuilder: any) => columnBuilder('owner_id').notNull(), // Foreign key to authUser.id
69-
created_at: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().defaultNow(),
70-
updated_at: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().defaultNow(),
69+
created_at: (columnBuilder: any) => columnBuilder('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
70+
updated_at: (columnBuilder: any) => columnBuilder('updated_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
7171
};
7272

7373
// Enum for team member roles
@@ -78,7 +78,7 @@ export const teamMembershipsTableColumns = {
7878
team_id: (columnBuilder: any) => columnBuilder('team_id').notNull(), // Foreign key to teams.id
7979
user_id: (columnBuilder: any) => columnBuilder('user_id').notNull(), // Foreign key to authUser.id
8080
role: (columnBuilder: any) => columnBuilder('role').notNull(), // team_admin or team_user
81-
joined_at: (columnBuilder: any) => columnBuilder('joined_at', { mode: 'timestamp' }).notNull().defaultNow(),
81+
joined_at: (columnBuilder: any) => columnBuilder('joined_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
8282
};
8383

8484
// This object will hold definitions for all base tables.

services/backend/src/hooks/authHook.ts

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { FastifyRequest, FastifyReply, HookHandlerDoneFunction } from 'fastify';
22
import { getLucia } from '../lib/lucia';
3-
import { getDbStatus, getSchema } from '../db';
3+
import { getDbStatus, getSchema, getDb } from '../db';
4+
import { eq } from 'drizzle-orm';
45
import type { User, Session } from 'lucia';
56

67
// Augment FastifyRequest to include user and session
@@ -37,23 +38,80 @@ export async function authHook(
3738
}
3839

3940
request.log.debug(`Auth hook: Found session ID: ${sessionId}`);
40-
const { session, user } = await lucia.validateSession(sessionId);
41-
42-
if (session && session.fresh) {
43-
// Session was refreshed, send new cookie
44-
request.log.debug(`Auth hook: Session ${sessionId} is fresh, sending new cookie`);
45-
const sessionCookie = lucia.createSessionCookie(session.id);
41+
42+
// Manual session validation to avoid Lucia SQL syntax issues
43+
const db = getDb();
44+
const schema = getSchema();
45+
const authSessionTable = schema.authSession;
46+
const authUserTable = schema.authUser;
47+
48+
if (!authSessionTable || !authUserTable) {
49+
request.log.error('Auth tables not found in schema');
50+
request.user = null;
51+
request.session = null;
52+
return;
53+
}
54+
55+
// Query session and user manually
56+
const sessionResult = await db.select({
57+
sessionId: authSessionTable.id,
58+
userId: authSessionTable.user_id,
59+
expiresAt: authSessionTable.expires_at,
60+
username: authUserTable.username,
61+
email: authUserTable.email,
62+
firstName: authUserTable.first_name,
63+
lastName: authUserTable.last_name,
64+
authType: authUserTable.auth_type,
65+
githubId: authUserTable.github_id
66+
})
67+
.from(authSessionTable)
68+
.innerJoin(authUserTable, eq(authSessionTable.user_id, authUserTable.id))
69+
.where(eq(authSessionTable.id, sessionId))
70+
.limit(1);
71+
72+
if (sessionResult.length === 0) {
73+
request.log.debug(`Auth hook: Session ${sessionId} not found`);
74+
const sessionCookie = lucia.createBlankSessionCookie();
4675
reply.setCookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
76+
request.user = null;
77+
request.session = null;
78+
return;
4779
}
48-
if (!session) {
49-
// Invalid session, clear cookie
50-
request.log.debug(`Auth hook: Session ${sessionId} is invalid, clearing cookie`);
80+
81+
const sessionData = sessionResult[0];
82+
83+
// Check if session is expired
84+
if (sessionData.expiresAt < Date.now()) {
85+
request.log.debug(`Auth hook: Session ${sessionId} is expired`);
86+
// Delete expired session
87+
await db.delete(authSessionTable).where(eq(authSessionTable.id, sessionId));
5188
const sessionCookie = lucia.createBlankSessionCookie();
5289
reply.setCookie(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
53-
} else {
54-
request.log.debug(`Auth hook: Session ${sessionId} is valid for user ${user?.id}`);
90+
request.user = null;
91+
request.session = null;
92+
return;
5593
}
94+
95+
// Create user and session objects
96+
const user = {
97+
id: sessionData.userId,
98+
username: sessionData.username,
99+
email: sessionData.email,
100+
firstName: sessionData.firstName,
101+
lastName: sessionData.lastName,
102+
authType: sessionData.authType,
103+
githubId: sessionData.githubId
104+
};
105+
106+
const session = {
107+
id: sessionData.sessionId,
108+
userId: sessionData.userId,
109+
expiresAt: new Date(sessionData.expiresAt),
110+
fresh: false
111+
};
56112

113+
request.log.debug(`Auth hook: Session ${sessionId} is valid for user ${user.id}`);
114+
57115
request.user = user;
58116
request.session = session;
59117
// No explicit done() call, Fastify awaits the promise

0 commit comments

Comments
 (0)