- Added a new column `subscriptions_locked` to the `customers` table to manage subscription access. - Implemented a script to create new modules from a template, including updates to various files (module.json, README.md, router.py, views.py, and migration SQL). - Developed a script to import BMC Office subscriptions from an Excel file into the database, including error handling and statistics reporting. - Created a script to lookup and update missing CVR numbers using the CVR.dk API. - Implemented a script to relink Hub customers to e-conomic customer numbers based on name matching. - Developed scripts to sync CVR numbers from Simply-CRM and vTiger to the local customers database.
9.9 KiB
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/- Authenticationcustomers/- Customer managementhardware/- Hardware trackingbilling/- Billing integrationcontacts/- Contact managementvendors/- Vendor managementsettings/- Settingssystem/- System utilitiesdashboard/- Dashboarddevportal/- Developer portaltimetracking/- Time trackingemails/- 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
python scripts/create_module.py my_feature "My awesome feature"
Dette opretter:
- Komplet modul struktur
- Opdateret
module.jsonmed modul navn - Placeholder kode i router og views
- Database migration template
Manuel Oprettelse
-
Kopiér template:
cp -r app/modules/_template app/modules/my_module -
Rediger
module.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"] } -
Opdater kode:
backend/router.py- Implementer dine endpointsfrontend/views.py- Implementer HTML viewstemplates/index.html- Design UImigrations/001_init.sql- Opret database tabeller
Database Isolering
Table Prefix Pattern
Alle tabeller SKAL bruge prefix fra module.json:
-- BAD: Risiko for kollision
CREATE TABLE customers (...);
-- GOOD: Isoleret med prefix
CREATE TABLE mymod_customers (...);
Database Queries
Brug ALTID helper functions:
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:
# 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
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:
# 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 enabledDRY_RUN=true- Log actions uden at udføre
Enable/Disable Moduler
Via API
# 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:
{
"enabled": true
}
Vigtigt: Kræver app restart!
docker-compose restart api
Migrations
Kør Migration
# 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
- Nummering: Sekventiel (001, 002, 003...)
- Idempotent: Brug
CREATE TABLE IF NOT EXISTS - Table prefix: Alle tabeller med prefix
- Rollback: Inkluder rollback instructions i kommentar
Eksempel:
-- 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
python scripts/create_module.py invoice_parser "Parse invoice PDFs"
2. Implementer Features
Rediger:
backend/router.py- API endpointsfrontend/views.py- HTML pagestemplates/*.html- UI components
3. Kør Migration
psql -U bmc_hub -d bmc_hub -f app/modules/invoice_parser/migrations/001_init.sql
4. Enable Modul
// module.json
{
"enabled": true
}
5. Restart & Test
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:
# 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
# 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:
@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 switchesfrontend/views.py- HTML view med Jinja2migrations/001_init.sql- Table creation med prefixmodule.json- Metadata og config
Troubleshooting
Modul loader ikke
-
Check
enabledflag:cat app/modules/my_module/module.json | grep enabled -
Check logs:
docker-compose logs -f api | grep my_module -
Verify dependencies: Hvis modul har dependencies i
module.json, check at de er loaded først.
Database fejl
-
Check table prefix:
SELECT tablename FROM pg_tables WHERE tablename LIKE 'mymod_%'; -
Verify migration:
psql -U bmc_hub -d bmc_hub -c "\d mymod_items"
Import fejl
Sørg for at __init__.py findes i alle directories:
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
.envfiles
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
- Hot reload uden restart - inotify watching af module.json
- Module marketplace - External repository
- Dependency resolution - Automatisk enable dependencies
- Version constraints - Min/max BMC Hub version
- Module API versioning - Breaking changes support
- Rollback support - Automatic migration rollback
- Module metrics - Usage tracking per module
- 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)