Add support for simple web server

This commit is contained in:
2025-04-04 15:40:50 +02:00
parent d5d1218f5c
commit 900145a4d0
9 changed files with 1410 additions and 197 deletions

View File

@@ -1,55 +1,64 @@
import Papa from "papaparse";
import fs from "fs";
import iconv from "iconv-lite";
import { createParser } from "@/parser";
import { Actual } from "@/server";
import { Actual, ActualImportResult } from "@/backend";
import { Config } from "@/types/config";
import { Readable } from "stream";
import { Transaction } from "@/types/transaction";
export type ImportResult = {
transactions: Transaction[];
result: ActualImportResult;
skipped: string[][];
};
export function loadTransactions(file: string, profile: string, server: string, config: Config, dryRun?: boolean) {
const profileConfig = config.profiles[profile];
if (!profileConfig) {
throw new Error(`Unknown profile: ${profile}`);
}
export async function importTransactions(stream: Readable, profile: string, server: string, config: Config, dryRun?: boolean): Promise<ImportResult> {
return new Promise((resolve, reject) => {
const profileConfig = config.profiles[profile];
const serverConfig = config.servers[server];
if(!serverConfig) {
throw new Error(`Unknown server: ${server}`);
}
if (!profileConfig) {
throw new Error(`Unknown profile: ${profile}`);
}
const parser = createParser(profileConfig, serverConfig);
const serverConfig = config.servers[server];
if(!serverConfig) {
throw new Error(`Unknown server: ${server}`);
}
const actualServer = new Actual(serverConfig, dryRun);
const skipped: string[] = [];
const parser = createParser(profileConfig, serverConfig);
const handleRow = async (data: string[]) => {
const pushed = await parser.pushTransaction(data);
const actualServer = new Actual(serverConfig, dryRun);
const skipped: string[][] = [];
const handleRow = async (data: string[]) => {
const pushed = await parser.pushTransaction(data);
if (!pushed) {
skipped.push(data);
}
};
const handleClose = async () => {
try {
const transactions = await parser.reconcile();
const result = await actualServer.submit(transactions);
resolve({
transactions,
result,
skipped
});
} catch (e) {
console.error(e);
reject(e);
}
};
if (!pushed) {
skipped.push(`Skipped ==> ${data.join(" ::: ")}`);
}
};
const handleClose = async () => {
try {
const transactions = await parser.reconcile();
const result = await actualServer.submit(transactions);
const now = new Date();
const date = `${now.getFullYear()}-${(now.getMonth()+1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')}`;
const time = `${now.getHours().toString().padStart(2, '0')}-${now.getMinutes().toString().padStart(2, '0')}-${now.getSeconds().toString().padStart(2, '0')}`
const filename = `${serverConfig.data}/import.${profile}.${server}.${date}T${time}.json`.replaceAll(/\/+/g, "/");
const logContent = `${JSON.stringify(result, undefined, 2)}\n\n\n\n${skipped.join("\n")}`
fs.writeFileSync(filename, logContent);
console.log(`Detailed output written to ${filename} file.`);
} catch (e) {
console.error(e);
}
};
fs.createReadStream(file)
.pipe(iconv.decodeStream(profileConfig.encoding ?? "utf8"))
.pipe(Papa.parse(Papa.NODE_STREAM_INPUT))
.on('data', handleRow)
.on('close', handleClose);
}
stream
.pipe(iconv.decodeStream(profileConfig.encoding ?? "utf8"))
.pipe(Papa.parse(Papa.NODE_STREAM_INPUT))
.on('data', handleRow)
.on('close', handleClose);
});
};