bmc_hub/docs/MODULE_SYSTEM.md

537 lines
12 KiB
Markdown
Raw Normal View History

# BMC Hub Module System
## Oversigt
BMC Hub har nu et dynamisk modul-system der tillader isoleret udvikling af features uden at påvirke core systemet. Moduler kan udvikles, testes og deployes uafhængigt.
## Arkitektur
### Core vs Modules
**Core System** (forbliver i `app/`):
- `auth/` - Authentication
- `customers/` - Customer management
- `hardware/` - Hardware tracking
- `billing/` - Billing integration
- `contacts/` - Contact management
- `vendors/` - Vendor management
- `settings/` - Settings
- `system/` - System utilities
- `dashboard/` - Dashboard
- `devportal/` - Developer portal
- `timetracking/` - Time tracking
- `emails/` - Email system
**Dynamic Modules** (nye features i `app/modules/`):
- Isolerede i egen directory
- Dynamisk loaded ved startup
- Hot-reload support (restart påkrævet)
- Egen database namespace (table prefix)
- Egen konfiguration (miljøvariable)
- Egne migrations
### File Struktur
```
app/modules/
├── _template/ # Template for nye moduler (IKKE loaded)
│ ├── module.json # Metadata og config
│ ├── README.md
│ ├── backend/
│ │ ├── __init__.py
│ │ └── router.py # FastAPI endpoints
│ ├── frontend/
│ │ ├── __init__.py
│ │ └── views.py # HTML views
│ ├── templates/
│ │ └── index.html # Jinja2 templates
│ └── migrations/
│ └── 001_init.sql # Database migrations
└── my_module/ # Eksempel modul
├── module.json
├── ...
```
## Opret Nyt Modul
### Via CLI Tool
```bash
python scripts/create_module.py my_feature "My awesome feature"
```
Dette opretter:
- Komplet modul struktur
- Opdateret `module.json` med modul navn
- Placeholder kode i router og views
- Database migration template
### Manuel Oprettelse
1. **Kopiér template:**
```bash
cp -r app/modules/_template app/modules/my_module
```
2. **Rediger `module.json`:**
```json
{
"name": "my_module",
"version": "1.0.0",
"description": "Min feature beskrivelse",
"author": "Dit navn",
"enabled": false,
"dependencies": [],
"table_prefix": "mymod_",
"api_prefix": "/api/v1/mymod",
"tags": ["My Module"]
}
```
3. **Opdater kode:**
- `backend/router.py` - Implementer dine endpoints
- `frontend/views.py` - Implementer HTML views
- `templates/index.html` - Design UI
- `migrations/001_init.sql` - Opret database tabeller
## Database Isolering
### Customer Linking Pattern
**VIGTIG ARKITEKTUR BESLUTNING:**
Core `customers` tabel er **single source of truth** for økonomi, fakturering og CVR.
#### Hvornår skal moduler have egen kunde-tabel?
**✅ Opret modul-specifik kunde-tabel hvis:**
- Modulet syncer fra eksternt system (vTiger, CRM, etc.)
- Mange module-specifikke felter nødvendige (external_id, sync_status, etc.)
- Custom lifecycle/workflow
- **Eksempel:** `tmodule_customers` (sync fra vTiger)
**🚫 Brug direkte `customers.id` foreign key hvis:**
- Simple relationer uden external sync
- Få/ingen custom felter
- Standard workflow
- **Eksempel:** `ticket_system` → direkte FK til `customers.id`
#### Hvis modul-kunde-tabel oprettes:
**SKAL altid have:**
```sql
hub_customer_id INTEGER REFERENCES customers(id) -- Link til core
```
**SKAL have auto-link trigger:**
```sql
CREATE OR REPLACE FUNCTION auto_link_{module}_customer()
RETURNS TRIGGER AS $$
DECLARE
matched_hub_id INTEGER;
BEGIN
IF NEW.hub_customer_id IS NOT NULL THEN
RETURN NEW;
END IF;
SELECT id INTO matched_hub_id
FROM customers
WHERE LOWER(TRIM(name)) = LOWER(TRIM(NEW.name))
LIMIT 1;
IF matched_hub_id IS NOT NULL THEN
NEW.hub_customer_id := matched_hub_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_auto_link_{module}_customer
BEFORE INSERT OR UPDATE OF name
ON {module}_customers
FOR EACH ROW
EXECUTE FUNCTION auto_link_{module}_customer();
```
**Hvorfor dette pattern?**
- ✅ E-conomic export virker automatisk (via hub_customer_id → customers.economic_customer_number)
- ✅ Billing integration automatisk
- ✅ Ingen manuel linking nødvendig
- ✅ Nye sync'ede kunder linkes automatisk
Se `migrations/028_auto_link_tmodule_customers.sql` for live eksempel.
### Table Prefix Pattern
Alle tabeller SKAL bruge prefix fra `module.json`:
```sql
-- BAD: Risiko for kollision
CREATE TABLE customers (...);
-- GOOD: Isoleret med prefix
CREATE TABLE mymod_customers (...);
```
### Database Queries
Brug ALTID helper functions:
```python
from app.core.database import execute_query, execute_insert
# Hent data
customers = execute_query(
"SELECT * FROM mymod_customers WHERE active = %s",
(True,)
)
# Insert med auto-returned ID
customer_id = execute_insert(
"INSERT INTO mymod_customers (name) VALUES (%s)",
("Test",)
)
```
## Konfiguration
### Environment Variables
Modul-specifik config følger pattern: `MODULES__{MODULE_NAME}__{KEY}`
**Eksempel `.env`:**
```bash
# Global module system
MODULES_ENABLED=true
MODULES_AUTO_RELOAD=true
# Specific module config
MODULES__MY_MODULE__API_KEY=secret123
MODULES__MY_MODULE__READ_ONLY=false
MODULES__MY_MODULE__DRY_RUN=false
```
### I Kode
```python
from app.core.config import get_module_config
# Hent config med fallback
api_key = get_module_config("my_module", "API_KEY")
read_only = get_module_config("my_module", "READ_ONLY", "false") == "true"
```
## Safety Switches
### Best Practice: Safety First
Alle moduler BØR have safety switches:
```python
# I backend/router.py
read_only = get_module_config("my_module", "READ_ONLY", "true") == "true"
dry_run = get_module_config("my_module", "DRY_RUN", "true") == "true"
if read_only:
return {"success": False, "message": "READ_ONLY mode"}
if dry_run:
logger.info("🧪 DRY_RUN: Would perform action")
return {"success": True, "dry_run": True}
```
**Anbefalet defaults:**
- `READ_ONLY=true` - Bloker alle writes indtil eksplicit enabled
- `DRY_RUN=true` - Log actions uden at udføre
## Enable/Disable Moduler
### Via API
```bash
# Enable
curl -X POST http://localhost:8000/api/v1/modules/my_module/enable
# Disable
curl -X POST http://localhost:8000/api/v1/modules/my_module/disable
# List alle
curl http://localhost:8000/api/v1/modules
```
### Manuelt
Rediger `app/modules/my_module/module.json`:
```json
{
"enabled": true
}
```
**Vigtigt:** Kræver app restart!
```bash
docker-compose restart api
```
## Migrations
### Kør Migration
```bash
# Via psql
psql -U bmc_hub -d bmc_hub -f app/modules/my_module/migrations/001_init.sql
# Via Python
python apply_migration.py my_module 001_init.sql
```
### Migration Best Practices
1. **Nummering:** Sekventiel (001, 002, 003...)
2. **Idempotent:** Brug `CREATE TABLE IF NOT EXISTS`
3. **Table prefix:** Alle tabeller med prefix
4. **Rollback:** Inkluder rollback instructions i kommentar
**Eksempel:**
```sql
-- Migration: 001_init.sql
-- Rollback: DROP TABLE mymod_items;
CREATE TABLE IF NOT EXISTS mymod_items (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
## Development Workflow
### 1. Opret Modul
```bash
python scripts/create_module.py invoice_parser "Parse invoice PDFs"
```
### 2. Implementer Features
Rediger:
- `backend/router.py` - API endpoints
- `frontend/views.py` - HTML pages
- `templates/*.html` - UI components
### 3. Kør Migration
```bash
psql -U bmc_hub -d bmc_hub -f app/modules/invoice_parser/migrations/001_init.sql
```
### 4. Enable Modul
```json
// module.json
{
"enabled": true
}
```
### 5. Restart & Test
```bash
docker-compose restart api
# Test API
curl http://localhost:8000/api/v1/invoice_parser/health
# Test UI
open http://localhost:8000/invoice_parser
```
## Hot Reload
Systemet understøtter hot-reload i development mode:
```bash
# I docker-compose.yml
environment:
- ENABLE_RELOAD=true
# Source code mounted
volumes:
- ./app:/app/app:ro
```
**Når skal jeg genstarte?**
-**IKKE nødvendigt:** Python kode ændringer i eksisterende filer
-**Restart påkrævet:** Enable/disable modul, nye filer, module.json ændringer
## Fejlhåndtering
### Startup Errors
Hvis et modul fejler under loading:
- Core systemet fortsætter
- Modulet bliver ikke loaded
- Fejl logges til console + `logs/app.log`
**Log output:**
```
📦 Fundet modul: my_module v1.0.0 (enabled=true)
❌ Kunne ikke loade modul my_module: ModuleNotFoundError
✅ Loaded 2 modules: ['other_module', 'third_module']
```
### Runtime Errors
Endpoints i moduler er isolerede:
- Exception i ét modul påvirker ikke andre
- FastAPI returner 500 med error message
- Logger fejl med module context
## API Documentation
Alle modul endpoints vises automatisk i FastAPI docs:
```
http://localhost:8000/api/docs
```
Endpoints grupperes under modul tags fra `module.json`.
## Testing
### Unit Tests
```python
# tests/modules/test_my_module.py
import pytest
from app.core.database import execute_query
def test_my_module_item_creation():
result = execute_query(
"SELECT * FROM mymod_items WHERE name = %s",
("Test",),
fetchone=True
)
assert result is not None
```
### Integration Tests
Brug samme test database som core:
```python
@pytest.fixture
def test_db():
# Setup test data
yield
# Cleanup
```
## Eksempel Modul
Se `app/modules/_template/` for komplet working example.
**Key files:**
- `backend/router.py` - CRUD endpoints med safety switches
- `frontend/views.py` - HTML view med Jinja2
- `migrations/001_init.sql` - Table creation med prefix
- `module.json` - Metadata og config
## Troubleshooting
### Modul loader ikke
1. **Check `enabled` flag:**
```bash
cat app/modules/my_module/module.json | grep enabled
```
2. **Check logs:**
```bash
docker-compose logs -f api | grep my_module
```
3. **Verify dependencies:**
Hvis modul har dependencies i `module.json`, check at de er loaded først.
### Database fejl
1. **Check table prefix:**
```sql
SELECT tablename FROM pg_tables WHERE tablename LIKE 'mymod_%';
```
2. **Verify migration:**
```bash
psql -U bmc_hub -d bmc_hub -c "\d mymod_items"
```
### Import fejl
Sørg for at `__init__.py` findes i alle directories:
```bash
touch app/modules/my_module/backend/__init__.py
touch app/modules/my_module/frontend/__init__.py
```
## Best Practices
### ✅ DO
- Brug table prefix konsistent
- Implementer safety switches (READ_ONLY, DRY_RUN)
- Log alle actions med emoji prefix
- Brug `execute_query()` helpers
- Dokumenter API endpoints i docstrings
- Version moduler semantisk (1.0.0 → 1.1.0)
- Test isoleret før enable
### ❌ DON'T
- Tilgå andre modulers tabeller direkte
- Hardcode credentials i kode
- Skip migration versioning
- Glem table prefix
- Disable safety switches uden grund
- Commit `.env` files
## Migration Path
### Eksisterende Features → Moduler?
**Beslutning:** Core features forbliver i `app/` structure.
**Rationale:**
- Proven patterns
- Stabil foundation
- Ingen gevinst ved migration
- Risk af breakage
**Nye features:** Brug modul-system fra dag 1.
## Fremtidig Udvidelse
### Potentielle Features
1. **Hot reload uden restart** - inotify watching af module.json
2. **Module marketplace** - External repository
3. **Dependency resolution** - Automatisk enable dependencies
4. **Version constraints** - Min/max BMC Hub version
5. **Module API versioning** - Breaking changes support
6. **Rollback support** - Automatic migration rollback
7. **Module metrics** - Usage tracking per module
8. **Module permissions** - RBAC per module
## Support
**Issues:** Se logs i `logs/app.log`
**Documentation:** Se `app/modules/_template/README.md`
**Examples:** Se template implementation
---
**Status:** ✅ Klar til brug (13. december 2025)