# 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 1. [Architecture](#architecture) 2. [Services](#services) 3. [Communication IPC](#communication-ipc) 4. [Configuration](#configuration) 5. [API REST](#api-rest) 6. [Système de Queries](#système-de-queries) 7. [Authentification](#authentification) 8. [Déploiement](#déploiement) 9. [Structure du projet](#structure-du-projet) --- ## Architecture ``` Client → Nginx(:443) → Sogoway(:8080) → Sogoms-* → MariaDB ↓ Unix Sockets /run/sogoms-*.sock ``` ### Flux de données 1. **Client** envoie requête HTTPS vers Nginx 2. **Nginx** route `/api/*` vers sogoway:8080 3. **Sogoway** identifie l'app par hostname, valide JWT, route vers le bon service 4. **Sogoms-db** exécute les requêtes SQL 5. **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.sock` pour commandes runtime : - `reload ` : envoie SIGHUP au service (rechargement config) - `status` : affiche l'état des services ```yaml # 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ésultats - `query_one` : SELECT un résultat - `insert` : INSERT, retourne `insert_id` - `update` : UPDATE, retourne `affected_rows` - `delete` : DELETE, retourne `affected_rows` - `health` : 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 YAML - `send_bulk` : envoi en masse (tableau de destinataires) - `health` : statut OK Configuration SMTP dans `config/routes/{app}.yaml` : ```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` : ```yaml subject: "Bienvenue {{.Name}} !" body: | Bonjour {{.Name}}, Bienvenue sur notre plateforme. body_html: |

Bienvenue {{.Name}} !

Bienvenue sur notre plateforme.

``` ### 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 run - `trigger` : déclenche un job manuellement - `status` : historique des dernières exécutions - `health` : statut OK Types de jobs : - `query_email` : requête DB + envoi email groupé par utilisateur - `http` : appel HTTP (GET/POST) vers endpoint interne ou externe - `service` : appel service interne (sogoms-db, sogoms-smtp, etc.) Configuration dans `config/apps/{app}/cron.yaml` : ```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 minutes - `0 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 services - `app_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 connexion - `POST /admin/login` : authentification - `GET /admin/` : dashboard principal - `POST /admin/logout` : déconnexion - `GET /admin/api/apps` : liste apps (htmx partial) - `GET /admin/api/services/health` : statut services (htmx partial) **Configuration :** ```yaml # /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 schema - `queries:read/write` : requêtes SQL - `emails:read/write` : templates email - `cron:read/write/trigger` : jobs cron - `logs:read` : consultation logs - `db: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 ```json { "id": "req_20251216123456.000000", "action": "query", "params": { "app_id": "prokov", "query": "SELECT * FROM users WHERE id = ?", "args": [1] } } ``` ### Response (succès) ```json { "id": "req_...", "status": "success", "result": { ... } } ``` ### Response (erreur) ```json { "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) ```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 ```json { "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 :** ```json { "success": true, "message": "Created", "data": { "insert_id": 123 } } ``` **Erreur :** ```json { "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) ```yaml 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) ```yaml 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_id - `admin` : 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 ```json { "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 ```bash ./deploy.sh ``` Étapes : 1. Build des binaires (linux/amd64) 2. Création des archives tar.gz 3. Copie vers jump server (IN3) 4. Déploiement dans container Incus (gw3) 5. Backup local des archives 6. 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 ```bash # 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 ```yaml # 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)