feat(docker): add dev.sh with auto-funding and ATM app setup
- Add dev.sh script for managing regtest development environment - Implement cmd_fund to fund ATM app owner via Lightning.Pub API - Add --fund flag to cmd_up for automatic funding on startup - Update setup_atm_app to write VITE_APP_ID to machine .env - Fix Electron IPC to pass appId and extensionApiUrl to renderer - Restructure repo from nested lamassu-next/ to root The dev.sh script now supports: - ./dev.sh up --fund # Start regtest and auto-fund ATM - ./dev.sh fund # Fund existing ATM app - ./dev.sh status # Show environment status - ./dev.sh reset # Clean restart Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
30a2eb2199
commit
c98f126ba7
180 changed files with 2695 additions and 9587 deletions
132
apps/machine/electron/main.ts
Normal file
132
apps/machine/electron/main.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* Electron Main Process
|
||||
*
|
||||
* Creates the ATM kiosk window and handles IPC with the renderer.
|
||||
* In production, runs in kiosk mode (fullscreen, no decorations).
|
||||
* HAL hardware drivers run here (Node.js environment).
|
||||
*/
|
||||
|
||||
import { app, BrowserWindow, ipcMain } from 'electron'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
// ESM equivalent of __dirname
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
|
||||
// Load .env file manually (Electron main process doesn't have Vite's env loading)
|
||||
function loadEnvFile() {
|
||||
const envPath = path.join(__dirname, '..', '.env')
|
||||
try {
|
||||
if (fs.existsSync(envPath)) {
|
||||
const envContent = fs.readFileSync(envPath, 'utf-8')
|
||||
for (const line of envContent.split('\n')) {
|
||||
const trimmed = line.trim()
|
||||
if (trimmed && !trimmed.startsWith('#')) {
|
||||
const [key, ...valueParts] = trimmed.split('=')
|
||||
if (key && valueParts.length > 0) {
|
||||
process.env[key] = valueParts.join('=')
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('[Electron] Loaded .env file from:', envPath)
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Electron] Failed to load .env file:', e)
|
||||
}
|
||||
}
|
||||
|
||||
loadEnvFile()
|
||||
|
||||
// Determine if we're in development
|
||||
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
|
||||
|
||||
// Window dimensions (vertical ATM display)
|
||||
const WINDOW_WIDTH = 1080
|
||||
const WINDOW_HEIGHT = 1920
|
||||
|
||||
let mainWindow: BrowserWindow | null = null
|
||||
|
||||
function createWindow() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: WINDOW_WIDTH,
|
||||
height: WINDOW_HEIGHT,
|
||||
resizable: isDev, // Allow resize in dev for debugging
|
||||
frame: isDev, // Show window frame in dev
|
||||
kiosk: !isDev, // Kiosk mode in production
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
})
|
||||
|
||||
// Load the app
|
||||
if (isDev) {
|
||||
// Development: load from Vite dev server
|
||||
const devServerUrl = process.env.VITE_DEV_SERVER_URL || 'http://localhost:1420'
|
||||
mainWindow.loadURL(devServerUrl)
|
||||
mainWindow.webContents.openDevTools()
|
||||
} else {
|
||||
// Production: load from built files
|
||||
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
|
||||
}
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
// IPC Handlers
|
||||
ipcMain.handle('get-version', () => {
|
||||
return app.getVersion()
|
||||
})
|
||||
|
||||
/**
|
||||
* Get runtime configuration from environment variables
|
||||
* This allows configuration to be set at runtime (not baked in at build time)
|
||||
*/
|
||||
ipcMain.handle('get-config', () => {
|
||||
return {
|
||||
// Lightning.Pub connection
|
||||
relayUrl: process.env.VITE_RELAY_URL || 'ws://localhost:7777',
|
||||
lightningPubPubkey: process.env.VITE_LIGHTNING_PUB_PUBKEY || '',
|
||||
lightningPubApiUrl: process.env.VITE_LIGHTNING_PUB_API_URL || 'http://localhost:1776',
|
||||
extensionApiUrl: process.env.VITE_EXTENSION_API_URL || 'http://localhost:1777',
|
||||
atmPrivateKey: process.env.VITE_ATM_PRIVATE_KEY || '',
|
||||
adminToken: process.env.VITE_ADMIN_TOKEN || '',
|
||||
appId: process.env.VITE_APP_ID || '',
|
||||
|
||||
// Hardware configuration
|
||||
machineModel: process.env.VITE_LAMASSU_MACHINE_MODEL || 'sintra',
|
||||
fiatCode: process.env.VITE_LAMASSU_FIAT_CODE || 'USD',
|
||||
validatorDevice: process.env.VITE_LAMASSU_VALIDATOR_DEVICE,
|
||||
dispenserDevice: process.env.VITE_LAMASSU_DISPENSER_DEVICE,
|
||||
cassettes: process.env.VITE_LAMASSU_CASSETTES,
|
||||
}
|
||||
})
|
||||
|
||||
// App lifecycle
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', () => {
|
||||
// macOS: re-create window when dock icon clicked
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
// Quit on all platforms (ATM doesn't need macOS dock behavior)
|
||||
app.quit()
|
||||
})
|
||||
|
||||
// Security: prevent new window creation
|
||||
app.on('web-contents-created', (_, contents) => {
|
||||
contents.setWindowOpenHandler(() => {
|
||||
return { action: 'deny' }
|
||||
})
|
||||
})
|
||||
50
apps/machine/electron/preload.ts
Normal file
50
apps/machine/electron/preload.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Electron Preload Script
|
||||
*
|
||||
* Exposes a secure API to the renderer process via contextBridge.
|
||||
* This is the only way for the Vue app to communicate with the main process.
|
||||
*/
|
||||
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
|
||||
/**
|
||||
* Runtime configuration interface
|
||||
* These values are read from environment variables at runtime (not build time)
|
||||
*/
|
||||
export interface RuntimeConfig {
|
||||
relayUrl: string
|
||||
lightningPubPubkey: string
|
||||
lightningPubApiUrl: string
|
||||
extensionApiUrl: string
|
||||
atmPrivateKey: string
|
||||
adminToken: string
|
||||
appId: string
|
||||
machineModel: string
|
||||
fiatCode: string
|
||||
validatorDevice?: string
|
||||
dispenserDevice?: string
|
||||
cassettes?: string
|
||||
}
|
||||
|
||||
// Expose protected methods to renderer
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
// Get app version
|
||||
getVersion: (): Promise<string> => ipcRenderer.invoke('get-version'),
|
||||
|
||||
// Get runtime configuration (read from environment at runtime)
|
||||
getConfig: (): Promise<RuntimeConfig> => ipcRenderer.invoke('get-config'),
|
||||
|
||||
// Platform info
|
||||
platform: process.platform,
|
||||
})
|
||||
|
||||
// Type declaration for the exposed API
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: {
|
||||
getVersion: () => Promise<string>
|
||||
getConfig: () => Promise<RuntimeConfig>
|
||||
platform: NodeJS.Platform
|
||||
}
|
||||
}
|
||||
}
|
||||
14
apps/machine/electron/tsconfig.json
Normal file
14
apps/machine/electron/tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "../dist-electron",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["main.ts"],
|
||||
"exclude": ["preload.ts"]
|
||||
}
|
||||
13
apps/machine/electron/tsconfig.preload.json
Normal file
13
apps/machine/electron/tsconfig.preload.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "../dist-electron",
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["preload.ts"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue