Phase 13 : sogoms-cron
- Jobs planifiés avec schedule cron standard
- Types: query_email, http, service
- Actions: list, trigger, status
Phase 16 : Réorganisation config/apps/{app}/
- Tous les fichiers d'une app dans un seul dossier
- Migration prokov vers nouvelle structure
Phase 17 : sogoms-admin
- Interface web d'administration (Go templates + htmx)
- Auth sessions cookies signées HMAC-SHA256
- Rôles super_admin / app_admin avec permissions
Phase 19 : Création d'app via Admin UI
- Formulaire création app avec config DB/auth
- Bouton "Scanner la base" : introspection + schema.yaml
- Rechargement automatique sogoway via SIGHUP
Infrastructure :
- sogoctl : socket de contrôle /run/sogoctl.sock
- sogoway : reload config sur SIGHUP sans restart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
17 KiB
SOGOMS - Documentation Technique
Service Oriented GO MicroServices - Plateforme SaaS modulaire multi-tenant.
Version: 1.0.1 Date: 16 décembre 2025
Table des matières
- Architecture
- Services
- Communication IPC
- Configuration
- API REST
- Système de Queries
- Authentification
- Déploiement
- Structure du projet
Architecture
Client → Nginx(:443) → Sogoway(:8080) → Sogoms-* → MariaDB
↓
Unix Sockets
/run/sogoms-*.sock
Flux de données
- Client envoie requête HTTPS vers Nginx
- Nginx route
/api/*vers sogoway:8080 - Sogoway identifie l'app par hostname, valide JWT, route vers le bon service
- Sogoms-db exécute les requêtes SQL
- Sogoms-logs enregistre événements et erreurs
Principes
- Modularité : 1 feature = 1 binaire Go
- Configuration YAML : requêtes SQL externalisées, pas de recompilation
- Multi-tenant : isolation par
user_id, filtrage configurable par rôle - Supervision : sogoctl gère le cycle de vie des services
Services
| Binaire | Rôle | Port/Socket |
|---|---|---|
sogoctl |
Superviseur PID 1, health checks, restart auto | - |
sogoway |
Gateway HTTP, auth JWT, routing CRUD | TCP :8080 |
sogoms-db |
Accès MariaDB, pool par application | /run/sogoms-db.1.sock |
sogoms-logs |
Logging centralisé, rotation auto | /run/sogoms-logs.1.sock |
sogoms-smtp |
Envoi d'emails, templates YAML | /run/sogoms-smtp.1.sock |
sogoms-cron |
Tâches planifiées, jobs périodiques | /run/sogoms-cron.1.sock |
sogoms-admin |
Interface web d'administration | TCP :9000 |
sogoctl (Superviseur)
- Démarre les services dans l'ordre des dépendances
- Health checks périodiques (socket ou HTTP)
- Redémarrage automatique en cas de crash
- Arrêt gracieux sur SIGTERM/SIGINT
- Socket de contrôle
/run/sogoctl.sockpour commandes runtime :reload <service>: envoie SIGHUP au service (rechargement config)status: affiche l'état des services
# config/sogoctl.yaml
supervisor:
health_interval: 10s
restart_delay: 2s
max_restarts: 5
services:
sogoms-logs:
binary: /opt/sogoms/bin/sogoms-logs
args: ["-config", "/config", "-socket", "/run/sogoms-logs.1.sock"]
health_socket: /run/sogoms-logs.1.sock
sogoms-db:
binary: /opt/sogoms/bin/sogoms-db
args: ["-config", "/config", "-socket", "/run/sogoms-db.1.sock"]
health_socket: /run/sogoms-db.1.sock
depends_on: [sogoms-logs]
sogoway:
binary: /opt/sogoms/bin/sogoway
args: ["-config", "/config", "-port", "8080"]
health_url: http://localhost:8080/health
depends_on: [sogoms-db, sogoms-logs]
sogoway (Gateway HTTP)
- Routing par hostname → charge la config de l'app
- Authentification JWT (HS256)
- CRUD générique paramétré par YAML
- Logging des événements (login, register)
- Rechargement à chaud : SIGHUP recharge registry + JWT sans restart
sogoms-db (Base de données)
Actions disponibles :
query: SELECT multi-résultatsquery_one: SELECT un résultatinsert: INSERT, retourneinsert_idupdate: UPDATE, retourneaffected_rowsdelete: DELETE, retourneaffected_rowshealth: ping DB
sogoms-logs (Logging)
Actions disponibles :
log_event: événement applicatif (login, register, etc.)log_error: erreur avec niveau (error, warn, info)health: statut OK
Fichiers générés : /var/log/sogoms/{app}-{YYYYMMDD}-{type}.log
Format : JSON, une ligne par entrée
Rotation : suppression automatique des fichiers > 30 jours (configurable)
sogoms-smtp (Emails)
Actions disponibles :
send: envoi email simple (to, subject, body, body_html)send_template: envoi avec template YAMLsend_bulk: envoi en masse (tableau de destinataires)health: statut OK
Configuration SMTP dans config/routes/{app}.yaml :
smtp:
host: mail.example.com
port: 587
user: noreply@example.com
password_file: /secrets/{app}_smtp_pass
from: noreply@example.com
from_name: Mon App
tls: false # false = STARTTLS (587), true = TLS (465)
Templates dans config/emails/{app}/*.yaml :
subject: "Bienvenue {{.Name}} !"
body: |
Bonjour {{.Name}},
Bienvenue sur notre plateforme.
body_html: |
<h1>Bienvenue {{.Name}} !</h1>
<p>Bienvenue sur notre plateforme.</p>
sogoms-cron (Tâches planifiées)
Exécute des jobs périodiques définis en YAML avec support cron standard.
Actions disponibles :
list: liste les jobs configurés avec prochain runtrigger: déclenche un job manuellementstatus: historique des dernières exécutionshealth: statut OK
Types de jobs :
query_email: requête DB + envoi email groupé par utilisateurhttp: appel HTTP (GET/POST) vers endpoint interne ou externeservice: appel service interne (sogoms-db, sogoms-smtp, etc.)
Configuration dans config/apps/{app}/cron.yaml :
timezone: Europe/Paris
retry:
max_attempts: 3
delay: 5m
history_days: 7
jobs:
tasks_today:
schedule: "0 8 * * 1-5" # 8h00 lun-ven
type: query_email
query: |
SELECT u.id AS user_id, u.email, u.name AS user_name,
t.title, p.name AS project_name, s.name AS status_name
FROM users u
INNER JOIN tasks t ON t.user_id = u.id
LEFT JOIN projects p ON t.project_id = p.id
LEFT JOIN statuses s ON t.status_id = s.id
WHERE t.date_end <= CURDATE()
group_by: user_id
template: tasks_today
enabled: true
Format cron : minute heure jour mois jour_semaine
0 8 * * 1-5: 8h00 du lundi au vendredi*/15 * * * *: toutes les 15 minutes0 9 1 * *: 9h00 le premier de chaque mois
sogoms-admin (Interface web)
Interface d'administration web pour gérer les applications SOGOMS.
Rôles :
super_admin: accès global à toutes les apps et servicesapp_admin: accès limité aux apps assignées avec permissions fines
Stack :
- Backend : Go net/http
- Frontend : Go templates + htmx + Pico.css (embarqués via go:embed)
- Auth : sessions cookies signées (HMAC-SHA256)
Sécurité :
- Passwords : bcrypt cost=12
- Sessions : Cookie HttpOnly + Secure + SameSite=Strict
- CSRF : Token par session
- Rate limiting : 5 tentatives/min par IP
Routes :
GET /admin/login: page de connexionPOST /admin/login: authentificationGET /admin/: dashboard principalPOST /admin/logout: déconnexionGET /admin/api/apps: liste apps (htmx partial)GET /admin/api/services/health: statut services (htmx partial)
Configuration :
# /secrets/admin_users.yaml
session:
secret_file: /secrets/admin_session_secret
max_age: 3600
cookie_name: sogoms_admin_sid
rate_limit:
login_max: 5
login_window: 60
users:
- username: pierre
password_hash: "$2a$12$..."
role: super_admin
email: pierre@example.com
- username: client1
password_hash: "$2a$12$..."
role: app_admin
apps: [prokov]
permissions:
- schema:read
- queries:read
- cron:read
- logs:read
Permissions disponibles :
schema:read/write/upload: gestion schemaqueries:read/write: requêtes SQLemails:read/write: templates emailcron:read/write/trigger: jobs cronlogs:read: consultation logsdb:introspect: introspection DB*: toutes (super_admin)
Accès externe : admin.sogoms.com via Nginx → :9000
Communication IPC
Protocole Unix socket avec messages JSON length-prefixed :
[4 bytes: longueur BigEndian] [payload JSON]
Request
{
"id": "req_20251216123456.000000",
"action": "query",
"params": {
"app_id": "prokov",
"query": "SELECT * FROM users WHERE id = ?",
"args": [1]
}
}
Response (succès)
{
"id": "req_...",
"status": "success",
"result": { ... }
}
Response (erreur)
{
"id": "req_...",
"status": "error",
"error": {
"code": "DB_ERROR",
"message": "connection refused"
}
}
Configuration
Structure
config/
├── sogoctl.yaml # Superviseur
├── routes/
│ └── prokov.yaml # Config app (DB, auth, routes)
└── queries/
└── prokov/
├── auth.yaml # Requêtes authentification
├── projects.yaml # CRUD projects
├── tasks.yaml # CRUD tasks
├── tags.yaml # CRUD tags
└── statuses.yaml # CRUD statuses
Config application (routes/prokov.yaml)
app: prokov
version: "1.0"
base_path: /api
hosts:
- prokov.unikoffice.com
- prokov.sogoms.com
database:
host: 13.23.33.4
port: 3306
user: prokov_user
password_file: /secrets/prokov_db_pass
name: prokov
auth:
jwt_secret_file: /secrets/prokov_jwt_secret
jwt_expiry: 24h
logs:
retention_days: 30
API REST
Authentification
| Méthode | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth/register |
Non | Inscription |
| POST | /api/auth/login |
Non | Connexion |
| POST | /api/auth/logout |
Oui | Déconnexion |
| GET | /api/auth/me |
Oui | User courant |
Login - Réponse enrichie
{
"success": true,
"message": "Connexion réussie",
"data": {
"token": "eyJ...",
"user": { "id": 1, "email": "...", "name": "..." },
"projects": [...],
"tasks": [...],
"tags": [...],
"statuses": [...]
}
}
CRUD Générique
| Méthode | Endpoint | Action |
|---|---|---|
| GET | /api/{resource} |
Liste |
| GET | /api/{resource}/{id} |
Détail |
| POST | /api/{resource} |
Création |
| PUT | /api/{resource}/{id} |
Modification |
| DELETE | /api/{resource}/{id} |
Suppression |
Resources disponibles : projects, tasks, tags, statuses
Réponses standardisées
Succès :
{
"success": true,
"message": "Created",
"data": { "insert_id": 123 }
}
Erreur :
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Token expired"
}
}
Système de Queries
Les requêtes SQL sont externalisées dans des fichiers YAML pour éviter la recompilation.
Structure Read (list/show)
list:
query: >
SELECT id, name, description FROM projects
filters:
default: "user_id = :user_id"
admin: ""
order: "position ASC"
show:
query: >
SELECT id, name, description FROM projects WHERE id = :id
filters:
default: "user_id = :user_id"
admin: ""
Structure CUD (create/update/delete)
create:
table: projects
fields:
- user_id
- name
- description
- position
update:
table: projects
fields:
- name
- description
- position
filters:
default: "user_id = :user_id"
admin: ""
delete:
table: projects
filters:
default: "user_id = :user_id"
admin: ""
Placeholders
:user_id→ ID utilisateur depuis JWT:id→ ID ressource depuis URL:project_id→ ID projet (pour filtres)
Filtres par rôle
Le filtre appliqué dépend du rôle de l'utilisateur :
default: utilisateur standard, filtré par user_idadmin: pas de filtre, voit tout
Authentification
JWT (JSON Web Token)
- Algorithme : HS256
- Expiration : configurable (défaut 24h)
- Secret : stocké dans fichier (
/secrets/{app}_jwt_secret)
Payload JWT
{
"sub": 1,
"email": "user@example.com",
"name": "User Name",
"app": "prokov",
"exp": 1765959278,
"iat": 1765872878
}
Header HTTP
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Sécurité
- Passwords hashés avec bcrypt
- JWT stateless (pas de stockage serveur)
- Secrets dans fichiers séparés (pas dans YAML)
- Filtrage user_id automatique sur toutes les requêtes CRUD
Déploiement
Script deploy.sh
./deploy.sh
Étapes :
- Build des binaires (linux/amd64)
- Création des archives tar.gz
- Copie vers jump server (IN3)
- Déploiement dans container Incus (gw3)
- Backup local des archives
- Redémarrage automatique de sogoctl
Cible
- Jump host : IN3 (195.154.80.116)
- Container : gw3 (Alpine, 13.23.33.5)
- Binaires :
/opt/sogoms/bin/ - Config :
/config/ - Secrets :
/secrets/ - Logs :
/var/log/sogoms/
Commandes manuelles sur gw3
# Voir les logs
tail -f /var/log/sogoms/sogoctl.log
# Lister les processus
pgrep -la sogo
# Redémarrer
pkill -9 sogo && /opt/sogoms/bin/sogoctl &
Structure du projet
sogoms/
├── cmd/
│ ├── sogoctl/main.go # Superviseur
│ ├── sogoway/main.go # Gateway HTTP
│ └── sogoms/
│ ├── db/main.go # Microservice DB
│ ├── logs/main.go # Microservice Logs
│ ├── smtp/main.go # Microservice SMTP
│ ├── cron/main.go # Microservice Cron
│ └── admin/ # Interface web admin
│ ├── main.go
│ ├── handlers.go
│ ├── middleware.go
│ ├── session.go
│ ├── services.go
│ └── templates/
├── internal/
│ ├── protocol/
│ │ ├── message.go # Structs Request/Response
│ │ ├── server.go # Serveur Unix socket
│ │ └── client.go # Client + Pool connexions
│ ├── config/
│ │ └── config.go # Registry, Queries, CUD
│ ├── cron/
│ │ └── scheduler.go # Parser cron, calcul next run
│ ├── auth/
│ │ ├── jwt.go # Génération/validation JWT
│ │ └── password.go # Hash bcrypt
│ ├── admin/
│ │ ├── config.go # Chargement admin_users.yaml
│ │ ├── permissions.go # Vérification droits
│ │ └── audit.go # Logging actions
│ └── version/
│ └── version.go # Version, BuildTime
├── config/
│ ├── sogoctl.yaml
│ └── apps/
│ └── prokov/
│ ├── app.yaml # Config app (DB, auth, SMTP)
│ ├── schema.yaml # Schema DB généré
│ ├── cron.yaml # Jobs planifiés
│ ├── queries/ # Requêtes SQL
│ │ ├── auth.yaml
│ │ ├── projects.yaml
│ │ └── ...
│ └── emails/ # Templates email
│ ├── welcome.yaml
│ └── tasks_today.yaml
├── bin/ # Binaires compilés
├── deploy.sh # Script déploiement
├── VERSION # Numéro de version
├── go.mod
├── go.sum
├── README.md
├── CLAUDE.md # Instructions Claude Code
├── DOCTECH.md # Documentation technique
└── TODO.md # Roadmap
Dépendances Go
go 1.24.0
github.com/go-sql-driver/mysql v1.9.3
gopkg.in/yaml.v3 v3.0.1
golang.org/x/crypto v0.46.0
Roadmap
Services planifiés
| Phase | Service | Description |
|---|---|---|
| 11 | sogoms-crypt |
Chiffrement/déchiffrement données sensibles (AES-256-GCM) |
| 12b | sogoms-imap |
Lecture boîtes email (IMAP) |
| 12c | sogoms-mailproc |
Traitement emails (règles, webhooks) |
| 13 | sogoms-cron |
Tâches planifiées (format cron standard) |
| 14 | sogoms-push |
Push temps réel via MQTT (Mosquitto) |
| 15 | sogoms-schema |
Génération d'API depuis schéma YAML |
Vision Phase 15 : Schema-Driven API
# config/schema/monapp.yaml - 1 fichier = 1 API complète
tables:
users:
fields:
id: { type: int, primary: true, auto: true }
email: { type: string, unique: true, auth: login }
password: { type: string, auth: password, hidden: true }
projects:
fields:
id: { type: int, primary: true, auto: true }
user_id: { type: int, foreign: users.id, filter: owner }
name: { type: string, required: true }
crud: [list, show, create, update, delete]
Génère automatiquement : routes, queries, validation, filtres user_id, auth.
Hors scope V1
- sogorch (orchestrateur scénarios)
- sogoms-pdf, sogoms-storage
- Multi-tenant avancé (workspaces, partage)
- Rate limiting
- Rôles utilisateurs (admin, manager, user)