Base de données & migrations¶
L'ORM utilisé est SQLModel (Pydantic v2 + SQLAlchemy), avec un accès 100% asynchrone (database.py). Les migrations de schéma sont gérées par Alembic.
Modèles (backend/app/models.py)¶
erDiagram
User ||--o{ UserRoleLink : a
Role ||--o{ UserRoleLink : a
User ||--o{ PersonalAccessToken : possede
WireGuardServer ||--o{ WireGuardClient : "sert de referentiel a"
User {
int id PK
string username UK
string email UK
string hashed_password
string auth_source "local | oidc"
bool active
string fs_uniquifier UK
}
Role {
int id PK
string name UK
string permissions "CSV"
}
WireGuardServer {
int id PK
string address
int listen_port
string private_key
string public_key
text postup
text postdown
}
WireGuardClient {
int id PK
string name UK
string email UK
string public_key
string private_key
string allocated_ips
json allowed_ips
bool use_server_dns
bool enabled
}
OidcSettings {
int id PK
bool enabled
bool oidc_only
string issuer
string client_id
string client_secret
}
SmtpSettings {
int id PK
string server
int port
string username
bool tls
bool ssl
}
AuditLog {
int id PK
datetime created_at
string action
string actor_username
string target
bool success
}
PersonalAccessToken {
int id PK
int user_id FK
string token_prefix
string token_hash UK
datetime expires_at
bool revoked
}
GlobalSettings {
int id PK
string endpoint_address
json dns_servers
int mtu
int persistent_keepalive
bool maintenance_mode
}
Points notables¶
User:auth_sourcedistingue les compteslocal(mot de passe géré par l'app) des comptesoidc(délégués à l'IdP — email/nom/mot de passe non modifiables depuis l'UI, voirusers.py).fs_uniquifierest un identifiant stable indépendant de l'id, utile pour invalider une session sans dépendre de la clé primaire.Role: les permissions sont stockées en CSV dans une colonne texte ("admin-read,admin-write"), lues viaRole.has_permission().WireGuardServer/WireGuardClient: une seule ligne serveur (référentiel de l'interfacewg0), plusieurs lignes clients (un pair = une ligne).allowed_ipsetdns_servers(surGlobalSettings) sont stockés en JSON natif.OidcSettings,SmtpSettings,GlobalSettings: tables singleton (une seule ligne), portant chacune un domaine de configuration distinct — historiquement fusionnées, elles ont été séparées par la migration0002_split_settings_tables.AuditLog: table append-only (jamais modifiée après insertion), avec purge automatique selonAUDIT_MAX_EVENTS/AUDIT_RETENTION_DAYS— voirservices/audit.py.PersonalAccessToken: seul le hash SHA-256 du token est stocké (token_hash), jamais le token en clair — celui-ci n'est renvoyé qu'une fois, à la création.
Migrations Alembic (backend/alembic/)¶
| Révision | Contenu |
|---|---|
0001_initial_schema |
Schéma initial : users, roles, user_roles, wg_server, wg_clients, réglages initiaux. |
0002_split_settings_tables |
Séparation des réglages en tables dédiées (oidc_settings, smtp_settings, global_settings). |
003_auth_source |
Ajout de la colonne auth_source sur users (support OIDC). |
0004_audit_and_pat |
Ajout des tables audit_log et personal_access_tokens. |
alembic/env.py¶
Particularités de cette configuration :
- Ajoute
backend/àsys.pathpour quefrom app.config import app_settingsfonctionne quel que soit le répertoire courant (le conteneur Docker lanceuvicorndepuis/app, pas depuis/app/backend). - Réutilise la même URL de base de données que l'application (
app_settings.db_path), en la convertissant du driver asynchrone vers un driver synchrone (sqlite+aiosqlite://→sqlite://) car Alembic fonctionne en mode synchrone (create_engine+NullPool). target_metadata = SQLModel.metadata: Alembic peut donc générer des migrations automatiquement (alembic revision --autogenerate) à partir des modèles définis dansmodels.py.
Appliquer les migrations¶
- En développement local, cette commande doit être lancée manuellement avant le premier démarrage (voir Installation locale).
- En production (image Docker), elle est exécutée automatiquement à chaque démarrage du conteneur par
docker/entrypoint.sh, avant de lanceruvicorn.
Créer une nouvelle migration¶
Un hook post-écriture (alembic.ini) exécute automatiquement ruff check --fix sur le fichier de migration généré.
Base de données par défaut¶
Par défaut, l'application utilise SQLite (DB_PATH=sqlite+aiosqlite:////var/lib/wireguard-ui/wireguard_ui.db), suffisant pour un déploiement mono-instance. PostgreSQL est également supporté : il suffit de fournir une URL postgres:// ou postgresql://, automatiquement convertie vers le driver asynchrone asyncpg par le validateur normalize_db_path de config.py.