# LandveX Finance > Banking-grade bokföringsgränssnitt för LandveX AB > Byggt på Ouroboros Ledger API med realtidsintegration mot Revolut, Nordea och SIE4-export. --- ## Arkitekturöversikt ``` ┌─────────────────────────────────────────────────────────────────┐ │ LANDVEX FINANCE │ │ (Single-Page Application) │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │ │ │ Översikt │ │Transaktioner│ │ Fakturor │ │ Moms │ │ │ │ (KPI) │ │ (Journal) │ │ (Invoice) │ │ (VAT) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────┘ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │ │ │ Löner │ │ Period │ │ Tasks │ │ Prognos│ │ │ │ (Payroll) │ │ (Close) │ │(Onboarding) │ │(Forecast)│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────┘ │ ├─────────────────────────────────────────────────────────────────┤ │ UI: Vanilla JS ES Modules + CSS Custom Properties │ │ State: In-memory med localStorage-persistens │ │ API: Fetch → LandveX Ledger Proxy → Ouroboros Ledger (port 3250)│ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ LANDVEX LEDGER PROXY │ │ (/opt/amos/api/landvex/ledger-proxy.mjs) │ ├─────────────────────────────────────────────────────────────────┤ │ • Server-side auth (JWT → ledger token) │ │ • Trial balance beräknas server-side (prestanda) │ │ • Revolut integration (JWT assertion + refresh token) │ │ • SIE4-export │ └─────────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │Ledger │ │ Revolut │ │ Nordea │ │(3250) │ │ (B2B) │ │ (API) │ └─────────┘ └──────────┘ └──────────┘ ``` ### Teknisk Stack | Lager | Teknik | |-------|--------| | Frontend | Vanilla JS (ES2022), CSS Custom Properties, inget framework | | Backend Proxy | Node.js + Express (ES modules) | | Ledger API | Ouroboros Ledger (port 3250) | | Bankintegration | Revolut B2B API, Nordea Open Banking | | Datalagring | PostgreSQL (ledger), JSONL-filer (backup) | | Cache | Redis | | Auth | JWT (wavult_token cookie) | --- ## Deployment-guide ### Förutsättningar - Node.js ≥ 20 - PostgreSQL ≥ 15 - Redis ≥ 7 - Nginx (reverse proxy + SSL) ### Miljövariabler ```bash # Databas DATABASE_URL=postgresql://user:pass@host:5432/wavult_identity LEDGER_DB_URL=postgresql://user:pass@host:5432/ledger # Redis REDIS_URL=redis://localhost:6379 # Auth AMOS_JWT_SECRET=your-jwt-secret-here # Revolut (B2B) REVOLUT_CLIENT_ID=your-client-id REVOLUT_REFRESH_TOKEN=your-refresh-token REVOLUT_PRIVATE_KEY_PATH=/opt/amos/data/revolut_private_prod.pem # Intern kommunikation LEDGER_BASE=http://localhost:3250 LEDGER_TOKEN=wavult-amos-internal-2026 # Server PORT=3100 BIND_HOST=0.0.0.0 NODE_ENV=production ``` ### Installation ```bash # 1. Klona repo (om separat) cd /opt/amos # 2. Installera beroenden npm install # 3. Verifiera miljövariabler cp .env.example .env nano .env # 4. Starta ledger-tjänsten (om separat) cd /opt/amos/services/ledger npm start # port 3250 # 5. Starta huvudservern npm start # port 3100 # 6. Verifiera health checks curl http://localhost:3100/health curl http://localhost:3100/health/finance curl http://localhost:3100/prom-metrics ``` ### PM2 (produktion) ```bash # ecosystem.config.cjs module.exports = { apps: [{ name: 'amos-finance', script: './scripts/server.mjs', instances: 1, exec_mode: 'fork', env: { NODE_ENV: 'production' }, log_file: '/var/log/amos/finance.log', error_file: '/var/log/amos/finance-error.log', out_file: '/var/log/amos/finance-out.log', merge_logs: true, log_date_format: 'YYYY-MM-DD HH:mm:ss Z', }] }; ``` ### Nginx-konfiguration ```nginx server { listen 443 ssl http2; server_name finance.wavult.com; ssl_certificate /etc/letsencrypt/live/wavult.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/wavult.com/privkey.pem; location / { root /opt/amos/public/ouroboros/finance; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:3100; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /prom-metrics { proxy_pass http://localhost:3100; allow 10.0.0.0/8; # Interna nätverk endast deny all; } } ``` --- ## API-endpoints ### Finance Proxy (`/api/landvex/*`) | Metod | Endpoint | Beskrivning | |-------|----------|-------------| | GET | `/api/landvex/ledger/journal` | Journal (verifikat) | | GET | `/api/landvex/ledger/trial-balance` | Saldobalans (server-side) | | POST | `/api/landvex/ledger/journal` | Skapa verifikat | | POST | `/api/landvex/ledger/journal/:id/post` | Bokför verifikat | | GET | `/api/landvex/ledger/accounts` | Kontoplan | | GET | `/api/landvex/ledger/periods` | Räkenskapsperioder | | GET | `/api/landvex/ledger/invoices` | Fakturor | | POST | `/api/landvex/ledger/invoices` | Skapa faktura | | GET | `/api/landvex/ledger/export/sie4` | SIE4-export | | POST | `/api/landvex/ledger/journal/:id/upload` | Ladda upp kvitto | | GET | `/api/landvex/ledger/journal/:id/receipts` | Hämta kvitton | ### Health & Metrics | Endpoint | Beskrivning | |----------|-------------| | `GET /health` | Simpel health check | | `GET /health/detailed` | Vault + OPA status | | `GET /health/finance` | DB + Redis + disk + ledger + Revolut | | `GET /prom-metrics` | Prometheus metrics (intern) | | `GET /metrics` | JSON metrics (legacy) | --- ## Miljövariabler (komplett lista) ### Obligatoriska | Variabel | Beskrivning | Exempel | |----------|-------------|---------| | `DATABASE_URL` | PostgreSQL för identity/tenant | `postgresql://.../wavult_identity` | | `AMOS_JWT_SECRET` | JWT-signering | `super-secret-key` | | `LEDGER_BASE` | Ouroboros Ledger URL | `http://localhost:3250` | | `LEDGER_TOKEN` | Intern token för ledger | `wavult-amos-internal-2026` | ### Bankintegration (valfria) | Variabel | Beskrivning | |----------|-------------| | `REVOLUT_CLIENT_ID` | Revolut B2B client ID | | `REVOLUT_REFRESH_TOKEN` | Refresh token för Revolut | | `REVOLUT_PRIVATE_KEY_PATH` | Sökväg till RSA-nyckel | | `NORDEA_CLIENT_ID` | Nordea Open Banking | | `NORDEA_CLIENT_SECRET` | Nordea secret | ### Operativa | Variabel | Beskrivning | Standard | |----------|-------------|----------| | `PORT` | Serverport | `3100` | | `BIND_HOST` | Bind-adress | `0.0.0.0` | | `NODE_ENV` | Miljö | `development` | | `REDIS_URL` | Redis-anslutning | `redis://localhost:6379` | | `LOG_LEVEL` | Loggnivå | `info` | --- ## Utveckling ### Lokal utveckling ```bash # 1. Starta med utvecklingsläge NODE_ENV=development npm start # 2. UI-filer serveras från /public/ouroboros/finance/ # Ändringar i index.html syns direkt (ingen build) # 3. Testa API curl -H "Authorization: Bearer $TOKEN" \ http://localhost:3100/api/landvex/ledger/journal?limit=5 ``` ### Filstruktur (frontend) ``` public/ouroboros/finance/ ├── index.html # Huvudfil (177KB, ska brytas ner) ├── ledger.html # Äldre ledger-vy └── docs/ ├── UI-REFACTOR-PLAN.md # Refaktoreringsplan └── ARCHITECTURE.md # Arkitekturbeskrivning ``` ### Filstruktur (backend) ``` api/ ├── finance/ # Finance-specifika moduler (nya) │ ├── metrics.mjs # Prometheus metrics │ ├── logger.mjs # Strukturerad loggning │ └── health.mjs # Health checks ├── landvex/ │ └── ledger-proxy.mjs # Huvud-proxy för LandveX ├── finance/ # Generella finance-routes │ ├── ledger.mjs │ ├── invoices.mjs │ └── ... └── ... ``` --- ## Övervakning ### Prometheus Metrics ```bash # Scrape-config för Prometheus scrape_configs: - job_name: 'landvex-finance' static_configs: - targets: ['localhost:3100'] metrics_path: '/prom-metrics' ``` ### Viktiga metrics | Metric | Typ | Beskrivning | |--------|-----|-------------| | `finance_journal_entries_total` | Counter | Antal verifikat | | `finance_invoices_total` | Gauge | Fakturor per status | | `finance_vat_payable` | Gauge | Moms att betala | | `finance_api_duration_seconds` | Histogram | API-svarstider | | `amos_http_requests_total` | Counter | HTTP-requests | ### Larm (förslag) | Larm | Condition | Åtgärd | |------|-----------|--------| | DB nere | `health/finance` != 200 | PagerDuty | | Disk full | `disk.free_gb < 1` | Rensa loggar | | Revolut auth fail | `revolut.authenticated == false` | Rotera token | | API-latens | `p95 > 2s` | Skala upp | --- ## Säkerhet - **Auth:** JWT i httpOnly-cookie, Bearer-token i header - **CORS:** Endast `*.wavult.com` - **Rate limiting:** 100 req/min per IP - **Input validation:** Alla POST/PUT valideras - **SQL injection:** Parameteriserade queries (pg) - **XSS:** `esc()`-funktion i UI, CSP-headers - **CSRF:** SameSite=Lax på cookies --- ## Support - **Slack:** #finance-dev - **On-call:** finance-oncall@wavult.com - **Runbook:** [Confluence/Runbooks/Finance](https://wiki.wavult.com) --- *Senast uppdaterad: 2026-06-24* *Version: Sprint 3 (UI & Observability)*