Files
sogoms/DOCTECH.md
Pierre a4694a10d1 SOGOMS v1.0.1 - Microservices logs, smtp et roadmap
Nouveaux services:
- sogoms-logs : logging centralisé avec rotation
- sogoms-smtp : envoi emails avec templates YAML

Nouvelles fonctionnalités:
- Queries YAML externalisées (config/queries/{app}/)
- CRUD générique paramétrable
- Filtres par rôle (default, admin)
- Templates email (config/emails/{app}/)

Documentation:
- DOCTECH.md : documentation technique complète
- README.md : vision et roadmap
- TODO.md : phases 11-15 planifiées

Roadmap:
- Phase 11: sogoms-crypt (chiffrement)
- Phase 12: sogoms-imap/mailproc (emails)
- Phase 13: sogoms-cron (tâches planifiées)
- Phase 14: sogoms-push (MQTT temps réel)
- Phase 15: sogoms-schema (API auto-générée)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 14:58:46 +01:00

562 lines
13 KiB
Markdown

# 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` |
### 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
```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)
### 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: |
<h1>Bienvenue {{.Name}} !</h1>
<p>Bienvenue sur notre plateforme.</p>
```
---
## 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
├── internal/
│ ├── protocol/
│ │ ├── message.go # Structs Request/Response
│ │ ├── server.go # Serveur Unix socket
│ │ └── client.go # Client + Pool connexions
│ ├── config/
│ │ └── config.go # Registry, Queries, CUD
│ ├── auth/
│ │ ├── jwt.go # Génération/validation JWT
│ │ └── password.go # Hash bcrypt
│ └── version/
│ └── version.go # Version, BuildTime
├── config/
│ ├── sogoctl.yaml
│ ├── routes/
│ │ └── prokov.yaml # Config app (DB, auth, SMTP)
│ ├── queries/
│ │ └── prokov/
│ │ ├── auth.yaml
│ │ ├── projects.yaml
│ │ ├── tasks.yaml
│ │ ├── tags.yaml
│ │ └── statuses.yaml
│ └── emails/
│ └── prokov/
│ ├── welcome.yaml
│ ├── password_reset.yaml
│ ├── task_assigned.yaml
│ └── tasks_today.yaml
├── clients/
│ └── prokov.sql # Schéma DB
├── 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)