Ce dépôt contient un socle Django 6 ASGI (servi par Granian) pour exposer une plateforme d’hébergement modulaire reliée au dépôt GitHub dealiot/smartappli et pilotée via Apache APISIX.
- Découper l’hébergement en modules activables (
apps.hosting.Module). - Synchroniser l’état applicatif avec GitHub (
/api/gateway/github/sync/). - Publier dynamiquement les routes APISIX (
/api/gateway/apisix/publish/). - Recevoir un webhook GitHub et déclencher un routage (
/api/gateway/github/webhook/).
GitHub (dealiot/smartappli)
│
│ webhook / API
▼
Django 6 ASGI + Granian (dealhost)
├── apps.hosting -> registre de modules déployables
└── apps.gateway -> orchestration GitHub + APISIX
│
│ Admin API
▼
Apache APISIX
│
▼
Upstream modules (containers/services Django)
│
└── Valkey (cache Redis)
dealhost/settings/: configuration modulaire (base,dev,prod,env).apps/hosting/: domaine hébergement (modèles, API REST des modules).apps/gateway/: services d’intégration GitHub + APISIX et endpoints d’orchestration.infra/apisix/: exemple de route APISIX standalone.
GET /api/gateway/health/: état du service gateway.POST /api/gateway/github/sync/: récupère le dernier commit d’une branche.POST /api/gateway/apisix/publish/: crée/met à jour une route APISIX.POST /api/gateway/github/webhook/: webhook signé GitHub -> publication de route.GET/POST /api/hosting/modules/: CRUD des modules hébergés.GET/POST /api/hosting/tools/: CRUD des outils (chaque outil peut lier plusieurs modules).GET/POST /api/hosting/applications/: CRUD des applications hébergées (chaque application peut lier plusieurs modules).POST /api/hosting/autodiscover/: auto découverte depuis les manifests tools/apps.GET /hosting/manage/: interface de gestion (modules, tools, datasets accessibles à l'utilisateur connecté, applications + auto découverte).POST /i18n/setlang/: changement de langue de l’interface de gestion.GET/POST /api/iam/users/: gestion des utilisateurs (avec groupes/permissions + endpointset-password).GET/POST /api/iam/groups/: gestion des groupes (rôles) et permissions associées.GET /api/iam/permissions/: catalogue des permissions Django.GET /iam/manage/: interface IAM (utilisateurs, groupes, permissions).
Un SDK R minimal est disponible dans sdk/r/dealhostR pour piloter l’API hosting.
Fonctions exposées :
dealhost_client(base_url, token)create_tool(...),update_tool(...),list_tools(...)create_application(...),update_application(...),list_applications(...)
Exemple rapide :
# install.packages(c("httr2", "jsonlite"))
source("sdk/r/dealhostR/R/client.R")
client <- dealhost_client("http://localhost:8000", token = "YOUR_TOKEN")
create_tool(
client,
name = "Backoffice",
slug = "backoffice",
description = "Outil d'administration",
module_ids = c(1, 2),
enabled = TRUE
)
create_application(
client,
name = "Storefront",
slug = "storefront",
description = "Application e-commerce",
module_ids = c(1),
enabled = TRUE
)Le SDK Python est disponible dans sdk/python.
Exemple rapide :
from dealhost_sdk import DealHostClient
client = DealHostClient("http://localhost:8000", token="YOUR_TOKEN")
client.create_tool(
name="Backoffice",
slug="backoffice",
description="Outil d'administration",
module_ids=[1, 2],
enabled=True,
)
client.create_application(
name="Storefront",
slug="storefront",
description="Application e-commerce",
module_ids=[1],
enabled=True,
)Le SDK Rust est disponible dans sdk/rust/dealhost-sdk.
Exemple rapide :
use dealhost_sdk::DealHostClient;
fn demo() -> Result<(), reqwest::Error> {
let client = DealHostClient::new("http://localhost:8000", Some("YOUR_TOKEN".to_string()));
client.create_tool("Backoffice", "backoffice", "Outil d'administration", vec![1, 2], true)?;
client.create_application("Storefront", "storefront", "Application e-commerce", vec![1], true)?;
Ok(())
}- Les manifests de découverte sont lus depuis:
manifests/tools/*.jsonmanifests/applications/*.json
- Champs attendus:
name,slug,description(optionnel),enabled(optionnel),module_slugs(optionnel),version(optionnel, semver),version_notes(optionnel). - L’auto découverte crée/met à jour automatiquement les objets
TooletHostedApplication, synchronise leurs liens modules, et enregistre l'historique des versions quandversionest fourni.
- L’interface
/hosting/manage/est traduisible et propose un sélecteur de langue. - Langues officielles FAO supportées : arabe, chinois (simplifié), anglais, français, russe, espagnol.
- Fichiers de traduction :
locale/<lang>/LC_MESSAGES/django.po.
- Chaque
TooletHostedApplicationexpose:current_version(version active),released_at(date de publication),- un historique de versions (
versions).
- Endpoints de versionning:
GET /api/hosting/tools/{id}/versions/POST /api/hosting/tools/{id}/versions/avec{ "version": "1.2.3", "notes": "...", "source": "manual" }GET /api/hosting/applications/{id}/versions/POST /api/hosting/applications/{id}/versions/
- Filtre de liste disponible:
?current_version=<semver>.
- Filtres disponibles sur les listes:
?enabled=true|false?module_slug=<slug>?search=<texte>(nom, slug, description, slug module)
- Actions dédiées:
POST /api/hosting/tools/{id}/attach-module/avec{ "module_id": <id> }POST /api/hosting/tools/{id}/detach-module/avec{ "module_id": <id> }GET /api/hosting/tools/{id}/modules/POST /api/hosting/applications/{id}/attach-module/avec{ "module_id": <id> }POST /api/hosting/applications/{id}/detach-module/avec{ "module_id": <id> }GET /api/hosting/applications/{id}/modules/
- Copier les variables d’environnement :
cp .env.example .env
- Lancer la stack :
docker compose up
- API servie en ASGI par Granian sur
http://localhost:8000.- le conteneur applique
migrate+collectstaticau démarrage ; valkeyest démarré avec healthcheck + volume persistant ;- APISIX attend que l’API Django soit healthy avant exposition.
- le conteneur applique
- Remplacer toutes les valeurs
replace-me/ placeholders. - Restreindre
ALLOWED_HOSTSet exposer uniquement APISIX en edge. - Protéger le webhook GitHub avec
GITHUB_WEBHOOK_SECRET. - Externaliser SQLite vers PostgreSQL en environnement de production.
- Sessions en backend
cached_db(persistance DB + cache Valkey pour performance).
- Entrée applicative:
dealhost.asgi:application. - Serveur applicatif:
granian --interface asgi dealhost.asgi:application. - Le projet est ASGI-only et ne contient pas d’entrée WSGI.
SESSION_ENGINE=django.contrib.sessions.backends.cached_db: sessions persistées en base Django.CACHES["default"]pointe vers Valkey viaVALKEY_URL(ex:redis://valkey:6379/1).ServeStaticest activé dans le middleware Django et via le storageCompressedManifestStaticFilesStoragepour servir les assets statiques en ASGI.
CI Django DEALHost(.github/workflows/ci.yml) : exécute une matrice multi-plateforme (Linux/macOS/Windows) et multi-versions Python (3.12 à 3.14). Le projet cible Python >=3.12 : les jobs 3.12/3.13/3.14 installent d'abordrequirements.txtpuis le package (uv pip install --system -r requirements.txtpuisuv pip install --system -e .), vérifient les migrations, exécutent les tests unitaires sous couverture (uv run coverage run --source=apps,dealhost,sdk manage.py test tests --verbosity 2), exportentcoverage.xmlet lancent le contrôle de compilation.SonarCloud(.github/workflows/sonarcloud.yml) : exécute les tests avec couverture sur Ubuntu + Python 3.12 puis lance l'analyse SonarCloud (SonarSource/sonarqube-scan-action@v6) à partir du fichiersonar-project.properties.Validate APISIX Routes(.github/workflows/apisix-routes-validate.yml) : valide la syntaxe JSON des routes APISIX et vérifie la présence d'une route coeurmodule-core.Pre-commit(.github/workflows/pre-commit.yml) : installepre-commitviauvpuis exécuteuv run pre-commit run --all-files --show-diff-on-failure(incluant Ruff en mode--select ALLetruff-format).
Dependabotest configuré via.github/dependabot.ymlpour surveiller chaque semaine les dépendances Python et GitHub Actions.Renovateest configuré viarenovate.jsonavec preset recommandé, regroupement des updates mineures/patch, et label spécifique pour les majors.
- Le dashboard
/hosting/manage/est protégé (utilisateur connecté requis). - Les datasets affichés sont filtrés pour l'utilisateur connecté :
- accès direct (
dataset.users) - accès via groupes (
dataset.groups) - uniquement
enabled=true.
- accès direct (
- Un superutilisateur voit tous les datasets actifs.