SOGOMS v1.0.3 - Admin UI, Cron, Config reload
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>
This commit is contained in:
@@ -20,7 +20,8 @@ type AppConfig struct {
|
||||
Database Database `yaml:"database"`
|
||||
Auth Auth `yaml:"auth"`
|
||||
Routes []Route `yaml:"routes"`
|
||||
Queries *Queries // Chargé depuis config/queries/{app}/
|
||||
Queries *Queries // Chargé depuis config/apps/{app}/queries/
|
||||
Schema *Schema // Chargé depuis config/apps/{app}/schema.yaml
|
||||
}
|
||||
|
||||
// Queries stocke les requêtes SQL par domaine.
|
||||
@@ -48,6 +49,14 @@ func (q *Queries) Get(domain, key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// FileCount retourne le nombre de fichiers de queries chargés.
|
||||
func (q *Queries) FileCount() int {
|
||||
if q == nil || q.files == nil {
|
||||
return 0
|
||||
}
|
||||
return len(q.files)
|
||||
}
|
||||
|
||||
// GetMap retourne une map de requêtes (ex: login_data).
|
||||
func (q *Queries) GetMap(domain, key string) map[string]string {
|
||||
if q == nil || q.files == nil {
|
||||
@@ -301,26 +310,33 @@ func NewRegistry(configDir string) *Registry {
|
||||
}
|
||||
}
|
||||
|
||||
// Load charge toutes les configurations depuis le répertoire routes.
|
||||
// Load charge toutes les configurations depuis le répertoire apps.
|
||||
func (r *Registry) Load() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
routesDir := filepath.Join(r.configDir, "routes")
|
||||
entries, err := os.ReadDir(routesDir)
|
||||
appsDir := filepath.Join(r.configDir, "apps")
|
||||
entries, err := os.ReadDir(appsDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read routes dir: %w", err)
|
||||
return fmt.Errorf("read apps dir: %w", err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".yaml") {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
path := filepath.Join(routesDir, entry.Name())
|
||||
cfg, err := r.loadAppConfig(path)
|
||||
appID := entry.Name()
|
||||
appConfigPath := filepath.Join(appsDir, appID, "app.yaml")
|
||||
|
||||
// Vérifier que app.yaml existe
|
||||
if _, err := os.Stat(appConfigPath); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
cfg, err := r.loadAppConfig(appConfigPath, appID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load %s: %w", entry.Name(), err)
|
||||
return fmt.Errorf("load %s: %w", appID, err)
|
||||
}
|
||||
|
||||
r.apps[cfg.App] = cfg
|
||||
@@ -333,7 +349,7 @@ func (r *Registry) Load() error {
|
||||
}
|
||||
|
||||
// loadAppConfig charge une configuration d'application depuis un fichier YAML.
|
||||
func (r *Registry) loadAppConfig(path string) (*AppConfig, error) {
|
||||
func (r *Registry) loadAppConfig(path string, appID string) (*AppConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -372,15 +388,18 @@ func (r *Registry) loadAppConfig(path string) (*AppConfig, error) {
|
||||
cfg.Auth.JWTExpiry = "24h"
|
||||
}
|
||||
|
||||
// Charger les requêtes depuis config/queries/{app}/
|
||||
cfg.Queries = r.loadQueries(cfg.App)
|
||||
// Charger les requêtes depuis config/apps/{app}/queries/
|
||||
cfg.Queries = r.loadQueries(appID)
|
||||
|
||||
// Charger le schema depuis config/apps/{app}/schema.yaml
|
||||
cfg.Schema = loadSchema(r.configDir, appID)
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// loadQueries charge les fichiers de requêtes pour une application.
|
||||
func (r *Registry) loadQueries(appID string) *Queries {
|
||||
queriesDir := filepath.Join(r.configDir, "queries", appID)
|
||||
queriesDir := filepath.Join(r.configDir, "apps", appID, "queries")
|
||||
entries, err := os.ReadDir(queriesDir)
|
||||
if err != nil {
|
||||
return nil // Pas de répertoire queries, c'est OK
|
||||
|
||||
Reference in New Issue
Block a user