feat(machine): add SQLite persistence and remove npub linking

Add crash-safe persistence for cassette inventory, cashbox state, and
transaction history using better-sqlite3 in the Electron main process.
The state machine now loads inventory from the database at runtime
instead of using hardcoded values, and transactions are automatically
persisted on completion.

Remove the unnecessary npub linking code — Lightning.Pub auto-creates
and associates Nostr users when appId is included in RPC requests,
making the HTTP-based user creation and token linking redundant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-02-19 11:45:09 -05:00
commit f37555565e
15 changed files with 739 additions and 171 deletions

View file

@ -10,6 +10,16 @@ import { app, BrowserWindow, ipcMain } from 'electron'
import path from 'node:path'
import fs from 'node:fs'
import { fileURLToPath } from 'node:url'
import {
initDatabase,
closeDatabase,
loadCassettes,
setCassettes,
getInventory,
recordTransaction,
emptyCashbox,
getCashbox,
} from './state-store.js'
// ESM equivalent of __dirname
const __filename = fileURLToPath(import.meta.url)
@ -100,8 +110,6 @@ ipcMain.handle('get-config', () => {
atmPrivateKey: process.env.VITE_ATM_PRIVATE_KEY || '',
adminToken: process.env.VITE_ADMIN_TOKEN || '',
appId: process.env.VITE_APP_ID || '',
appToken: process.env.VITE_APP_TOKEN || '',
linkingToken: process.env.VITE_LINKING_TOKEN || '',
// Hardware configuration
machineModel: process.env.VITE_LAMASSU_MACHINE_MODEL || 'sintra',
@ -112,8 +120,17 @@ ipcMain.handle('get-config', () => {
}
})
// State persistence IPC handlers
ipcMain.handle('state:load-cassettes', () => loadCassettes())
ipcMain.handle('state:set-cassettes', (_event, cassettes) => setCassettes(cassettes))
ipcMain.handle('state:get-inventory', () => getInventory())
ipcMain.handle('state:get-cashbox', () => getCashbox())
ipcMain.handle('state:record-transaction', (_event, tx) => recordTransaction(tx))
ipcMain.handle('state:empty-cashbox', () => emptyCashbox())
// App lifecycle
app.whenReady().then(() => {
initDatabase()
createWindow()
app.on('activate', () => {
@ -125,6 +142,7 @@ app.whenReady().then(() => {
})
app.on('window-all-closed', () => {
closeDatabase()
// Quit on all platforms (ATM doesn't need macOS dock behavior)
app.quit()
})