fix: OIDC auth flow, Vite proxy, and PostgreSQL query compat

- Add auth redirect in root layout for unauthenticated users
- Proxy OIDC routes (/login, /callback, /logout) through Vite dev server
- Strip Secure flag from OIDC cookies in dev mode (HTTP localhost)
- Disable retry on auth query to prevent stale cookie loops
- Fix SQLite .get()/.all()/.run() calls in category and global-item
  services for PostgreSQL compatibility
- Add userId scoping to category service functions
- Add OIDC error logging in auth middleware
- Apply linter auto-formatting across affected files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-05 18:25:31 +02:00
parent f7588827b1
commit 574a12e6fa
32 changed files with 315 additions and 253 deletions

View File

@@ -30,11 +30,7 @@ function insertGlobalItem(db: TestDb, brand: string, model: string) {
}
function insertItem(db: TestDb, name: string) {
return db
.insert(items)
.values({ name, categoryId: 1 })
.returning()
.get();
return db.insert(items).values({ name, categoryId: 1 }).returning().get();
}
describe("Global Item Routes", () => {

View File

@@ -16,7 +16,10 @@ mock.module("../../src/server/services/storage.service", () => ({
// Also mock image service for from-url test
const mockFetchImageFromUrl = mock(() =>
Promise.resolve({ filename: "test.png", sourceUrl: "https://example.com/img.png" }),
Promise.resolve({
filename: "test.png",
sourceUrl: "https://example.com/img.png",
}),
);
mock.module("../../src/server/services/image.service", () => ({
fetchImageFromUrl: mockFetchImageFromUrl,

View File

@@ -45,7 +45,10 @@ describe("OAuth Routes", () => {
userId = testApp.userId;
mockGetAuth.mockReset();
// Default: user is authenticated via OIDC
mockGetAuth.mockReturnValue({ sub: "test-user-logto-sub", email: "admin@example.com" });
mockGetAuth.mockReturnValue({
sub: "test-user-logto-sub",
email: "admin@example.com",
});
});
describe("GET /.well-known/oauth-authorization-server", () => {

View File

@@ -1,15 +1,17 @@
import { beforeEach, describe, expect, it } from "bun:test";
import { zValidator } from "@hono/zod-validator";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
import * as schema from "../../src/db/schema.ts";
import { updateProfileSchema } from "../../src/shared/schemas.ts";
import { parseId } from "../../src/server/lib/params.ts";
import { profileRoutes } from "../../src/server/routes/profiles.ts";
import { setupRoutes } from "../../src/server/routes/setups.ts";
import { getPublicSetupWithItems } from "../../src/server/services/profile.service.ts";
import { updateProfile } from "../../src/server/services/profile.service.ts";
import {
getPublicSetupWithItems,
updateProfile,
} from "../../src/server/services/profile.service.ts";
import { updateProfileSchema } from "../../src/shared/schemas.ts";
import { createTestDb } from "../helpers/db.ts";
import { zValidator } from "@hono/zod-validator";
import { parseId } from "../../src/server/lib/params.ts";
type Db = Awaited<ReturnType<typeof createTestDb>>["db"];
@@ -112,12 +114,10 @@ describe("Profile Routes", () => {
it("includes only public setups", async () => {
// Create public and private setups
await db
.insert(schema.setups)
.values([
{ name: "Public Setup", userId, isPublic: true },
{ name: "Private Setup", userId, isPublic: false },
]);
await db.insert(schema.setups).values([
{ name: "Public Setup", userId, isPublic: true },
{ name: "Private Setup", userId, isPublic: false },
]);
const res = await app.request(`/api/users/${userId}/profile`);
expect(res.status).toBe(200);