aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
authorivar <i@oiee.no>2025-12-19 22:00:06 +0100
committerivar <i@oiee.no>2025-12-19 22:00:06 +0100
commit874e1572298531dde9bc1d3ccdb704af0a045605 (patch)
tree2dd684797894eed12fa2cdcc2216f102e1515813 /app/src
parent6c65d3947e56530e7ec3976e2955b027b7abe679 (diff)
downloadsparebank1-actualbudget-874e1572298531dde9bc1d3ccdb704af0a045605.tar.xz
sparebank1-actualbudget-874e1572298531dde9bc1d3ccdb704af0a045605.zip
Migrate schema to pg
Diffstat (limited to 'app/src')
-rw-r--r--app/src/lib/server/db/index.ts3
-rw-r--r--app/src/lib/server/db/schema.ts12
-rw-r--r--app/src/lib/server/sb1.ts36
-rw-r--r--app/src/lib/ui/button.svelte3
4 files changed, 23 insertions, 31 deletions
diff --git a/app/src/lib/server/db/index.ts b/app/src/lib/server/db/index.ts
index 3168d01..e477388 100644
--- a/app/src/lib/server/db/index.ts
+++ b/app/src/lib/server/db/index.ts
@@ -1,9 +1,6 @@
import { drizzle } from 'drizzle-orm/node-postgres';
-import Database from 'better-sqlite3';
import { env } from '$env/dynamic/private';
if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
-const client = new Database(env.DATABASE_URL);
-
export const db = drizzle(env.DATABASE_URL); \ No newline at end of file
diff --git a/app/src/lib/server/db/schema.ts b/app/src/lib/server/db/schema.ts
index 150d970..bb57703 100644
--- a/app/src/lib/server/db/schema.ts
+++ b/app/src/lib/server/db/schema.ts
@@ -1,17 +1,17 @@
-import { relations } from 'drizzle-orm';
-import { numeric, text, pgTable } from "drizzle-orm/pg-core";
-
+import { relations, sql } from 'drizzle-orm';
+import { numeric, text, pgTable, uuid, json } from "drizzle-orm/pg-core";
+import type { Sb1Tokens } from '../sb1';
export const syncSession = pgTable("session", {
- id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
+ id: uuid('id').primaryKey().default(sql`uuidv7()`),
authzState: text("authzState"),
accessTokenCreated: numeric("accessTokenCreated"),
refreshTokenCreated: numeric("refreshTokenCreated"),
- tokens: text("tokens")
+ tokens: json("tokens").$type<Sb1Tokens>()
})
export const syncLog = pgTable("session_log", {
- id: text("id").primaryKey(),
+ id: uuid('id').primaryKey().default(sql`uuidv7()`),
sessionId: text("session_id"),
dateTime: text("date_time"),
msg: text("msg")
diff --git a/app/src/lib/server/sb1.ts b/app/src/lib/server/sb1.ts
index b456609..a7cad3e 100644
--- a/app/src/lib/server/sb1.ts
+++ b/app/src/lib/server/sb1.ts
@@ -5,7 +5,7 @@ import { randomUUID } from "node:crypto";
import { db } from "./db";
import { syncSession } from "./db/schema";
-type Sb1Tokens = {
+export type Sb1Tokens = {
access_token: string
expires_in: number
refresh_token_expires_in: number
@@ -65,10 +65,8 @@ const auth = {
if (!entity[0]) return undefined
const { tokens, accessTokenCreated, refreshTokenCreated } = entity[0]
if (!tokens) return undefined
- const tokensParsed = JSON.parse(tokens)
- if (!tokensParsed) return undefined
- const refreshTokenExpires = Temporal.Instant.fromEpochMilliseconds(Number(refreshTokenCreated)).add({ seconds: tokensParsed?.refresh_token_expires_in })
- const accessTokenExpires = Temporal.Instant.fromEpochMilliseconds(Number(accessTokenCreated)).add({ seconds: tokensParsed?.expires_in })
+ const refreshTokenExpires = Temporal.Instant.fromEpochMilliseconds(Number(refreshTokenCreated)).add({ seconds: tokens.refresh_token_expires_in })
+ const accessTokenExpires = Temporal.Instant.fromEpochMilliseconds(Number(accessTokenCreated)).add({ seconds: tokens.expires_in })
return {
refreshTokenExpires,
accessTokenExpires
@@ -95,15 +93,16 @@ const auth = {
refreshTokenCreated: syncSession.refreshTokenCreated,
accessTokenCreated: syncSession.accessTokenCreated
}).from(syncSession)
- const { tokens: _tokens, accessTokenCreated, refreshTokenCreated } = result[0]
- let tokens = JSON.parse(_tokens ?? "") as Sb1Tokens
+ if (!result[0]) return undefined
+ const { tokens, accessTokenCreated, refreshTokenCreated } = result[0]
+ if (!tokens) return undefined
const nowInstant = Temporal.Now.instant()
- const accessTokenExpiredInstant = Temporal.Instant.fromEpochMilliseconds(accessTokenCreated ?? 0).add({ seconds: tokens.expires_in })
+ const accessTokenExpiredInstant = Temporal.Instant.fromEpochMilliseconds(Number(accessTokenCreated)).add({ seconds: tokens.expires_in })
if (Temporal.Instant.compare(nowInstant, accessTokenExpiredInstant) >= 0) {
const refreshedTokens = await this.refresh_token()
if (refreshedTokens) return refreshedTokens.access_token
}
- const refreshTokenExpiredInstant = Temporal.Instant.fromEpochMilliseconds(refreshTokenCreated ?? 0).add({ seconds: tokens.refresh_token_expires_in })
+ const refreshTokenExpiredInstant = Temporal.Instant.fromEpochMilliseconds(Number(refreshTokenCreated)).add({ seconds: tokens.refresh_token_expires_in })
if (Temporal.Instant.compare(nowInstant, refreshTokenExpiredInstant) >= 0) {
return undefined
}
@@ -117,19 +116,16 @@ const auth = {
id: syncSession.id
}).from(syncSession)
- const { tokens, id } = entity[0]
-
- if (!tokens) return null
-
- const parsed = JSON.parse(tokens) as Sb1Tokens
+ const { tokens: currentTokens, id } = entity[0]
- if (!parsed.refresh_token) throw new Error("No refresh token");
+ if (!currentTokens) return null
+ if (!currentTokens.refresh_token) throw new Error("No refresh token");
const params = new URLSearchParams();
params.set("client_id", SB1_ID);
params.set("client_secret", SB1_SECRET);
- params.set("refresh_token", parsed.refresh_token);
+ params.set("refresh_token", currentTokens.refresh_token);
params.set("grant_type", "refresh_token");
const res = await fetch("https://api.sparebank1.no/oauth/token", {
@@ -139,13 +135,13 @@ const auth = {
method: "POST",
body: params,
});
- const text = await res.text()
+ const tokens = await res.json() as Sb1Tokens
const epoch = Temporal.Now.instant().epochMilliseconds
if (res.ok) {
- await db.update(syncSession).set({ tokens: text, accessTokenCreated: epoch, refreshTokenCreated: epoch }).where(eq(syncSession.id, id))
- return JSON.parse(text) as Sb1Tokens
+ await db.update(syncSession).set({ tokens, accessTokenCreated: epoch.toString(), refreshTokenCreated: epoch.toString() }).where(eq(syncSession.id, id))
+ return tokens
} else {
- console.error("Failed to refresh tokens", text)
+ console.error("Failed to refresh tokens", tokens)
return null
}
diff --git a/app/src/lib/ui/button.svelte b/app/src/lib/ui/button.svelte
index 8c66bd2..37b1c97 100644
--- a/app/src/lib/ui/button.svelte
+++ b/app/src/lib/ui/button.svelte
@@ -1,6 +1,5 @@
<script lang="ts">
import type { HTMLButtonAttributes } from "svelte/elements";
- import { Spinner } from "phosphor-svelte";
let { children, loading, type = "button", ...restProps }: Props = $props();
type Props = {
@@ -11,7 +10,7 @@
<button {...restProps} {type}>
{@render children?.()}
{#if loading}
- <Spinner />
+ ...
{/if}
</button>