// Expects a JSON config file to be passed in the env variable CONFIG_FILE. // The file should contain: // "dataDir": path // "serverURL": where to point the requests // "password": The password for logging in // "budgetId": The ID of the budget containing the account to write transactions into. // "accountId": UUID for the account to add transactions to. import express from 'express' import AsyncLock from 'async-lock'; import cors from 'cors' import api from '@actual-app/api'; import fs from 'fs'; import { makeTransaction, parse } from './helpers.cjs'; async function addTransaction(config, transaction) { try { await api.init({ dataDir: config.dataDir, serverURL: config.serverURL, password: config.password, }); await api.downloadBudget(config.budgetId); await api.sync(); await api.addTransactions(config.accountId, [transaction]); } finally { await api.shutdown(); } } async function init() { const config = JSON.parse(fs.readFileSync(process.env.CONFIG_FILE)); const app = express(); const lock = new AsyncLock(); // Adds headers: Access-Control-Allow-Origin: * app.use(cors()) app.use(express.json()); app.post('/actual-api/transaction', async function (req, res, _next) { await lock.acquire('transaction', async () => { console.debug('Executing Transaction'); try { const transaction = makeTransaction(parse(req.body), config.accountId); console.log(transaction); await addTransaction(config, transaction) console.log(`Successfully logged "${transaction.payee_name} - ¥${transaction.amount}"`); return res.json({ result: 'success' }); } catch (e) { console.log('Transaction failed...'); console.log(e); console.log(e.message); return res.json({ result: 'failure', error: e.message }); } }); }); app.listen(12467, function () { console.log('Starting AMEX Transaction Writer'); }); } init();