From ae34265f1d447fe00174e4a32ae86faaa27d9b26 Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sat, 21 Feb 2026 14:48:37 -0700 Subject: [PATCH 1/7] Adds schema logging source --- .../db/drizzle/0012_lumpy_the_anarchist.sql | 1 + packages/db/drizzle/meta/0012_snapshot.json | 842 ++++++++++++++++++ packages/db/drizzle/meta/_journal.json | 7 + packages/db/schema.ts | 5 + 4 files changed, 855 insertions(+) create mode 100644 packages/db/drizzle/0012_lumpy_the_anarchist.sql create mode 100644 packages/db/drizzle/meta/0012_snapshot.json diff --git a/packages/db/drizzle/0012_lumpy_the_anarchist.sql b/packages/db/drizzle/0012_lumpy_the_anarchist.sql new file mode 100644 index 0000000..1cc3aeb --- /dev/null +++ b/packages/db/drizzle/0012_lumpy_the_anarchist.sql @@ -0,0 +1 @@ +ALTER TABLE `log` ADD `source` text NOT NULL; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0012_snapshot.json b/packages/db/drizzle/meta/0012_snapshot.json new file mode 100644 index 0000000..8e926bb --- /dev/null +++ b/packages/db/drizzle/meta/0012_snapshot.json @@ -0,0 +1,842 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "3e77dc7c-fc5e-4778-b47a-608216379cf9", + "prevId": "39b0cf06-8d6d-489a-875e-cc3b19d55c00", + "tables": { + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job": { + "name": "backup_job", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "authentication_data": { + "name": "authentication_data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "database_type": { + "name": "database_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cron_string": { + "name": "cron_string", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_team_id_team_id_fk": { + "name": "backup_job_team_id_team_id_fk", + "tableFrom": "backup_job", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "backup_job_run": { + "name": "backup_job_run", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "invocation_type": { + "name": "invocation_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "backup_job_id": { + "name": "backup_job_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "completed_at": { + "name": "completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result": { + "name": "result", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_job_run_backup_job_id_backup_job_id_fk": { + "name": "backup_job_run_backup_job_id_backup_job_id_fk", + "tableFrom": "backup_job_run", + "tableTo": "backup_job", + "columnsFrom": [ + "backup_job_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "log": { + "name": "log", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "log_type": { + "name": "log_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "route": { + "name": "route", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_id": { + "name": "request_id", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "time_elapsed_ms": { + "name": "time_elapsed_ms", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team": { + "name": "team", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_invite": { + "name": "team_invite", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'MEMBER'" + } + }, + "indexes": {}, + "foreignKeys": { + "team_invite_team_id_team_id_fk": { + "name": "team_invite_team_id_team_id_fk", + "tableFrom": "team_invite", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_join_request": { + "name": "team_join_request", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'PENDING'" + } + }, + "indexes": {}, + "foreignKeys": { + "team_join_request_team_id_team_id_fk": { + "name": "team_join_request_team_id_team_id_fk", + "tableFrom": "team_join_request", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_join_request_user_id_user_id_fk": { + "name": "team_join_request_user_id_user_id_fk", + "tableFrom": "team_join_request", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_name": { + "name": "last_name", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_seen": { + "name": "last_seen", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + }, + "site_role": { + "name": "site_role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'USER'" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user_to_team": { + "name": "user_to_team", + "columns": { + "user_id": { + "name": "user_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "team_id": { + "name": "team_id", + "type": "text(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'MEMBER'" + }, + "joined_on": { + "name": "joined_on", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(current_timestamp)" + } + }, + "indexes": {}, + "foreignKeys": { + "user_to_team_user_id_user_id_fk": { + "name": "user_to_team_user_id_user_id_fk", + "tableFrom": "user_to_team", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_to_team_team_id_team_id_fk": { + "name": "user_to_team_team_id_team_id_fk", + "tableFrom": "user_to_team", + "tableTo": "team", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_to_team_user_id_team_id_pk": { + "columns": [ + "user_id", + "team_id" + ], + "name": "user_to_team_user_id_team_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 3395974..8c83481 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -85,6 +85,13 @@ "when": 1771399307185, "tag": "0011_robust_sabretooth", "breakpoints": true + }, + { + "idx": 12, + "version": "6", + "when": 1771710466128, + "tag": "0012_lumpy_the_anarchist", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 192479d..a1c8957 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -6,6 +6,7 @@ import { primaryKey, } from "drizzle-orm/sqlite-core"; import { nanoid } from "nanoid"; +import { db } from "."; const STANDARD_NANOID_SIZE = 12; const STANDARD_VARCHAR_LENGTH = 255; @@ -43,6 +44,9 @@ const siteRoleType = text({ enum: ["SUPER_ADMIN", "ADMIN", "USER"] }); const teamJoinRequestStatusType = text({ enum: ["PENDING", "APPROVED", "REJECTED", "RESCINDED"], }); +const dbLoggingSourceType = text({ + enum: ["SERVER", "LAMBDA", "CLIENT"], +}); // User Table - Partially generated based on Better Auth requirements. Modify with extreme caution. export const user = sqliteTable("user", { @@ -194,6 +198,7 @@ export const log = sqliteTable("log", { logType: logType.notNull(), message: standardVarcharFactory(), occurredAt: standardDateFactory(), + source:dbLoggingSourceType.notNull(), // TODO(https://github.com/acmutsa/Fallback/issues/39): All of these fields are nullable because not all logs have the same info. There might be a better approach. teamId: standardVarcharFactoryNullable(), userId: standardVarcharFactoryNullable(), From 366e24b44b014c5143ada8b7aa33920b4072f3d4 Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sat, 21 Feb 2026 14:49:11 -0700 Subject: [PATCH 2/7] Updates enum name --- packages/db/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/db/schema.ts b/packages/db/schema.ts index a1c8957..20b5f87 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -44,7 +44,7 @@ const siteRoleType = text({ enum: ["SUPER_ADMIN", "ADMIN", "USER"] }); const teamJoinRequestStatusType = text({ enum: ["PENDING", "APPROVED", "REJECTED", "RESCINDED"], }); -const dbLoggingSourceType = text({ +const loggingSourceType = text({ enum: ["SERVER", "LAMBDA", "CLIENT"], }); @@ -198,7 +198,7 @@ export const log = sqliteTable("log", { logType: logType.notNull(), message: standardVarcharFactory(), occurredAt: standardDateFactory(), - source:dbLoggingSourceType.notNull(), + source:loggingSourceType.notNull(), // TODO(https://github.com/acmutsa/Fallback/issues/39): All of these fields are nullable because not all logs have the same info. There might be a better approach. teamId: standardVarcharFactoryNullable(), userId: standardVarcharFactoryNullable(), From 2f7b9a4b10c1740fb2fd54ac55ab428cb8c42a75 Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sun, 1 Mar 2026 12:37:37 -0700 Subject: [PATCH 3/7] changes --- apps/api/src/lib/functions/database.ts | 27 ++++++++++++++++++------ apps/api/src/lib/functions/middleware.ts | 4 +--- apps/api/src/lib/types.ts | 4 +++- apps/web/src/lib/functions/auth.ts | 5 +++-- packages/shared/constants.ts | 3 ++- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/apps/api/src/lib/functions/database.ts b/apps/api/src/lib/functions/database.ts index 1201c4d..1364145 100644 --- a/apps/api/src/lib/functions/database.ts +++ b/apps/api/src/lib/functions/database.ts @@ -1,6 +1,6 @@ import { userToTeam, db, and, eq, log, team, teamJoinRequest } from "db"; import type { UserType, SiteRoleType } from "db/types"; -import type { LoggingOptions, LoggingType } from "../types"; +import type { LoggingOptions, LoggingType, LoggingSource } from "../types"; import { type Context } from "hono"; import { isInDevMode } from "."; @@ -111,33 +111,38 @@ export async function isUserSiteAdminOrQueryHasPermissions( export async function logError(message: string, c?: Context) { const options = getAllContextValues(c); - await logToDb("ERROR", message, options); + const source = getLoggingSourceFromContext(c); + await logToDb("ERROR", message, source, options); } export async function logInfo(message: string, c?: Context) { const options = getAllContextValues(c); - await logToDb("INFO", message, options); + const source = getLoggingSourceFromContext(c); + await logToDb("INFO", message, source, options); } export async function logWarning(message: string, c?: Context) { const options = getAllContextValues(c); - await logToDb("WARNING", message, options); + const source = getLoggingSourceFromContext(c); + await logToDb("WARNING", message, source, options); } export async function logToDb( - loggingType: LoggingType, + logType: LoggingType, message: string, + source: LoggingSource, options?: LoggingOptions, ) { if (isInDevMode()) { - console.log(`[${loggingType}] - ${message} - Options: `, options); + console.log(`[${logType}] - ${message} - Options: `, options); return; } try { await db.insert(log).values({ ...options, - logType: loggingType, + logType, message, + source, }); } catch (e) { // Silently fail if logging to the db fails. @@ -158,6 +163,14 @@ function getAllContextValues(c?: Context): LoggingOptions | undefined { }; } +function getLoggingSourceFromContext(c?: Context): LoggingSource { + if (!c){ + return "SERVER" + } + + return "SERVER" +} + /** * Safely extract an error code string from an unknown thrown value from a db error. * Returns the code as a string when present, otherwise null. diff --git a/apps/api/src/lib/functions/middleware.ts b/apps/api/src/lib/functions/middleware.ts index 0397b0b..15069c9 100644 --- a/apps/api/src/lib/functions/middleware.ts +++ b/apps/api/src/lib/functions/middleware.ts @@ -3,9 +3,7 @@ import { auth } from "../auth"; import { logInfo } from "./database"; import { nanoid } from "nanoid"; import type { ApiContext } from "../types"; -import { API_ERROR_MESSAGES } from "shared"; - -export const MIDDLEWARE_PUBLIC_ROUTES = ["/health", "/api/auth"]; +import { API_ERROR_MESSAGES, MIDDLEWARE_PUBLIC_ROUTES } from "shared"; export async function setUserSessionContextMiddleware(c: Context, next: Next) { const session = await auth.api.getSession({ headers: c.req.raw.headers }); diff --git a/apps/api/src/lib/types.ts b/apps/api/src/lib/types.ts index 33ef5c7..35ed34d 100644 --- a/apps/api/src/lib/types.ts +++ b/apps/api/src/lib/types.ts @@ -15,7 +15,9 @@ export type ApiContext = Context<{ export type LoggingOptions = Omit< typeof log.$inferInsert, - "id" | "occurredAt" | "logType" | "message" + "id" | "occurredAt" | "logType" | "message" | "source" >; // Single type representing the logType value (e.g. "INFO" | "WARNING" | "ERROR") export type LoggingType = (typeof log.$inferSelect)["logType"]; + +export type LoggingSource = (typeof log.$inferSelect)["source"]; diff --git a/apps/web/src/lib/functions/auth.ts b/apps/web/src/lib/functions/auth.ts index 71c242c..ec404a0 100644 --- a/apps/web/src/lib/functions/auth.ts +++ b/apps/web/src/lib/functions/auth.ts @@ -1,10 +1,11 @@ import { authClient } from "../auth-client"; -import { PUBLIC_ROUTES } from "shared/constants"; +import { WEB_PUBLIC_ROUTES } from "shared/constants"; import type { RouterContext } from "../types"; import { redirect } from "@tanstack/react-router"; export function isPublicRoute(pathname: string) { - return PUBLIC_ROUTES.includes(pathname); + console.log("checking if public route for pathname: ", pathname); + return WEB_PUBLIC_ROUTES.includes(pathname); } export function isProtectedRoute(pathname: string) { diff --git a/packages/shared/constants.ts b/packages/shared/constants.ts index 4d065f3..6c252d0 100644 --- a/packages/shared/constants.ts +++ b/packages/shared/constants.ts @@ -55,7 +55,8 @@ export const AUTH_CONFIG = { }, }; -export const PUBLIC_ROUTES = ["/", "/sign-in", "/sign-up", "/forgot-password"]; +export const WEB_PUBLIC_ROUTES = ["/", "/sign-in", "/sign-up", "/forgot-password", "/api/auth"]; +export const MIDDLEWARE_PUBLIC_ROUTES = ["/health", "/api/auth"]; export const THEME_CONFIG = { accessKey: "fallback-theme", From bf4e86636a78f76d1b583d9e1367a269249a48e3 Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sun, 1 Mar 2026 13:37:46 -0700 Subject: [PATCH 4/7] Whoops save this for another PR --- apps/api/src/lib/functions/database.ts | 2 +- apps/api/src/lib/functions/middleware.ts | 5 ++++- apps/web/src/lib/functions/auth.ts | 5 ++--- packages/db/schema.ts | 2 +- packages/shared/constants.ts | 4 +--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/api/src/lib/functions/database.ts b/apps/api/src/lib/functions/database.ts index 4044d47..e593214 100644 --- a/apps/api/src/lib/functions/database.ts +++ b/apps/api/src/lib/functions/database.ts @@ -165,7 +165,7 @@ export async function logToDb( options?: LoggingOptions, ) { if (isInDevMode()) { - console.log(`[${logType}] - ${message} - Options: `, options); + console.log(`[${logType}] from ${source} - ${message} - Options: `, options); return; } try { diff --git a/apps/api/src/lib/functions/middleware.ts b/apps/api/src/lib/functions/middleware.ts index ce60d6e..9480ecc 100644 --- a/apps/api/src/lib/functions/middleware.ts +++ b/apps/api/src/lib/functions/middleware.ts @@ -3,7 +3,10 @@ import { auth } from "../auth"; import { logInfo } from "./database"; import { nanoid } from "nanoid"; import type { ApiContext } from "../types"; -import { API_ERROR_MESSAGES, MIDDLEWARE_PUBLIC_ROUTES } from "shared"; +import { API_ERROR_MESSAGES } from "shared"; + +export const MIDDLEWARE_PUBLIC_ROUTES = ["/health", "/api/auth"]; + /** * Middleware to set user and session context for each request. This middleware checks the authentication status of the incoming request, retrieves the user session if it exists, and sets relevant information in the context for downstream handlers to use. It also logs the request path and authentication status for monitoring purposes. * @param c - The Hono context object diff --git a/apps/web/src/lib/functions/auth.ts b/apps/web/src/lib/functions/auth.ts index 95c8ebd..713c69d 100644 --- a/apps/web/src/lib/functions/auth.ts +++ b/apps/web/src/lib/functions/auth.ts @@ -1,5 +1,5 @@ import { authClient } from "../auth-client"; -import { WEB_PUBLIC_ROUTES } from "shared/constants"; +import { PUBLIC_ROUTES } from "shared/constants"; import type { RouterContext } from "../types"; import { redirect } from "@tanstack/react-router"; /** @@ -8,8 +8,7 @@ import { redirect } from "@tanstack/react-router"; * @returns True if the pathname is a public route, false otherwise */ export function isPublicRoute(pathname: string) { - console.log("checking if public route for pathname: ", pathname); - return WEB_PUBLIC_ROUTES.includes(pathname); + return PUBLIC_ROUTES.includes(pathname); } /** diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 20b5f87..33c04a3 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -45,7 +45,7 @@ const teamJoinRequestStatusType = text({ enum: ["PENDING", "APPROVED", "REJECTED", "RESCINDED"], }); const loggingSourceType = text({ - enum: ["SERVER", "LAMBDA", "CLIENT"], + enum: ["SERVER", "BACKUP_SERVICE", "CLIENT"], }); // User Table - Partially generated based on Better Auth requirements. Modify with extreme caution. diff --git a/packages/shared/constants.ts b/packages/shared/constants.ts index 6c252d0..fda462e 100644 --- a/packages/shared/constants.ts +++ b/packages/shared/constants.ts @@ -54,9 +54,7 @@ export const AUTH_CONFIG = { }, }, }; - -export const WEB_PUBLIC_ROUTES = ["/", "/sign-in", "/sign-up", "/forgot-password", "/api/auth"]; -export const MIDDLEWARE_PUBLIC_ROUTES = ["/health", "/api/auth"]; +export const PUBLIC_ROUTES = ["/", "/sign-in", "/sign-up", "/forgot-password"]; export const THEME_CONFIG = { accessKey: "fallback-theme", From 4293c232252d96b098d4df4147437772aa969cb3 Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sun, 1 Mar 2026 13:37:59 -0700 Subject: [PATCH 5/7] formatter --- apps/api/src/lib/functions/database.ts | 11 +++++++---- packages/db/schema.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/api/src/lib/functions/database.ts b/apps/api/src/lib/functions/database.ts index e593214..cc6eb24 100644 --- a/apps/api/src/lib/functions/database.ts +++ b/apps/api/src/lib/functions/database.ts @@ -165,7 +165,10 @@ export async function logToDb( options?: LoggingOptions, ) { if (isInDevMode()) { - console.log(`[${logType}] from ${source} - ${message} - Options: `, options); + console.log( + `[${logType}] from ${source} - ${message} - Options: `, + options, + ); return; } try { @@ -200,11 +203,11 @@ function getAllContextValues(c?: Context): LoggingOptions | undefined { } function getLoggingSourceFromContext(c?: Context): LoggingSource { - if (!c){ - return "SERVER" + if (!c) { + return "SERVER"; } - return "SERVER" + return "SERVER"; } /** diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 33c04a3..2e2d328 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -198,7 +198,7 @@ export const log = sqliteTable("log", { logType: logType.notNull(), message: standardVarcharFactory(), occurredAt: standardDateFactory(), - source:loggingSourceType.notNull(), + source: loggingSourceType.notNull(), // TODO(https://github.com/acmutsa/Fallback/issues/39): All of these fields are nullable because not all logs have the same info. There might be a better approach. teamId: standardVarcharFactoryNullable(), userId: standardVarcharFactoryNullable(), From b3eb0e40e4ce628cbe4171f64182aa6815b7420d Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sun, 1 Mar 2026 13:38:55 -0700 Subject: [PATCH 6/7] Forgot newline --- packages/shared/constants.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared/constants.ts b/packages/shared/constants.ts index fda462e..4d065f3 100644 --- a/packages/shared/constants.ts +++ b/packages/shared/constants.ts @@ -54,6 +54,7 @@ export const AUTH_CONFIG = { }, }, }; + export const PUBLIC_ROUTES = ["/", "/sign-in", "/sign-up", "/forgot-password"]; export const THEME_CONFIG = { From 429d431cd5e767991855c9855fd6c23803a9532f Mon Sep 17 00:00:00 2001 From: Christian Walker Date: Sun, 1 Mar 2026 13:41:45 -0700 Subject: [PATCH 7/7] Only when not drafted, run workflows --- .github/workflows/presubmits.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/presubmits.yml b/.github/workflows/presubmits.yml index fbae8f4..6e39a3d 100644 --- a/.github/workflows/presubmits.yml +++ b/.github/workflows/presubmits.yml @@ -7,6 +7,7 @@ on: [pull_request] jobs: prettier-check: runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} steps: - name: Checkout code @@ -28,6 +29,7 @@ jobs: run: pnpm run format-check builder: runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} steps: - name: Checkout code