feat: add runtime config via Electron IPC
- Add .env.example with all configuration options documented - Add get-config IPC handler in Electron main process - Expose getConfig() in preload for renderer access - Update lightning.ts to load config at runtime in Electron - Fall back to Vite build-time env vars for browser dev mode - Add TypeScript declarations for electronAPI This allows the same build to be deployed to different machines with different configurations (relay URL, Lightning.Pub pubkey, etc.) set via environment variables at runtime. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c604aa501c
commit
56b1d594d7
5 changed files with 167 additions and 5 deletions
49
lamassu-next/apps/machine/.env.example
Normal file
49
lamassu-next/apps/machine/.env.example
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Lamassu ATM Configuration
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# =============================================================================
|
||||
# Hardware Configuration
|
||||
# =============================================================================
|
||||
|
||||
# Machine model preset (sintra, gaia, or custom)
|
||||
VITE_LAMASSU_MACHINE_MODEL=sintra
|
||||
|
||||
# Fiat currency code (ISO 4217)
|
||||
VITE_LAMASSU_FIAT_CODE=USD
|
||||
|
||||
# Custom device paths (optional - uses preset defaults if not set)
|
||||
# VITE_LAMASSU_VALIDATOR_DEVICE=/dev/ttyJ5
|
||||
# VITE_LAMASSU_DISPENSER_DEVICE=/dev/ttyJ7
|
||||
|
||||
# Cassette configuration (optional - JSON array)
|
||||
# VITE_LAMASSU_CASSETTES='[{"denomination":20,"count":100}]'
|
||||
|
||||
# =============================================================================
|
||||
# Lightning.Pub Connection (Required)
|
||||
# =============================================================================
|
||||
|
||||
# Nostr relay WebSocket URL
|
||||
VITE_RELAY_URL=ws://localhost:7777
|
||||
|
||||
# Lightning.Pub's Nostr public key (required!)
|
||||
# Get from: docker logs lamassu-lightning-pub | grep pubkey
|
||||
VITE_LIGHTNING_PUB_PUBKEY=
|
||||
|
||||
# Lightning.Pub HTTP API URL (optional)
|
||||
VITE_LIGHTNING_PUB_API_URL=http://localhost:1776
|
||||
|
||||
# =============================================================================
|
||||
# ATM Identity
|
||||
# =============================================================================
|
||||
|
||||
# ATM's Nostr private key (hex format, 64 characters)
|
||||
# Generate with: npx @lamassu/nostr-client generate-keypair
|
||||
# If not set, generates ephemeral identity on each restart
|
||||
VITE_ATM_PRIVATE_KEY=
|
||||
|
||||
# =============================================================================
|
||||
# Development Only
|
||||
# =============================================================================
|
||||
|
||||
# Lightning.Pub admin token (dev/testing only)
|
||||
VITE_ADMIN_TOKEN=lamassu-dev-admin-token
|
||||
|
|
@ -53,6 +53,28 @@ 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',
|
||||
atmPrivateKey: process.env.VITE_ATM_PRIVATE_KEY || '',
|
||||
adminToken: process.env.VITE_ADMIN_TOKEN || '',
|
||||
|
||||
// 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()
|
||||
|
|
|
|||
|
|
@ -7,11 +7,31 @@
|
|||
|
||||
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
|
||||
atmPrivateKey: string
|
||||
adminToken: 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,
|
||||
})
|
||||
|
|
@ -21,6 +41,7 @@ declare global {
|
|||
interface Window {
|
||||
electronAPI: {
|
||||
getVersion: () => Promise<string>
|
||||
getConfig: () => Promise<RuntimeConfig>
|
||||
platform: NodeJS.Platform
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,22 +30,42 @@ import {
|
|||
import type { OfferRequest } from '@lamassu/clink'
|
||||
import type { ATMServices, ATMContext } from '@lamassu/state-machine'
|
||||
|
||||
// Import Electron types
|
||||
import type {} from '@/types/electron'
|
||||
|
||||
// Check if we're in a browser environment
|
||||
const isBrowser = typeof window !== 'undefined'
|
||||
|
||||
// Check if we're running in Electron (electronAPI is exposed via preload)
|
||||
const isElectron = typeof window !== 'undefined' && window.electronAPI !== undefined
|
||||
|
||||
/**
|
||||
* Lightning configuration from environment variables
|
||||
* Lightning configuration
|
||||
*
|
||||
* Environment variables (prefix with VITE_ for Vite):
|
||||
* In Electron: Read from environment at runtime via IPC
|
||||
* In Browser (dev): Read from Vite's import.meta.env (build-time)
|
||||
*
|
||||
* Environment variables:
|
||||
* - VITE_RELAY_URL: Nostr relay WebSocket URL
|
||||
* - VITE_LIGHTNING_PUB_PUBKEY: Lightning.Pub's Nostr pubkey (hex or npub)
|
||||
* - VITE_LIGHTNING_PUB_API_URL: Lightning.Pub HTTP API URL
|
||||
* - VITE_ATM_PRIVATE_KEY: ATM's Nostr private key (hex or nsec)
|
||||
* - VITE_ADMIN_TOKEN: Lightning.Pub admin token (dev only)
|
||||
*/
|
||||
function loadLightningConfig() {
|
||||
interface LightningConfig {
|
||||
relayUrl: string
|
||||
lightningPubPubkey: string
|
||||
lightningPubApiUrl: string
|
||||
adminToken: string
|
||||
atmPrivateKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration - async to support Electron IPC
|
||||
*/
|
||||
async function loadLightningConfig(): Promise<LightningConfig> {
|
||||
// Development defaults (local Docker infrastructure)
|
||||
const defaults = {
|
||||
const defaults: LightningConfig = {
|
||||
relayUrl: 'ws://localhost:7777',
|
||||
lightningPubPubkey: '',
|
||||
lightningPubApiUrl: 'http://localhost:1776',
|
||||
|
|
@ -53,6 +73,23 @@ function loadLightningConfig() {
|
|||
atmPrivateKey: '',
|
||||
}
|
||||
|
||||
// In Electron, get runtime config from main process
|
||||
if (isElectron && window.electronAPI) {
|
||||
try {
|
||||
const runtimeConfig = await window.electronAPI.getConfig()
|
||||
return {
|
||||
relayUrl: runtimeConfig.relayUrl || defaults.relayUrl,
|
||||
lightningPubPubkey: runtimeConfig.lightningPubPubkey || defaults.lightningPubPubkey,
|
||||
lightningPubApiUrl: runtimeConfig.lightningPubApiUrl || defaults.lightningPubApiUrl,
|
||||
adminToken: runtimeConfig.adminToken || defaults.adminToken,
|
||||
atmPrivateKey: runtimeConfig.atmPrivateKey || defaults.atmPrivateKey,
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Lightning] Failed to get runtime config from Electron:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Vite build-time env vars (for browser dev mode)
|
||||
return {
|
||||
relayUrl: import.meta.env.VITE_RELAY_URL || defaults.relayUrl,
|
||||
lightningPubPubkey: import.meta.env.VITE_LIGHTNING_PUB_PUBKEY || defaults.lightningPubPubkey,
|
||||
|
|
@ -62,7 +99,8 @@ function loadLightningConfig() {
|
|||
}
|
||||
}
|
||||
|
||||
const CONFIG = loadLightningConfig()
|
||||
// Config is loaded async now - will be set in initializeLightningServices
|
||||
let CONFIG: LightningConfig
|
||||
|
||||
interface LightningServices {
|
||||
nostrClient: NostrClient
|
||||
|
|
@ -87,6 +125,10 @@ type PaymentReceivedCallback = (preimage: string) => void
|
|||
*/
|
||||
export async function initializeLightningServices(): Promise<LightningServices> {
|
||||
console.log('[Lightning] Initializing services...')
|
||||
|
||||
// Load configuration (async for Electron runtime config)
|
||||
CONFIG = await loadLightningConfig()
|
||||
|
||||
console.log('[Lightning] Relay URL:', CONFIG.relayUrl)
|
||||
console.log('[Lightning] Lightning.Pub pubkey:', CONFIG.lightningPubPubkey || '(not configured)')
|
||||
|
||||
|
|
|
|||
28
lamassu-next/apps/machine/src/types/electron.d.ts
vendored
Normal file
28
lamassu-next/apps/machine/src/types/electron.d.ts
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Type declarations for Electron API exposed via preload
|
||||
*/
|
||||
|
||||
export interface RuntimeConfig {
|
||||
relayUrl: string
|
||||
lightningPubPubkey: string
|
||||
lightningPubApiUrl: string
|
||||
atmPrivateKey: string
|
||||
adminToken: string
|
||||
machineModel: string
|
||||
fiatCode: string
|
||||
validatorDevice?: string
|
||||
dispenserDevice?: string
|
||||
cassettes?: string
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI?: {
|
||||
getVersion: () => Promise<string>
|
||||
getConfig: () => Promise<RuntimeConfig>
|
||||
platform: NodeJS.Platform
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue