aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
authorivar <i@oiee.no>2025-12-15 00:40:21 +0100
committerivar <i@oiee.no>2025-12-15 00:40:21 +0100
commit2b5e4c4aa372632b87a404027cf53d5a7bfc3808 (patch)
tree4782fec17e6c3745fb43e209dfe80e33340d37c0 /app/src
parent008862f8a2431c8f755a38a0ef242b8faf125057 (diff)
downloadsparebank1-actualbudget-2b5e4c4aa372632b87a404027cf53d5a7bfc3808.tar.xz
sparebank1-actualbudget-2b5e4c4aa372632b87a404027cf53d5a7bfc3808.zip
Progress
Diffstat (limited to 'app/src')
-rw-r--r--app/src/lib/server/actual.ts21
-rw-r--r--app/src/lib/server/sb1.ts31
-rw-r--r--app/src/lib/ui/button.svelte1
-rw-r--r--app/src/routes/+page.server.ts14
-rw-r--r--app/src/routes/+page.svelte41
-rw-r--r--app/src/routes/actual.remote.ts4
-rw-r--r--app/src/routes/sb1.remote.ts16
7 files changed, 83 insertions, 45 deletions
diff --git a/app/src/lib/server/actual.ts b/app/src/lib/server/actual.ts
new file mode 100644
index 0000000..7291aad
--- /dev/null
+++ b/app/src/lib/server/actual.ts
@@ -0,0 +1,21 @@
+import { ACTUAL_HOST, ACTUAL_PASS } from "$env/static/private";
+import * as actual from "@actual-app/api"
+import { existsSync, mkdirSync } from "node:fs";
+import path from "node:path";
+
+async function init_actual() {
+ const dataDir = path.resolve(import.meta.dirname, "actualDataDir");
+
+ if (!existsSync(dataDir)) mkdirSync(dataDir);
+
+ return actual.init({
+ password: ACTUAL_PASS,
+ serverURL: ACTUAL_HOST,
+ dataDir: dataDir
+ })
+}
+
+export async function get_budgets() {
+ await init_actual()
+ return await actual.getBudgets()
+} \ No newline at end of file
diff --git a/app/src/lib/server/sb1.ts b/app/src/lib/server/sb1.ts
index c060af5..1d6b869 100644
--- a/app/src/lib/server/sb1.ts
+++ b/app/src/lib/server/sb1.ts
@@ -24,12 +24,7 @@ type Transaction = {
const auth = {
async is_ready(): Promise<boolean> {
const token = await this.get_access_token()
- const ping = await fetch("https://developer.sparebank1.no/helloworld/ping", {
- headers: {
- "Authorization": "Bearer " + token
- }
- })
- return ping.ok
+ return token !== ""
},
async get_auth_info() {
const entity = await db.select({
@@ -39,7 +34,8 @@ const auth = {
}).from(syncSession)
if (!entity[0]) return undefined
const { tokens, accessTokenCreated, refreshTokenCreated } = entity[0]
- const tokensParsed = JSON.parse(tokens ?? "")
+ 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 })
@@ -56,22 +52,20 @@ const auth = {
})
const authorizeUrl = new URL("https://api.sparebank1.no/oauth/authorize");
-
authorizeUrl.searchParams.set("client_id", SB1_ID);
authorizeUrl.searchParams.set("state", state);
authorizeUrl.searchParams.set("redirect_uri", SB1_REDIRECT_URI);
authorizeUrl.searchParams.set("finInst", SB1_FIN_INST);
authorizeUrl.searchParams.set("response_type", "code");
-
return authorizeUrl.toString()
},
async get_access_token() {
const entity = await db.select({
tokens: syncSession.tokens
}).from(syncSession)
- const { tokens } = entity[0]
- if (!tokens) return null
- const parsed = JSON.parse(tokens) as Sb1Tokens
+ const res = entity[0]
+ if (!res?.tokens) return null
+ const parsed = JSON.parse(res.tokens) as Sb1Tokens
return parsed.access_token as string
},
async refresh_tokem() {
@@ -127,15 +121,20 @@ const data = {
async get_transactions(accountKey: string) {
const token = await auth.get_access_token()
if (token) return undefined
- const url = new URL(
- "https://api.sparebank1.no/personal/banking/transactions",
- );
+ const url = new URL("https://api.sparebank1.no/personal/banking/transactions/transactions")
+ console.log(token)
url.searchParams.set("accountKey", accountKey);
- const response = await fetch(url, {
+ console.log(accountKey)
+ const response = await fetch("https://api.sparebank1.no/personal/banking/transactions" + new URLSearchParams({
+ "accountKey": accountKey,
+ "fromDate": "",
+ "toDate": ""
+ }), {
headers: {
Authorization: `Bearer ${token}`,
},
});
+ const json = await response.json()
return (await response.json())["transactions"] as Transaction[];
}
diff --git a/app/src/lib/ui/button.svelte b/app/src/lib/ui/button.svelte
index 313cd90..8c66bd2 100644
--- a/app/src/lib/ui/button.svelte
+++ b/app/src/lib/ui/button.svelte
@@ -26,6 +26,7 @@
display: flex;
gap: 3px;
transition: 0.1s all ease;
+ height: fit-content;
&:hover,
&:focus {
diff --git a/app/src/routes/+page.server.ts b/app/src/routes/+page.server.ts
new file mode 100644
index 0000000..0d6016b
--- /dev/null
+++ b/app/src/routes/+page.server.ts
@@ -0,0 +1,14 @@
+import type { PageServerLoad } from './$types';
+import { get_budgets } from '$lib/server/actual';
+import sb1 from "$lib/server/sb1"
+
+export const load = (async () => {
+ return {
+ actual: {
+ meta: await get_budgets()
+ },
+ sb1: {
+ accounts: (await sb1.data.get_accounts())?.accounts
+ }
+ };
+}) satisfies PageServerLoad; \ No newline at end of file
diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte
index e89df9b..26db3e4 100644
--- a/app/src/routes/+page.svelte
+++ b/app/src/routes/+page.svelte
@@ -1,10 +1,10 @@
<script lang="ts">
import Button from "$lib/ui/button.svelte";
- import { onMount } from "svelte";
- import { clear_auth_session, get_accounts, get_transactions, init_auth_session, is_ready } from "./sb1.remote";
- import { get_actual_meta } from "./actual.remote";
+ import { clear_auth_session, get_transactions, init_auth_session } from "./sb1.remote";
+ import type { PageProps } from "./$types";
let navigating = $state(false);
+ let { data }: PageProps = $props();
async function authorize() {
navigating = true;
@@ -16,23 +16,16 @@
async function logout() {
await clear_auth_session();
}
-
- onMount(async () => {
- await get_actual_meta();
- });
</script>
<main>
- {#if await is_ready()}
- {@const accounts = await get_accounts()}
- {@const actual_meta = await get_actual_meta()}
- {#if accounts}
- {#each accounts?.accounts as account}
- {@const transactions = await get_transactions(account.key)}
+ {#if data.sb1.accounts?.length}
+ <ul>
+ {#each data.sb1.accounts as account}
<li>{account.name}</li>
- {#if transactions?.length}
+ {#if (await get_transactions(account.key))?.length}
<ul>
- {#each transactions as transaction}
+ {#each await get_transactions(account.key) as transaction}
<li>{JSON.stringify(transaction)}</li>
{/each}
</ul>
@@ -40,12 +33,22 @@
<small>Ingen transaksjoner</small>
{/if}
{/each}
- {/if}
- {#if actual_meta}
- <pre>{JSON.stringify(actual_meta, null, 2)}</pre>
- {/if}
+ </ul>
<Button onclick={logout}>Logg ut</Button>
{:else}
<Button onclick={authorize} loading={navigating}>Autentisér hos Sparebanken 1</Button>
{/if}
+
+ {#if data.actual.meta}
+ <pre>{JSON.stringify(data.actual.meta, null, 2)}</pre>
+ {/if}
</main>
+
+<style>
+ main {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ height: 90vh;
+ }
+</style>
diff --git a/app/src/routes/actual.remote.ts b/app/src/routes/actual.remote.ts
index 4bd70b4..9560d4d 100644
--- a/app/src/routes/actual.remote.ts
+++ b/app/src/routes/actual.remote.ts
@@ -5,7 +5,7 @@ import { existsSync, mkdirSync } from "node:fs";
import path from "node:path";
async function init_actual() {
- const dataDir = path.resolve(__dirname, "actualDataDir");
+ const dataDir = path.resolve(import.meta.dirname, "actualDataDir");
if (!existsSync(dataDir)) mkdirSync(dataDir);
@@ -19,4 +19,4 @@ async function init_actual() {
export const get_actual_meta = query(async () => {
await init_actual()
return await actual.getBudgets()
-}) \ No newline at end of file
+})
diff --git a/app/src/routes/sb1.remote.ts b/app/src/routes/sb1.remote.ts
index d2eb0cc..c3967c1 100644
--- a/app/src/routes/sb1.remote.ts
+++ b/app/src/routes/sb1.remote.ts
@@ -8,24 +8,24 @@ const init_auth_session = command(async () => {
return await sb1.auth.init_auth_session()
})
-const is_ready = query(async () => {
- return await sb1.auth.is_ready()
+const is_ready = query(() => {
+ return sb1.auth.is_ready()
})
-const get_accounts = query(async () => {
- return await sb1.data.get_accounts()
+const get_accounts = query(() => {
+ return sb1.data.get_accounts()
})
-const get_transactions = query(v.string(), async (accountKey: string) => {
- return await sb1.data.get_transactions(accountKey)
+const get_transactions = query(v.string(), (accountKey: string) => {
+ return sb1.data.get_transactions(accountKey)
})
const clear_auth_session = query(async () => {
await db.delete(syncSession)
})
-const get_auth_info = query(async () => {
- return await sb1.auth.get_auth_info()
+const get_auth_info = query(() => {
+ return sb1.auth.get_auth_info()
})
const refresh_tokem = command(async () => {