SOGOMS v1.0.7 - 2FA obligatoire et Infrastructure Management
Phase 17g - Double Authentification: - TOTP avec Google Authenticator/Authy - QR code pour enrôlement - Codes de backup (10 codes usage unique) - Page /admin/security pour gestion 2FA - Page /admin/users avec Reset 2FA (super_admin) - 2FA obligatoire pour rôles configurés Phase 21 - Infrastructure Management: - SQLite pour données infra (/data/infra.db) - SSH Pool avec reconnexion auto - Gestion Incus (list, start, stop, restart, sync) - Gestion Nginx (test, reload, deploy, sync, certbot) - Interface admin /admin/infra - Formulaire ajout serveur - Page détail serveur avec containers et sites Fichiers créés: - internal/infra/ (db, models, migrations, repository, ssh, incus, nginx) - cmd/sogoms/admin/totp.go - cmd/sogoms/admin/handlers_2fa.go - cmd/sogoms/admin/handlers_infra.go - Templates: 2fa_*, security, users, infra, server_* 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,10 +19,12 @@ type Schema struct {
|
||||
|
||||
// Table représente une table de la base de données.
|
||||
type Table struct {
|
||||
Columns map[string]*Column `yaml:"columns"`
|
||||
Primary []string `yaml:"primary,omitempty"` // Clé primaire composite
|
||||
CRUD []string `yaml:"crud"`
|
||||
Order string `yaml:"order,omitempty"`
|
||||
Columns map[string]*Column `yaml:"columns"`
|
||||
Primary []string `yaml:"primary,omitempty"` // Clé primaire composite
|
||||
CRUD []string `yaml:"crud"`
|
||||
Order string `yaml:"order,omitempty"`
|
||||
SoftDelete bool `yaml:"soft_delete,omitempty"` // Si true, DELETE → UPDATE deleted_at
|
||||
Cascade bool `yaml:"cascade,omitempty"` // Si true, soft delete en cascade sur enfants
|
||||
}
|
||||
|
||||
// Column représente une colonne d'une table.
|
||||
@@ -78,6 +80,50 @@ func (t *Table) IsCompositePK() bool {
|
||||
return len(t.Primary) > 1
|
||||
}
|
||||
|
||||
// IsSoftDelete vérifie si la table utilise le soft delete.
|
||||
func (t *Table) IsSoftDelete() bool {
|
||||
return t.SoftDelete
|
||||
}
|
||||
|
||||
// IsCascade vérifie si le cascade delete est activé.
|
||||
func (t *Table) IsCascade() bool {
|
||||
return t.Cascade
|
||||
}
|
||||
|
||||
// ChildRelation représente une relation enfant (FK vers table parent).
|
||||
type ChildRelation struct {
|
||||
ChildTable string // Nom de la table enfant
|
||||
ChildColumn string // Colonne FK dans la table enfant
|
||||
ParentTable string // Nom de la table parent
|
||||
}
|
||||
|
||||
// GetChildRelations retourne les tables enfants qui ont une FK vers parentTable.
|
||||
func (s *Schema) GetChildRelations(parentTable string) []ChildRelation {
|
||||
var children []ChildRelation
|
||||
|
||||
for tableName, table := range s.Tables {
|
||||
if tableName == parentTable {
|
||||
continue // Skip la table elle-même
|
||||
}
|
||||
|
||||
for colName, col := range table.Columns {
|
||||
if col.Foreign != "" {
|
||||
// col.Foreign = "table.column"
|
||||
parts := strings.Split(col.Foreign, ".")
|
||||
if len(parts) >= 1 && parts[0] == parentTable {
|
||||
children = append(children, ChildRelation{
|
||||
ChildTable: tableName,
|
||||
ChildColumn: colName,
|
||||
ParentTable: parentTable,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
// GetSelectColumns retourne la liste des colonnes pour un SELECT.
|
||||
func (t *Table) GetSelectColumns() []string {
|
||||
cols := make([]string, 0, len(t.Columns))
|
||||
@@ -111,13 +157,26 @@ func (t *Table) GetUpdateColumns() []string {
|
||||
}
|
||||
|
||||
// BuildListQuery génère la requête SELECT pour list.
|
||||
// Filtre automatiquement les enregistrements supprimés si soft_delete est activé.
|
||||
func (t *Table) BuildListQuery(tableName string) string {
|
||||
cols := t.GetSelectColumns()
|
||||
query := fmt.Sprintf("SELECT %s FROM %s", strings.Join(cols, ", "), tableName)
|
||||
|
||||
// Ajouter filtre owner si présent
|
||||
// Construire les conditions WHERE
|
||||
var conditions []string
|
||||
|
||||
// Filtre owner si présent
|
||||
if ownerCol := t.GetOwnerColumn(); ownerCol != "" {
|
||||
query += fmt.Sprintf(" WHERE %s = ?", ownerCol)
|
||||
conditions = append(conditions, fmt.Sprintf("%s = ?", ownerCol))
|
||||
}
|
||||
|
||||
// Filtre soft delete
|
||||
if t.SoftDelete {
|
||||
conditions = append(conditions, "deleted_at IS NULL")
|
||||
}
|
||||
|
||||
if len(conditions) > 0 {
|
||||
query += " WHERE " + strings.Join(conditions, " AND ")
|
||||
}
|
||||
|
||||
// Ajouter ORDER BY si défini
|
||||
@@ -129,6 +188,7 @@ func (t *Table) BuildListQuery(tableName string) string {
|
||||
}
|
||||
|
||||
// BuildShowQuery génère la requête SELECT pour show (un seul enregistrement).
|
||||
// Filtre automatiquement les enregistrements supprimés si soft_delete est activé.
|
||||
func (t *Table) BuildShowQuery(tableName string) string {
|
||||
cols := t.GetSelectColumns()
|
||||
pk := t.GetPrimaryKey()
|
||||
@@ -139,6 +199,11 @@ func (t *Table) BuildShowQuery(tableName string) string {
|
||||
query += fmt.Sprintf(" AND %s = ?", ownerCol)
|
||||
}
|
||||
|
||||
// Filtre soft delete
|
||||
if t.SoftDelete {
|
||||
query += " AND deleted_at IS NULL"
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user