aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/lib/server/sb1.ts
diff options
context:
space:
mode:
authorivar <i@oiee.no>2025-12-26 13:18:15 +0100
committerivar <i@oiee.no>2025-12-26 13:18:15 +0100
commitfe0fe074ec8e8959bbdeff0ccc7f68d20b30e963 (patch)
tree032fcc7ee0b72899405a5046e7761d71fb029c9c /app/src/lib/server/sb1.ts
parent874e1572298531dde9bc1d3ccdb704af0a045605 (diff)
downloadsparebank1-actualbudget-fe0fe074ec8e8959bbdeff0ccc7f68d20b30e963.tar.xz
sparebank1-actualbudget-fe0fe074ec8e8959bbdeff0ccc7f68d20b30e963.zip
WIP form
Diffstat (limited to 'app/src/lib/server/sb1.ts')
-rw-r--r--app/src/lib/server/sb1.ts85
1 files changed, 71 insertions, 14 deletions
diff --git a/app/src/lib/server/sb1.ts b/app/src/lib/server/sb1.ts
index a7cad3e..f6507ef 100644
--- a/app/src/lib/server/sb1.ts
+++ b/app/src/lib/server/sb1.ts
@@ -4,6 +4,7 @@ import { Temporal } from "temporal-polyfill";
import { randomUUID } from "node:crypto";
import { db } from "./db";
import { syncSession } from "./db/schema";
+import { add_session_log } from "./session-log";
export type Sb1Tokens = {
access_token: string
@@ -14,12 +15,12 @@ export type Sb1Tokens = {
refresh_token: string
}
-export type Transaction = {
+export type Sb1Transaction = {
id: string
nonUniqueId: string
description: string
cleanedDescription: string
- accountNumber: AccountNumber
+ accountNumber: Sb1AccountNumber
amount: number
date: number
interestDate: number
@@ -34,16 +35,44 @@ export type Transaction = {
accountKey: string
accountCurrency: string
isFromCurrencyAccount: boolean
- classificationInput: ClassificationInput
+ classificationInput: Sb1ClassificationInput
}
-export type AccountNumber = {
+export type Sb1Account = {
+ key: string;
+ accountNumber: string;
+ iban: string;
+ name: string;
+ description: string;
+ balance: number;
+ availableBalance: number;
+ currencyCode: string;
+ owner: Sb1AccountOwner;
+ productType: string;
+ type: string;
+ productId: string;
+ descriptionCode: string;
+ accountProperties: { [key: string]: boolean };
+}
+
+export type Sb1AccountOwner = {
+ name: string;
+ firstName: string;
+ lastName: string;
+ type: string;
+ age: number;
+ customerKey: string;
+ ssnKey: string;
+}
+
+
+export type Sb1AccountNumber = {
value: string
formatted: string
unformatted: string
}
-export type ClassificationInput = {
+export type Sb1ClassificationInput = {
id: string
amount: number
type: string
@@ -139,12 +168,13 @@ const auth = {
const epoch = Temporal.Now.instant().epochMilliseconds
if (res.ok) {
await db.update(syncSession).set({ tokens, accessTokenCreated: epoch.toString(), refreshTokenCreated: epoch.toString() }).where(eq(syncSession.id, id))
+ await add_session_log(id, "REFRESH_SB1_TOKEN", "Done")
return tokens
} else {
console.error("Failed to refresh tokens", tokens)
+ await add_session_log(id, "REFRESH_SB1_TOKEN", "Failed: " + JSON.stringify(res))
return null
}
-
}
}
@@ -160,23 +190,50 @@ const data = {
Authorization: `Bearer ${token}`
}
})
- if (response.ok) return await response.json() as { accounts: Array<any> }
+ if (response.ok) return await response.json() as { accounts: Array<Sb1Account> }
else console.error(await response.text())
},
- async get_transactions(accountKey: string) {
+ async get_transactions(accountKey: string, delta?: Temporal.Instant) {
const token = await auth.get_access_token()
+
if (!token) return undefined
- const response = await fetch("https://api.sparebank1.no/personal/banking/transactions?" + new URLSearchParams({
- "accountKey": accountKey
- }), {
+ const params = new URLSearchParams({
+ "accountKey": accountKey,
+ });
+ if (delta) params.append("fromDate", formatInstant(delta, "yyyy-MM-dd"))
+ const response = await fetch("https://api.sparebank1.no/personal/banking/transactions?" + params, {
headers: {
Authorization: `Bearer ${token}`,
},
});
const json = await response.json()
- console.log(accountKey + ":" + json["transactions"]?.length)
- return json["transactions"] as Transaction[];
+ return json["transactions"] as Sb1Transaction[];
}
}
-export default { auth, data } \ No newline at end of file
+export default { auth, data }
+
+export function formatInstant(
+ instant: Temporal.Instant,
+ format: string,
+ timeZone: string = "UTC"
+): string {
+ const zdt = instant.toZonedDateTimeISO(timeZone);
+
+ const pad = (value: number, length = 2) =>
+ value.toString().padStart(length, "0");
+
+ const replacements: Record<string, string> = {
+ yyyy: pad(zdt.year, 4),
+ MM: pad(zdt.month),
+ dd: pad(zdt.day),
+ HH: pad(zdt.hour),
+ mm: pad(zdt.minute),
+ ss: pad(zdt.second),
+ };
+
+ return format.replace(
+ /yyyy|MM|dd|HH|mm|ss/g,
+ (token) => replacements[token]
+ );
+}