aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
1007 lines
45 KiB
Markdown
1007 lines
45 KiB
Markdown
# LandveX Enterprise Platform — Admin Backend Design
|
|
|
|
## Overview
|
|
|
|
A multi-tenant admin backend for LandveX Enterprise Platform serving municipalities (kommuner) and enterprises. Built with FastAPI (Python), PostgreSQL, and React 19.
|
|
|
|
---
|
|
|
|
## 1. Data Model (PostgreSQL)
|
|
|
|
### Core Tables
|
|
|
|
```sql
|
|
-- Tenants (top-level isolation)
|
|
CREATE TABLE tenants (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(255) NOT NULL,
|
|
slug VARCHAR(100) UNIQUE NOT NULL,
|
|
type VARCHAR(20) NOT NULL CHECK (type IN ('municipality', 'enterprise', 'partner')),
|
|
org_number VARCHAR(20) UNIQUE,
|
|
billing_email VARCHAR(255) NOT NULL,
|
|
plan_id UUID REFERENCES subscription_plans(id),
|
|
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'cancelled', 'trial')),
|
|
settings JSONB DEFAULT '{}',
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Subscription Plans
|
|
CREATE TABLE subscription_plans (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL,
|
|
slug VARCHAR(50) UNIQUE NOT NULL,
|
|
description TEXT,
|
|
price_monthly DECIMAL(10,2) NOT NULL,
|
|
price_yearly DECIMAL(10,2),
|
|
features JSONB NOT NULL DEFAULT '{}',
|
|
limits JSONB NOT NULL DEFAULT '{}', -- {"users": 10, "api_calls": 100000}
|
|
is_public BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Users (tenant-scoped)
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
|
email VARCHAR(255) NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
first_name VARCHAR(100),
|
|
last_name VARCHAR(100),
|
|
phone VARCHAR(50),
|
|
avatar_url TEXT,
|
|
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'pending', 'locked')),
|
|
last_login_at TIMESTAMPTZ,
|
|
failed_login_attempts INT DEFAULT 0,
|
|
locked_until TIMESTAMPTZ,
|
|
mfa_enabled BOOLEAN DEFAULT false,
|
|
mfa_secret VARCHAR(255),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(tenant_id, email)
|
|
);
|
|
|
|
-- Roles
|
|
CREATE TABLE roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE, -- NULL = global role
|
|
name VARCHAR(100) NOT NULL,
|
|
slug VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
permissions JSONB NOT NULL DEFAULT '[]',
|
|
is_system BOOLEAN DEFAULT false,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(tenant_id, slug)
|
|
);
|
|
|
|
-- User Roles (many-to-many)
|
|
CREATE TABLE user_roles (
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
|
|
assigned_by UUID REFERENCES users(id),
|
|
assigned_at TIMESTAMPTZ DEFAULT NOW(),
|
|
PRIMARY KEY (user_id, role_id)
|
|
);
|
|
|
|
-- API Keys
|
|
CREATE TABLE api_keys (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
|
name VARCHAR(100) NOT NULL,
|
|
key_hash VARCHAR(255) NOT NULL,
|
|
key_prefix VARCHAR(8) NOT NULL,
|
|
scopes JSONB NOT NULL DEFAULT '[]',
|
|
rate_limit INT DEFAULT 1000,
|
|
expires_at TIMESTAMPTZ,
|
|
last_used_at TIMESTAMPTZ,
|
|
created_by UUID REFERENCES users(id),
|
|
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'revoked', 'expired')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Audit Log
|
|
CREATE TABLE audit_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES tenants(id),
|
|
user_id UUID REFERENCES users(id),
|
|
action VARCHAR(50) NOT NULL,
|
|
resource_type VARCHAR(50) NOT NULL,
|
|
resource_id UUID,
|
|
details JSONB DEFAULT '{}',
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
severity VARCHAR(20) DEFAULT 'info' CHECK (severity IN ('info', 'warning', 'critical')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Invoices
|
|
CREATE TABLE invoices (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
|
invoice_number VARCHAR(50) UNIQUE NOT NULL,
|
|
period_start DATE NOT NULL,
|
|
period_end DATE NOT NULL,
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
tax_amount DECIMAL(10,2) NOT NULL,
|
|
total_amount DECIMAL(10,2) NOT NULL,
|
|
currency VARCHAR(3) DEFAULT 'SEK',
|
|
status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'sent', 'paid', 'overdue', 'cancelled')),
|
|
paid_at TIMESTAMPTZ,
|
|
stripe_invoice_id VARCHAR(100),
|
|
pdf_url TEXT,
|
|
due_date DATE NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Invoice Line Items
|
|
CREATE TABLE invoice_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
invoice_id UUID NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
|
|
description VARCHAR(255) NOT NULL,
|
|
quantity DECIMAL(10,2) NOT NULL,
|
|
unit_price DECIMAL(10,2) NOT NULL,
|
|
amount DECIMAL(10,2) NOT NULL,
|
|
metadata JSONB DEFAULT '{}'
|
|
);
|
|
|
|
-- Plugins
|
|
CREATE TABLE plugins (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name VARCHAR(100) NOT NULL,
|
|
slug VARCHAR(100) UNIQUE NOT NULL,
|
|
description TEXT,
|
|
version VARCHAR(20) NOT NULL,
|
|
author VARCHAR(100),
|
|
config_schema JSONB DEFAULT '{}',
|
|
is_system BOOLEAN DEFAULT false,
|
|
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'deprecated')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Tenant Plugins (installed plugins)
|
|
CREATE TABLE tenant_plugins (
|
|
tenant_id UUID REFERENCES tenants(id) ON DELETE CASCADE,
|
|
plugin_id UUID REFERENCES plugins(id) ON DELETE CASCADE,
|
|
config JSONB DEFAULT '{}',
|
|
installed_by UUID REFERENCES users(id),
|
|
installed_at TIMESTAMPTZ DEFAULT NOW(),
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
PRIMARY KEY (tenant_id, plugin_id)
|
|
);
|
|
|
|
-- Reports
|
|
CREATE TABLE reports (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
type VARCHAR(50) NOT NULL,
|
|
config JSONB NOT NULL DEFAULT '{}',
|
|
schedule VARCHAR(50), -- cron expression or null for one-time
|
|
last_run_at TIMESTAMPTZ,
|
|
next_run_at TIMESTAMPTZ,
|
|
created_by UUID REFERENCES users(id),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- Report Runs
|
|
CREATE TABLE report_runs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
report_id UUID NOT NULL REFERENCES reports(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
|
|
result_url TEXT,
|
|
error_message TEXT,
|
|
started_at TIMESTAMPTZ DEFAULT NOW(),
|
|
completed_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- Indexes for performance
|
|
CREATE INDEX idx_users_tenant_email ON users(tenant_id, email);
|
|
CREATE INDEX idx_audit_logs_tenant_created ON audit_logs(tenant_id, created_at DESC);
|
|
CREATE INDEX idx_audit_logs_user ON audit_logs(user_id, created_at DESC);
|
|
CREATE INDEX idx_invoices_tenant_status ON invoices(tenant_id, status);
|
|
CREATE INDEX idx_api_keys_tenant ON api_keys(tenant_id, status);
|
|
```
|
|
|
|
---
|
|
|
|
## 2. API Specification (FastAPI + OpenAPI)
|
|
|
|
### Base URL
|
|
```
|
|
https://api.landvex.com/v1
|
|
```
|
|
|
|
### Authentication
|
|
- **JWT Bearer** for user sessions
|
|
- **API Key** (`X-API-Key` header) for service-to-service
|
|
|
|
### Common Headers
|
|
```
|
|
X-Tenant-ID: <tenant-uuid> # Required for all tenant-scoped endpoints
|
|
```
|
|
|
|
### Endpoints
|
|
|
|
#### Authentication
|
|
```
|
|
POST /auth/login
|
|
POST /auth/logout
|
|
POST /auth/refresh
|
|
POST /auth/mfa/setup
|
|
POST /auth/mfa/verify
|
|
POST /auth/forgot-password
|
|
POST /auth/reset-password
|
|
```
|
|
|
|
#### Tenants
|
|
```
|
|
GET /tenants # List (superadmin)
|
|
POST /tenants # Create (superadmin)
|
|
GET /tenants/{id}
|
|
PATCH /tenants/{id}
|
|
DELETE /tenants/{id} # Soft delete
|
|
GET /tenants/{id}/stats # Usage statistics
|
|
GET /tenants/{id}/billing # Billing summary
|
|
```
|
|
|
|
#### Users
|
|
```
|
|
GET /users
|
|
POST /users
|
|
GET /users/{id}
|
|
PATCH /users/{id}
|
|
DELETE /users/{id}
|
|
PATCH /users/{id}/status # Activate/deactivate/lock
|
|
POST /users/{id}/roles # Assign roles
|
|
DELETE /users/{id}/roles/{role_id}
|
|
GET /users/me # Current user profile
|
|
PATCH /users/me
|
|
```
|
|
|
|
#### Roles & Permissions
|
|
```
|
|
GET /roles
|
|
POST /roles
|
|
GET /roles/{id}
|
|
PATCH /roles/{id}
|
|
DELETE /roles/{id}
|
|
GET /permissions # List all available permissions
|
|
```
|
|
|
|
#### API Keys
|
|
```
|
|
GET /api-keys
|
|
POST /api-keys # Returns key once (not stored plaintext)
|
|
GET /api-keys/{id}
|
|
PATCH /api-keys/{id}
|
|
DELETE /api-keys/{id}
|
|
POST /api-keys/{id}/revoke
|
|
```
|
|
|
|
#### Billing & Subscriptions
|
|
```
|
|
GET /billing/plans
|
|
GET /billing/subscription
|
|
POST /billing/subscribe
|
|
POST /billing/cancel
|
|
POST /billing/upgrade
|
|
GET /billing/invoices
|
|
GET /billing/invoices/{id}
|
|
GET /billing/invoices/{id}/pdf
|
|
POST /billing/payment-method
|
|
```
|
|
|
|
#### Audit Logs
|
|
```
|
|
GET /audit-logs
|
|
GET /audit-logs/export # CSV/JSON export
|
|
GET /audit-logs/stats # Summary statistics
|
|
```
|
|
|
|
#### Reports
|
|
```
|
|
GET /reports
|
|
POST /reports
|
|
GET /reports/{id}
|
|
PATCH /reports/{id}
|
|
DELETE /reports/{id}
|
|
POST /reports/{id}/run
|
|
GET /reports/{id}/runs
|
|
GET /reports/{id}/runs/{run_id}/download
|
|
```
|
|
|
|
#### Plugins
|
|
```
|
|
GET /plugins # Available plugins
|
|
GET /plugins/installed
|
|
POST /plugins/{id}/install
|
|
PATCH /plugins/{id}/config
|
|
DELETE /plugins/{id}/uninstall
|
|
POST /plugins/{id}/enable
|
|
POST /plugins/{id}/disable
|
|
```
|
|
|
|
#### Dashboard & Analytics
|
|
```
|
|
GET /dashboard/summary
|
|
GET /dashboard/activity
|
|
GET /dashboard/usage
|
|
GET /analytics/users
|
|
GET /analytics/api-usage
|
|
GET /analytics/revenue
|
|
```
|
|
|
|
### Response Format
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {},
|
|
"meta": {
|
|
"page": 1,
|
|
"per_page": 20,
|
|
"total": 100,
|
|
"total_pages": 5
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Format
|
|
```json
|
|
{
|
|
"success": false,
|
|
"error": {
|
|
"code": "VALIDATION_ERROR",
|
|
"message": "Invalid input data",
|
|
"details": [
|
|
{"field": "email", "message": "Invalid email format"}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. UI Sketches (React 19)
|
|
|
|
### Layout Structure
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ 🏠 LandveX 🔍 Search 🔔 👤 Profile ▼ │ ← TopBar
|
|
├──────────┬──────────────────────────────────────────────┤
|
|
│ │ │
|
|
│ Dashboard│ Main Content Area │
|
|
│ ├── Overview │
|
|
│ ├── Analytics │
|
|
│ Tenants │ │
|
|
│ ├── All Tenants │
|
|
│ ├── Pending │
|
|
│ Users │ │
|
|
│ ├── All Users │
|
|
│ ├── Roles │
|
|
│ API Keys │ │
|
|
│ Billing │ │
|
|
│ ├── Invoices │
|
|
│ ├── Plans │
|
|
│ ├── Subscriptions │
|
|
│ Audit Logs│ │
|
|
│ Reports │ │
|
|
│ Plugins │ │
|
|
│ Settings │ │
|
|
│ │ │
|
|
└──────────┴──────────────────────────────────────────────┘
|
|
Sidebar Content
|
|
```
|
|
|
|
### Key Screens
|
|
|
|
#### Dashboard Overview
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Dashboard [Refresh] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
|
│ │ Active │ │ Total │ │ API Calls│ │ Revenue │ │
|
|
│ │ Tenants │ │ Users │ │ Today │ │ (MTD) │ │
|
|
│ │ 42 │ │ 156 │ │ 1.2M │ │ 45,000 │ │
|
|
│ │ ↑ 12% │ │ ↑ 8% │ │ ↑ 23% │ │ ↑ 15% │ │
|
|
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Recent Activity API Usage (7d) │
|
|
│ ───────────────── ────────────── │
|
|
│ • New tenant: Stockholm Kommun ┌──────────────┐ │
|
|
│ • User locked: admin@example.com │ 📈 │ │
|
|
│ • Invoice paid: #INV-2024-001 │ Chart │ │
|
|
│ • API key revoked: prod-key │ │ │
|
|
│ └──────────────┘ │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Recent Invoices Recent Audit Logs │
|
|
│ ─────────────── ───────────────── │
|
|
│ #INV-001 5,000 SEK admin@x DELETE user 2m │
|
|
│ #INV-002 2,500 SEK api@x CREATE key 5m │
|
|
│ #INV-003 12,000 SEK sys UPDATE tenant 1h │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Tenant Management
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Tenants [+ New Tenant] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [🔍 Search...] [Type ▼] [Status ▼] [Plan ▼] [Export] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Name Type Plan Users Status │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ Stockholm Kommun Municipality Enterprise 12 🟢 Active│
|
|
│ Volvo Group Enterprise Business 45 🟢 Active│
|
|
│ Malmö Stad Municipality Starter 8 🟡 Trial │
|
|
│ Ericsson Enterprise Enterprise 89 🟢 Active│
|
|
│ ... │
|
|
│ │
|
|
│ [← Prev] Page 1 of 5 [Next →] Showing 1-10 of 42 │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### User Management with Roles
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Users [+ Invite User] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [🔍 Search...] [Role ▼] [Status ▼] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Name Email Role Status │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ Anna Svensson anna@x.com Admin 🟢 Active │
|
|
│ Erik Johansson erik@x.com Editor 🟢 Active │
|
|
│ Maria Lind maria@x.com Viewer 🟡 Pending│
|
|
│ [Locked] Johan johan@x.com Editor 🔴 Locked │
|
|
│ │
|
|
│ Actions: [Edit] [Reset PW] [Lock] [Delete] [Assign Role]│
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Role Editor
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Edit Role: Admin [Save] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Name: [Admin ] │
|
|
│ Description: [Full system access... ] │
|
|
│ │
|
|
│ Permissions │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ ☑ Users ☑ Create ☑ Read ☑ Update ☑ Delete │
|
|
│ ☑ Tenants ☐ Create ☑ Read ☑ Update ☐ Delete │
|
|
│ ☑ Billing ☑ Read ☑ Manage Invoices │
|
|
│ ☑ API Keys ☑ Create ☑ Read ☑ Revoke │
|
|
│ ☑ Reports ☑ Create ☑ Run ☑ Export │
|
|
│ ☐ Plugins ☐ Install ☐ Configure ☐ Uninstall │
|
|
│ │
|
|
│ Members (12) │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ Anna Svensson, Erik Johansson, ... [+ Add Member] │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### API Key Management
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ API Keys [+ Generate Key] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ ⚠️ Only show key once on creation! │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Name Prefix Scopes Last Used │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ Production prod_*** read,write 2 min ago │
|
|
│ Integration int_*** read 1 hour ago │
|
|
│ Backup Script back_*** read 3 days ago │
|
|
│ │
|
|
│ [Revoke] [Regenerate] [Edit Scopes] │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Billing & Invoices
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Billing │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Current Plan: Enterprise │
|
|
│ Next billing: 2024-08-01 (15 days) │
|
|
│ Amount: 12,000 SEK/month │
|
|
│ [Upgrade] [Cancel Subscription] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Payment Methods [+ Add Card] │
|
|
│ 💳 •••• 4242 Exp: 12/25 [Default] [Remove] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Invoices │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ #INV-2024-007 12,000 SEK Paid Jul 1 [PDF] │
|
|
│ #INV-2024-006 12,000 SEK Paid Jun 1 [PDF] │
|
|
│ #INV-2024-005 12,000 SEK Paid May 1 [PDF] │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Audit Log Viewer
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Audit Logs [Export CSV] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [🔍 Search] [Action ▼] [User ▼] [Date Range] [Severity]│
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ Time User Action Resource IP │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ 14:32:05 admin@x DELETE user/abc 10.0.0.1 │
|
|
│ 14:30:22 api@x CREATE api_key/xyz 10.0.0.2 │
|
|
│ 14:15:00 system UPDATE tenant/123 internal │
|
|
│ 13:45:10 erik@x LOGIN session/xxx 192.168.1 │
|
|
│ │
|
|
│ [View Details] for each row │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Report Builder
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Reports [+ New Report] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ Report: Monthly API Usage │ │
|
|
│ │ Type: Bar Chart Schedule: Monthly (1st) │ │
|
|
│ │ │ │
|
|
│ │ Filters: │ │
|
|
│ │ Date Range: [2024-01-01] to [2024-06-30] │ │
|
|
│ │ Tenant: [All ▼] Endpoint: [/api/v1/users ▼] │ │
|
|
│ │ │ │
|
|
│ │ [Run Now] [Schedule] [Export PDF] │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ Recent Runs │
|
|
│ ─────────────────────────────────────────────────────── │
|
|
│ Jul 1, 2024 Completed [Download PDF] [Download CSV] │
|
|
│ Jun 1, 2024 Completed [Download PDF] │
|
|
│ May 1, 2024 Failed [View Error] │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
#### Plugin Marketplace
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Plugins │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ [🔍 Search...] [Installed] [Available] [Updates] │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ 📊 Analytics│ │ 🗺️ GIS │ │ 📄 PDF Gen │ │
|
|
│ │ v2.1.0 │ │ v1.5.2 │ │ v3.0.0 │ │
|
|
│ │ By LandveX │ │ By LandveX │ │ By LandveX │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ [Installed] │ │ [Install] │ │ [Update] │ │
|
|
│ │ [Configure] │ │ [Details] │ │ [Details] │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
│ │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ 🔗 Webhook │ │ 📧 Email │ │ 🤖 AI Assist│ │
|
|
│ │ v1.0.0 │ │ v2.0.1 │ │ v0.9.0 │ │
|
|
│ │ By Acme │ │ By LandveX │ │ By OpenAI │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ [Install] │ │ [Installed] │ │ [Install] │ │
|
|
│ │ [Details] │ │ [Configure] │ │ [Details] │ │
|
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Security Architecture
|
|
|
|
### Authentication Flow
|
|
```
|
|
┌─────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────┐
|
|
│ Client │────▶│ FastAPI │────▶│ PostgreSQL │────▶│ Redis │
|
|
│ │◄────│ Backend │◄────│ (users) │◄────│ (sessions)
|
|
└─────────┘ └─────────────┘ └─────────────┘ └─────────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ Argon2id │ ← Password hashing
|
|
│ (pwd) │
|
|
└─────────────┘
|
|
```
|
|
|
|
### Multi-Tenant Isolation
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ API Gateway / WAF │
|
|
│ (Rate limiting, DDoS protection) │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Authentication Layer │
|
|
│ (JWT validation, API key check) │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Tenant Resolution │
|
|
│ • Header: X-Tenant-ID │
|
|
│ • JWT claim: tenant_id │
|
|
│ • API key → lookup tenant │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ Row-Level Security (RLS) │
|
|
│ All queries filtered by tenant_id │
|
|
│ Users can only access their tenant │
|
|
└─────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ PostgreSQL Database │
|
|
│ • RLS policies on all tenant tables │
|
|
│ • Separate schemas optional │
|
|
│ • Encrypted at rest (AES-256) │
|
|
└─────────────────────────────────────────┘
|
|
```
|
|
|
|
### Authorization (RBAC)
|
|
```python
|
|
# Permission hierarchy
|
|
permissions = {
|
|
"users": ["create", "read", "update", "delete", "impersonate"],
|
|
"tenants": ["create", "read", "update", "delete", "manage"],
|
|
"roles": ["create", "read", "update", "delete", "assign"],
|
|
"api_keys": ["create", "read", "revoke", "rotate"],
|
|
"billing": ["read", "manage", "invoices", "subscribe"],
|
|
"audit_logs": ["read", "export"],
|
|
"reports": ["create", "read", "run", "export", "delete"],
|
|
"plugins": ["install", "configure", "uninstall", "enable"],
|
|
"settings": ["read", "update"]
|
|
}
|
|
|
|
# Role examples
|
|
roles = {
|
|
"superadmin": "*", # All permissions
|
|
"admin": ["users.*", "roles.read", "billing.read", "settings.*"],
|
|
"editor": ["users.read", "users.update", "reports.*"],
|
|
"viewer": ["*.read"],
|
|
"api": ["api_keys.read", "api_keys.create"] # Service account
|
|
}
|
|
```
|
|
|
|
### API Key Security
|
|
```python
|
|
# Key format: lv_<prefix>_<random>
|
|
# Example: lv_prod_abc123def456
|
|
|
|
# Storage: Only hash is stored
|
|
key_hash = argon2.hash(full_key)
|
|
key_prefix = full_key[:8] # For identification
|
|
|
|
# Scopes restrict access
|
|
scopes = ["read:users", "write:reports"]
|
|
|
|
# Rate limiting per key
|
|
rate_limit = 1000 # requests per hour
|
|
```
|
|
|
|
### Audit Logging
|
|
```python
|
|
# All actions logged automatically
|
|
@audit_log(action="user.update", resource_type="user")
|
|
async def update_user(user_id: UUID, data: UserUpdate):
|
|
# ... update logic
|
|
# Automatically logged with:
|
|
# - who (user_id)
|
|
# - what (action)
|
|
# - when (timestamp)
|
|
# - where (IP, user_agent)
|
|
# - changes (before/after diff)
|
|
```
|
|
|
|
### Data Protection
|
|
```
|
|
1. Encryption at Rest
|
|
- PostgreSQL: AES-256 (AWS RDS encryption / LUKS)
|
|
- Backups: Encrypted with tenant-specific keys
|
|
|
|
2. Encryption in Transit
|
|
- TLS 1.3 for all API communications
|
|
- mTLS for service-to-service
|
|
|
|
3. Secrets Management
|
|
- AWS Secrets Manager / HashiCorp Vault
|
|
- API keys: Hashed with Argon2id
|
|
- JWT: RS256 with key rotation
|
|
|
|
4. PII Handling
|
|
- Email encryption for sensitive tenants
|
|
- GDPR-compliant data export/deletion
|
|
- Automatic PII redaction in logs
|
|
```
|
|
|
|
### Security Headers
|
|
```
|
|
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
X-Content-Type-Options: nosniff
|
|
X-Frame-Options: DENY
|
|
Content-Security-Policy: default-src 'self'
|
|
X-XSS-Protection: 1; mode=block
|
|
Referrer-Policy: strict-origin-when-cross-origin
|
|
Permissions-Policy: geolocation=(), microphone=(), camera=()
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Project Structure
|
|
|
|
```
|
|
landvex-admin/
|
|
├── backend/
|
|
│ ├── app/
|
|
│ │ ├── __init__.py
|
|
│ │ ├── main.py # FastAPI app entry
|
|
│ │ ├── config.py # Settings & env vars
|
|
│ │ ├── database.py # DB connection & session
|
|
│ │ ├── models/ # SQLAlchemy models
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ ├── tenant.py
|
|
│ │ │ ├── user.py
|
|
│ │ │ ├── role.py
|
|
│ │ │ ├── api_key.py
|
|
│ │ │ ├── billing.py
|
|
│ │ │ ├── audit.py
|
|
│ │ │ ├── report.py
|
|
│ │ │ └── plugin.py
|
|
│ │ ├── schemas/ # Pydantic models
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ ├── tenant.py
|
|
│ │ │ ├── user.py
|
|
│ │ │ └── ...
|
|
│ │ ├── api/
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ ├── deps.py # Dependencies (auth, tenant)
|
|
│ │ │ ├── v1/
|
|
│ │ │ │ ├── __init__.py
|
|
│ │ │ │ ├── auth.py
|
|
│ │ │ │ ├── tenants.py
|
|
│ │ │ │ ├── users.py
|
|
│ │ │ │ ├── roles.py
|
|
│ │ │ │ ├── api_keys.py
|
|
│ │ │ │ ├── billing.py
|
|
│ │ │ │ ├── audit.py
|
|
│ │ │ │ ├── reports.py
|
|
│ │ │ │ ├── plugins.py
|
|
│ │ │ │ └── dashboard.py
|
|
│ │ ├── services/ # Business logic
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ ├── auth_service.py
|
|
│ │ │ ├── tenant_service.py
|
|
│ │ │ ├── user_service.py
|
|
│ │ │ ├── billing_service.py
|
|
│ │ │ └── audit_service.py
|
|
│ │ ├── core/
|
|
│ │ │ ├── __init__.py
|
|
│ │ │ ├── security.py # Password, JWT, crypto
|
|
│ │ │ ├── permissions.py # RBAC logic
|
|
│ │ │ ├── tenant_resolver.py # Tenant isolation
|
|
│ │ │ └── exceptions.py # Custom exceptions
|
|
│ │ └── utils/
|
|
│ │ ├── __init__.py
|
|
│ │ ├── pagination.py
|
|
│ │ └── validators.py
|
|
│ ├── alembic/ # Database migrations
|
|
│ ├── tests/
|
|
│ ├── Dockerfile
|
|
│ ├── requirements.txt
|
|
│ └── pyproject.toml
|
|
│
|
|
├── frontend/
|
|
│ ├── src/
|
|
│ │ ├── main.tsx # Entry point
|
|
│ │ ├── App.tsx
|
|
│ │ ├── components/
|
|
│ │ │ ├── layout/
|
|
│ │ │ │ ├── Sidebar.tsx
|
|
│ │ │ │ ├── TopBar.tsx
|
|
│ │ │ │ └── Layout.tsx
|
|
│ │ │ ├── ui/ # Reusable UI components
|
|
│ │ │ │ ├── DataTable.tsx
|
|
│ │ │ │ ├── Modal.tsx
|
|
│ │ │ │ ├── Form.tsx
|
|
│ │ │ │ └── Charts.tsx
|
|
│ │ │ └── features/
|
|
│ │ │ ├── tenants/
|
|
│ │ │ ├── users/
|
|
│ │ │ ├── billing/
|
|
│ │ │ └── reports/
|
|
│ │ ├── pages/
|
|
│ │ │ ├── Dashboard.tsx
|
|
│ │ │ ├── Tenants/
|
|
│ │ │ │ ├── List.tsx
|
|
│ │ │ │ └── Detail.tsx
|
|
│ │ │ ├── Users/
|
|
│ │ │ ├── Billing/
|
|
│ │ │ └── Settings/
|
|
│ │ ├── hooks/
|
|
│ │ │ ├── useAuth.ts
|
|
│ │ │ ├── useTenant.ts
|
|
│ │ │ ├── useApi.ts
|
|
│ │ │ └── usePermissions.ts
|
|
│ │ ├── stores/
|
|
│ │ │ ├── authStore.ts # Zustand/Jotai
|
|
│ │ │ ├── tenantStore.ts
|
|
│ │ │ └── uiStore.ts
|
|
│ │ ├── lib/
|
|
│ │ │ ├── api.ts # API client (axios/fetch)
|
|
│ │ │ ├── utils.ts
|
|
│ │ │ └── constants.ts
|
|
│ │ └── types/
|
|
│ │ └── index.ts
|
|
│ ├── public/
|
|
│ ├── index.html
|
|
│ ├── package.json
|
|
│ ├── tsconfig.json
|
|
│ ├── vite.config.ts
|
|
│ └── tailwind.config.js
|
|
│
|
|
├── docker-compose.yml
|
|
├── Makefile
|
|
└── README.md
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Key Implementation Details
|
|
|
|
### FastAPI Dependencies (Tenant Isolation)
|
|
```python
|
|
# app/api/deps.py
|
|
from fastapi import Depends, HTTPException, Header
|
|
from fastapi.security import HTTPBearer
|
|
|
|
security = HTTPBearer()
|
|
|
|
async def get_current_user(token: str = Depends(security)):
|
|
"""Validate JWT and return user"""
|
|
pass
|
|
|
|
async def get_current_tenant(
|
|
x_tenant_id: UUID = Header(...),
|
|
user: User = Depends(get_current_user)
|
|
):
|
|
"""Validate user belongs to tenant"""
|
|
if not user.has_access_to(x_tenant_id):
|
|
raise HTTPException(403, "Access denied to tenant")
|
|
return x_tenant_id
|
|
|
|
async def require_permission(permission: str):
|
|
"""Dependency factory for permission checking"""
|
|
async def checker(user: User = Depends(get_current_user)):
|
|
if not user.has_permission(permission):
|
|
raise HTTPException(403, f"Missing permission: {permission}")
|
|
return checker
|
|
|
|
# Usage:
|
|
@router.get("/users", dependencies=[Depends(require_permission("users.read"))])
|
|
async def list_users(tenant_id: UUID = Depends(get_current_tenant)):
|
|
pass
|
|
```
|
|
|
|
### React 19 + Server Components Pattern
|
|
```tsx
|
|
// Using React 19 features
|
|
// Server Components for data fetching
|
|
async function TenantListPage() {
|
|
const tenants = await api.tenants.list(); // Server-side fetch
|
|
|
|
return (
|
|
<DataTable
|
|
data={tenants}
|
|
columns={tenantColumns}
|
|
// React 19: actions for mutations
|
|
onDelete={deleteTenantAction}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Client Components for interactivity
|
|
'use client';
|
|
function TenantActions({ tenantId }: { tenantId: string }) {
|
|
const { hasPermission } = usePermissions();
|
|
|
|
return (
|
|
<div>
|
|
{hasPermission('tenants.update') && (
|
|
<Button onClick={() => editTenant(tenantId)}>Edit</Button>
|
|
)}
|
|
{hasPermission('tenants.delete') && (
|
|
<Button variant="danger" onClick={() => deleteTenant(tenantId)}>
|
|
Delete
|
|
</Button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Audit Log Middleware
|
|
```python
|
|
# Automatically log all state-changing operations
|
|
@app.middleware("http")
|
|
async def audit_middleware(request: Request, call_next):
|
|
response = await call_next(request)
|
|
|
|
if request.method in ["POST", "PUT", "PATCH", "DELETE"]:
|
|
await audit_service.log(
|
|
action=f"{request.method.lower()}.{request.url.path}",
|
|
user=get_current_user_from_request(request),
|
|
tenant=get_tenant_from_request(request),
|
|
ip=request.client.host,
|
|
user_agent=request.headers.get("user-agent"),
|
|
details={"path": str(request.url), "status": response.status_code}
|
|
)
|
|
|
|
return response
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Deployment Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ CDN (CloudFront) │
|
|
│ Static assets, DDoS protection │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Load Balancer (ALB) │
|
|
│ SSL termination, health checks │
|
|
└─────────────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────┼───────────────┐
|
|
▼ ▼ ▼
|
|
┌────────────┐ ┌────────────┐ ┌────────────┐
|
|
│ Frontend │ │ Backend │ │ Backend │
|
|
│ (React) │ │ (FastAPI) │ │ (FastAPI) │
|
|
│ Container │ │ Container │ │ Container │
|
|
└────────────┘ └────────────┘ └────────────┘
|
|
│ │ │
|
|
└───────────────┼───────────────┘
|
|
▼
|
|
┌─────────────────────────────────────────────────┐
|
|
│ PostgreSQL (RDS/Aurora) │
|
|
│ Primary → Replica (Multi-AZ) │
|
|
│ Automated backups │
|
|
└─────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────┼───────────────┐
|
|
▼ ▼ ▼
|
|
┌────────────┐ ┌────────────┐ ┌────────────┐
|
|
│ Redis │ │ S3 │ │ Stripe │
|
|
│ (ElastiCache)│ (Backups) │ │ (Billing) │
|
|
│ Sessions │ │ Reports │ │ │
|
|
│ Cache │ │ Exports │ │ │
|
|
└────────────┘ └────────────┘ └────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Summary
|
|
|
|
| Component | Technology | Purpose |
|
|
|-----------|-----------|---------|
|
|
| Backend API | FastAPI (Python) | REST API, async request handling |
|
|
| Database | PostgreSQL 15+ | Primary data store with RLS |
|
|
| Cache | Redis | Sessions, rate limiting, cache |
|
|
| Frontend | React 19 + TypeScript | Admin UI with modern patterns |
|
|
| Styling | Tailwind CSS | Utility-first CSS |
|
|
| State | Zustand/Jotai | Client state management |
|
|
| Auth | JWT + Argon2id | Token-based authentication |
|
|
| Payments | Stripe | Subscription billing |
|
|
| Monitoring | Prometheus + Grafana | Metrics and alerting |
|
|
| Logs | ELK Stack / CloudWatch | Centralized logging |
|
|
|
|
This design provides a secure, scalable, and maintainable admin backend for the LandveX Enterprise Platform with full multi-tenant isolation, comprehensive audit logging, and flexible plugin architecture.
|