🔨 Run once, graceful shutdown, db filename in env
- Refactored toISODateString function - Added Db filename to env - Added getOrDefault function for fetching envs, replaced LOG_LEVEL with getOrDefault - Added script to run job once then exit - Catch SIGINT signal and do a graceful shutdown
This commit is contained in:
parent
b61903f5c8
commit
4977e7ad6a
@ -4,6 +4,7 @@ ACTUAL_SYNC_ID=your-sync-id
|
||||
ACTUAL_SERVER_URL=http://your-server-url:5006
|
||||
ACTUAL_PASSWORD=your-password
|
||||
ACTUAL_ACCOUNT_IDS=your-account-id1,your-account-id2
|
||||
ACTUAL_DATA_DIR=.cache
|
||||
# Bank
|
||||
BANK_INITIAL_REFRESH_TOKEN=initial-valid-refresh-token
|
||||
BANK_OAUTH_CLIENT_ID=your-client-id
|
||||
@ -13,3 +14,4 @@ BANK_OAUTH_REDIRECT_URI=http://your-redirect-uri.com
|
||||
BANK_ACCOUNT_IDS=your-account-id1,your-account-id2
|
||||
# Configuration
|
||||
LOG_LEVEL=info# trace | error | warn | info | debug | trace
|
||||
DB_FILENAME=default
|
||||
|
12
config.ts
12
config.ts
@ -3,12 +3,14 @@ import dotenv from "dotenv"
|
||||
|
||||
dotenv.config()
|
||||
|
||||
// Actual
|
||||
export const ACTUAL_SYNC_ID = getOrThrow("ACTUAL_SYNC_ID")
|
||||
export const ACTUAL_SERVER_URL = getOrThrow("ACTUAL_SERVER_URL")
|
||||
export const ACTUAL_PASSWORD = getOrThrow("ACTUAL_PASSWORD")
|
||||
export const ACTUAL_ACCOUNT_IDS = getArrayOrThrow("ACTUAL_ACCOUNT_IDS")
|
||||
export const ACTUAL_DATA_DIR = ".cache"
|
||||
export const ACTUAL_DATA_DIR = getOrDefault("ACTUAL_DATA_DIR", ".cache")
|
||||
|
||||
// Bank
|
||||
export const BANK_INITIAL_REFRESH_TOKEN = getOrThrow(
|
||||
"BANK_INITIAL_REFRESH_TOKEN",
|
||||
)
|
||||
@ -16,6 +18,14 @@ export const BANK_OAUTH_CLIENT_ID = getOrThrow("BANK_OAUTH_CLIENT_ID")
|
||||
export const BANK_OAUTH_CLIENT_SECRET = getOrThrow("BANK_OAUTH_CLIENT_SECRET")
|
||||
export const BANK_ACCOUNT_IDS = getArrayOrThrow("BANK_ACCOUNT_IDS")
|
||||
|
||||
// Configuration
|
||||
export const DB_FILENAME = getOrDefault("DB_FILENAME", "default")
|
||||
export const LOG_LEVEL = getOrDefault("LOG_LEVEL", "info")
|
||||
|
||||
function getOrDefault(key: string, def: string): string {
|
||||
return process.env[key] || def
|
||||
}
|
||||
|
||||
function getOrThrow(key: string): string {
|
||||
const value = process.env[key]
|
||||
assert(value, `Missing environment variable: ${key}`)
|
||||
|
@ -5,6 +5,7 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "dotenvx run --env-file=.env.local -- node --import=tsx ./src/main.ts | pino-pretty",
|
||||
"start-once": "ONCE=true dotenvx run --env-file=.env.local -- node --import=tsx ./src/main.ts | pino-pretty",
|
||||
"test": "dotenvx run --env-file=.env.test.local -- node --experimental-vm-modules node_modules/jest/bin/jest.js | pino-pretty",
|
||||
"format": "prettier --write \"./**/*.{js,mjs,ts,md,json}\""
|
||||
},
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { type Dayjs } from "dayjs"
|
||||
|
||||
export function toISODateString(day: Dayjs): string {
|
||||
return `${day.year()}-${(day.month() + 1).toString().padStart(2, "0")}-${day.date().toString().padStart(2, "0")}`
|
||||
}
|
||||
export const toISODateString = (day: Dayjs): string => day.format("YYYY-MM-DD")
|
||||
|
@ -1,8 +1,9 @@
|
||||
import pino from "pino"
|
||||
import { LOG_LEVEL } from "../config.ts"
|
||||
|
||||
/**
|
||||
* / Returns a logging instance with the default log-level "info"
|
||||
*/
|
||||
export default pino({
|
||||
level: process.env.LOG_LEVEL as string || "info",
|
||||
level: LOG_LEVEL,
|
||||
})
|
||||
|
37
src/main.ts
37
src/main.ts
@ -10,6 +10,7 @@ import {
|
||||
ACTUAL_ACCOUNT_IDS,
|
||||
ACTUAL_DATA_DIR,
|
||||
BANK_ACCOUNT_IDS,
|
||||
DB_FILENAME,
|
||||
} from "../config.ts"
|
||||
import logger from "@/logger.ts"
|
||||
import type { UUID } from "node:crypto"
|
||||
@ -17,7 +18,6 @@ import { createDb } from "@/bank/db/queries.ts"
|
||||
import * as fs from "node:fs"
|
||||
|
||||
// TODO Transports api for pino https://github.com/pinojs/pino/blob/HEAD/docs/transports.md
|
||||
// TODO create .cache if missing
|
||||
|
||||
export async function daily(actual: Actual, bank: Bank): Promise<void> {
|
||||
// Fetch transactions from the bank
|
||||
@ -59,29 +59,40 @@ function createCacheDirIfMissing(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add a script to run an immediate job, without cron
|
||||
// TODO catch ^C to stop server
|
||||
async function main(): Promise<void> {
|
||||
logger.info("Starting application")
|
||||
|
||||
createCacheDirIfMissing()
|
||||
|
||||
const actual = await ActualImpl.init()
|
||||
const databaseFileName = "default.sqlite" // TODO move name to env
|
||||
const databaseFileName = `${DB_FILENAME}.sqlite`
|
||||
const db = createDb(databaseFileName)
|
||||
logger.info(`Started SQLlite database with filename="${databaseFileName}"`)
|
||||
|
||||
process.on("SIGINT", async () => {
|
||||
logger.info("Caught interrupt signal")
|
||||
await shutdown()
|
||||
})
|
||||
|
||||
if (process.env.ONCE) {
|
||||
await daily(actual, new Sparebank1Impl(db))
|
||||
await shutdown()
|
||||
return
|
||||
}
|
||||
|
||||
logger.info("Waiting for CRON job to start")
|
||||
const cronJob = cronJobDaily(async () => {
|
||||
logger.info("Running daily job")
|
||||
await daily(actual, new Sparebank1Impl(db))
|
||||
logger.info("Finished daily job")
|
||||
})
|
||||
|
||||
// cronJobDaily(async () => {
|
||||
logger.info("Running daily job")
|
||||
await daily(actual, new Sparebank1Impl(db))
|
||||
logger.info("Finished daily job")
|
||||
// })
|
||||
|
||||
logger.info("Shutting down")
|
||||
await actual.shutdown()
|
||||
db.close()
|
||||
async function shutdown(): Promise<void> {
|
||||
logger.info("Shutting down")
|
||||
await actual.shutdown()
|
||||
db.close()
|
||||
cronJob.stop()
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user