- Moved database to subdir specified by env - Default data dir /data - Move loggers to cronJob onTick - Added volume for data dir in Docker compose - Create any missing dir specified by env
This commit is contained in:
parent
aaa85e99cb
commit
9850017e3a
@ -14,4 +14,5 @@ BANK_OAUTH_REDIRECT_URI=http://your-redirect-uri.com
|
|||||||
BANK_ACCOUNT_IDS=your-account-id1,your-account-id2
|
BANK_ACCOUNT_IDS=your-account-id1,your-account-id2
|
||||||
# Configuration
|
# Configuration
|
||||||
LOG_LEVEL=info# trace | error | warn | info | debug | trace
|
LOG_LEVEL=info# trace | error | warn | info | debug | trace
|
||||||
|
DB_DIRECTORY=data# Relative path, must not start or end with /
|
||||||
DB_FILENAME=default
|
DB_FILENAME=default
|
||||||
|
@ -2,7 +2,7 @@ name: Deploy application
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
@ -25,6 +25,7 @@ jobs:
|
|||||||
BANK_ACCOUNT_IDS: ${{ secrets.BANK_ACCOUNT_IDS }}
|
BANK_ACCOUNT_IDS: ${{ secrets.BANK_ACCOUNT_IDS }}
|
||||||
# Configuration
|
# Configuration
|
||||||
LOG_LEVEL: ${{ var.LOG_LEVEL }}
|
LOG_LEVEL: ${{ var.LOG_LEVEL }}
|
||||||
|
DB_DIRECTORY: ${{ var.DB_DIRECTORY }}
|
||||||
DB_FILENAME: ${{ var.DB_FILENAME }}
|
DB_FILENAME: ${{ var.DB_FILENAME }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -19,6 +19,7 @@ export const BANK_OAUTH_CLIENT_SECRET = getOrThrow("BANK_OAUTH_CLIENT_SECRET")
|
|||||||
export const BANK_ACCOUNT_IDS = getArrayOrThrow("BANK_ACCOUNT_IDS")
|
export const BANK_ACCOUNT_IDS = getArrayOrThrow("BANK_ACCOUNT_IDS")
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
|
export const DB_DIRECTORY = getOrDefault("DB_DIRECTORY", "data")
|
||||||
export const DB_FILENAME = getOrDefault("DB_FILENAME", "default")
|
export const DB_FILENAME = getOrDefault("DB_FILENAME", "default")
|
||||||
export const LOG_LEVEL = getOrDefault("LOG_LEVEL", "info")
|
export const LOG_LEVEL = getOrDefault("LOG_LEVEL", "info")
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
# TODO Database in storage
|
|
||||||
server:
|
server:
|
||||||
container_name: actual_budget_sparebank1
|
container_name: actual_sparebank1_cronjob
|
||||||
restart: no # TODO unless-stopped
|
restart: unless-stopped
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
environment:
|
environment:
|
||||||
@ -19,4 +18,10 @@ services:
|
|||||||
- BANK_OAUTH_REDIRECT_URI
|
- BANK_OAUTH_REDIRECT_URI
|
||||||
- BANK_ACCOUNT_IDS
|
- BANK_ACCOUNT_IDS
|
||||||
- LOG_LEVEL
|
- LOG_LEVEL
|
||||||
|
- DB_DIRECTORY # Required for Docker Compose
|
||||||
- DB_FILENAME
|
- DB_FILENAME
|
||||||
|
volumes:
|
||||||
|
- data:/${DB_DIRECTORY}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
@ -17,8 +17,8 @@ export type TokenResponseRaw = {
|
|||||||
|
|
||||||
export type TokenKey = "access-token" | "refresh-token"
|
export type TokenKey = "access-token" | "refresh-token"
|
||||||
|
|
||||||
export function createDb(filename: string) {
|
export function createDb(filepath: string) {
|
||||||
const db = new Database(filename)
|
const db = new Database(filepath)
|
||||||
db.pragma("journal_mode = WAL")
|
db.pragma("journal_mode = WAL")
|
||||||
db.exec(
|
db.exec(
|
||||||
"CREATE TABLE IF NOT EXISTS tokens ('key' VARCHAR PRIMARY KEY, token VARCHAR NOT NULL, expires_at DATETIME NOT NULL)",
|
"CREATE TABLE IF NOT EXISTS tokens ('key' VARCHAR PRIMARY KEY, token VARCHAR NOT NULL, expires_at DATETIME NOT NULL)",
|
||||||
|
@ -93,6 +93,7 @@ export class Sparebank1Impl implements Sparebank1 {
|
|||||||
async transactionsPastDay(
|
async transactionsPastDay(
|
||||||
...accountKeys: ReadonlyArray<string>
|
...accountKeys: ReadonlyArray<string>
|
||||||
): Promise<TransactionResponse> {
|
): Promise<TransactionResponse> {
|
||||||
|
// TODO API is inclusive, today should equal lastDay
|
||||||
const today = dayjs()
|
const today = dayjs()
|
||||||
const lastDay = today.subtract(1, "day")
|
const lastDay = today.subtract(1, "day")
|
||||||
return await Api.transactions(await this.getAccessToken(), accountKeys, {
|
return await Api.transactions(await this.getAccessToken(), accountKeys, {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CronJob } from "cron"
|
import { CronJob } from "cron"
|
||||||
|
import logger from "@/logger.ts"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a function every day at 1 AM, Oslo time.
|
* Run a function every day at 1 AM, Oslo time.
|
||||||
@ -8,7 +9,11 @@ import { CronJob } from "cron"
|
|||||||
export function cronJobDaily(onTick: () => Promise<void>): CronJob {
|
export function cronJobDaily(onTick: () => Promise<void>): CronJob {
|
||||||
return CronJob.from({
|
return CronJob.from({
|
||||||
cronTime: "0 0 1 * * *",
|
cronTime: "0 0 1 * * *",
|
||||||
onTick,
|
onTick: async () => {
|
||||||
|
logger.info("Starting daily job")
|
||||||
|
await onTick()
|
||||||
|
logger.info("Finished daily job")
|
||||||
|
},
|
||||||
start: true,
|
start: true,
|
||||||
timeZone: "Europe/Oslo",
|
timeZone: "Europe/Oslo",
|
||||||
})
|
})
|
||||||
|
29
src/main.ts
29
src/main.ts
@ -10,6 +10,7 @@ import {
|
|||||||
ACTUAL_ACCOUNT_IDS,
|
ACTUAL_ACCOUNT_IDS,
|
||||||
ACTUAL_DATA_DIR,
|
ACTUAL_DATA_DIR,
|
||||||
BANK_ACCOUNT_IDS,
|
BANK_ACCOUNT_IDS,
|
||||||
|
DB_DIRECTORY,
|
||||||
DB_FILENAME,
|
DB_FILENAME,
|
||||||
} from "../config.ts"
|
} from "../config.ts"
|
||||||
import logger from "@/logger.ts"
|
import logger from "@/logger.ts"
|
||||||
@ -19,10 +20,9 @@ import * as fs from "node:fs"
|
|||||||
import { CronJob } from "cron"
|
import { CronJob } from "cron"
|
||||||
|
|
||||||
// TODO Transports api for pino https://github.com/pinojs/pino/blob/HEAD/docs/transports.md
|
// TODO Transports api for pino https://github.com/pinojs/pino/blob/HEAD/docs/transports.md
|
||||||
// TODO create Dockerfile and docker-compose.yml
|
|
||||||
// TODO Gitea workflow
|
|
||||||
// TODO move tsx to devDependency. Requires ts support for Node with support for @ alias
|
// TODO move tsx to devDependency. Requires ts support for Node with support for @ alias
|
||||||
// TODO global exception handler, log and graceful shutdown
|
// TODO global exception handler, log and graceful shutdown
|
||||||
|
// TODO verbatimSyntax in tsconfig, conflicts with jest
|
||||||
|
|
||||||
export async function daily(actual: Actual, bank: Bank): Promise<void> {
|
export async function daily(actual: Actual, bank: Bank): Promise<void> {
|
||||||
// Fetch transactions from the bank
|
// Fetch transactions from the bank
|
||||||
@ -60,22 +60,23 @@ async function fetchTransactionsFromPastDay(
|
|||||||
return response.transactions
|
return response.transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
function createCacheDirIfMissing(): void {
|
function createDirIfMissing(directory: string): void {
|
||||||
if (!fs.existsSync(ACTUAL_DATA_DIR)) {
|
if (!fs.existsSync(directory)) {
|
||||||
logger.info(`Missing '${ACTUAL_DATA_DIR}', creating...`)
|
logger.info(`Missing '${directory}', creating...`)
|
||||||
fs.mkdirSync(ACTUAL_DATA_DIR)
|
fs.mkdirSync(directory, { recursive: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
logger.info("Starting application")
|
logger.info("Starting application")
|
||||||
|
|
||||||
createCacheDirIfMissing()
|
createDirIfMissing(ACTUAL_DATA_DIR)
|
||||||
|
createDirIfMissing(DB_DIRECTORY)
|
||||||
|
|
||||||
const actual = await ActualImpl.init()
|
const actual = await ActualImpl.init()
|
||||||
const databaseFileName = `${DB_FILENAME}.sqlite`
|
const databaseFilePath = `${DB_DIRECTORY}/${DB_FILENAME}.sqlite`
|
||||||
const db = createDb(databaseFileName)
|
const db = createDb(databaseFilePath)
|
||||||
logger.info(`Started SQLlite database with filename="${databaseFileName}"`)
|
logger.info(`Started Sqlite database at '${databaseFilePath}'`)
|
||||||
const bank = new Sparebank1Impl(db)
|
const bank = new Sparebank1Impl(db)
|
||||||
|
|
||||||
process.on("SIGINT", async () => {
|
process.on("SIGINT", async () => {
|
||||||
@ -91,14 +92,10 @@ async function main(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Waiting for CRON job to start")
|
logger.info("Waiting for CRON job to start")
|
||||||
cronJob = cronJobDaily(async () => {
|
cronJob = cronJobDaily(async () => await daily(actual, bank))
|
||||||
logger.info("Running daily job")
|
|
||||||
await daily(actual, bank)
|
|
||||||
logger.info("Finished daily job")
|
|
||||||
})
|
|
||||||
|
|
||||||
async function shutdown(): Promise<void> {
|
async function shutdown(): Promise<void> {
|
||||||
logger.info("Shutting down")
|
logger.info("Shutting down, Bye!")
|
||||||
await actual.shutdown()
|
await actual.shutdown()
|
||||||
db.close()
|
db.close()
|
||||||
cronJob?.stop()
|
cronJob?.stop()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user