feat(backend): add DB persistence layer (#79)

* feat(backend): add DB persistence layer

Replace raw pg Pool with Drizzle ORM backed by Bun.sql.
Add per-user source configuration table (user_sources).
Migrate Better Auth to drizzle-adapter.
Add AES-256-GCM credential encryption.

Co-authored-by: Ona <no-reply@ona.com>

* fix(backend): set updatedAt explicitly in all mutations

onConflictDoUpdate bypasses Drizzle's $onUpdate hook.
Set updatedAt explicitly in all mutation methods.

Co-authored-by: Ona <no-reply@ona.com>

* fix(backend): add composite index on user_sources

Add (user_id, enabled) index for the enabled() query path.

Co-authored-by: Ona <no-reply@ona.com>

---------

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2026-03-16 01:30:02 +00:00
committed by GitHub
parent 9ac88d921c
commit 61c1ade631
29 changed files with 2034 additions and 115 deletions

View File

@@ -1,14 +1,11 @@
import type { WeatherKitClient, WeatherKitResponse } from "@aelis/source-weatherkit"
import { LocationSource } from "@aelis/source-location"
import { WeatherSource } from "@aelis/source-weatherkit"
import { describe, expect, mock, spyOn, test } from "bun:test"
import { WeatherSourceProvider } from "../weather/provider.ts"
import { UserSessionManager } from "./user-session-manager.ts"
const mockWeatherClient: WeatherKitClient = {
fetch: async () => ({}) as WeatherKitResponse,
}
const mockWeatherProvider = async () =>
new WeatherSource({ client: { fetch: async () => ({}) as never } })
describe("UserSessionManager", () => {
test("getOrCreate creates session on first call", async () => {
@@ -76,9 +73,8 @@ describe("UserSessionManager", () => {
})
test("accepts object providers", async () => {
const provider = new WeatherSourceProvider({ client: mockWeatherClient })
const manager = new UserSessionManager({
providers: [async () => new LocationSource(), provider],
providers: [async () => new LocationSource(), mockWeatherProvider],
})
const session = await manager.getOrCreate("user-1")
@@ -87,9 +83,8 @@ describe("UserSessionManager", () => {
})
test("accepts mixed providers", async () => {
const provider = new WeatherSourceProvider({ client: mockWeatherClient })
const manager = new UserSessionManager({
providers: [async () => new LocationSource(), provider],
providers: [async () => new LocationSource(), mockWeatherProvider],
})
const session = await manager.getOrCreate("user-1")