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:
2025-12-19 20:30:56 +01:00
parent a4694a10d1
commit 65da4efdad
76 changed files with 5305 additions and 80 deletions

View File

@@ -0,0 +1,44 @@
# Configuration des tâches planifiées pour Prokov
timezone: Europe/Paris
retry:
max_attempts: 3
delay: 5m
history_days: 7
jobs:
# Email quotidien des tâches à faire
tasks_today:
schedule: "0 8 * * 1-5" # 8h00 du lundi au vendredi
type: query_email
enabled: true
# Requête : tâches du jour pour chaque utilisateur
# Retourne les tâches dont la date de fin est aujourd'hui ou dépassée
# et qui ne sont pas dans un statut "terminé" (code >= 100)
query: |
SELECT
u.id AS user_id,
u.email,
u.name AS user_name,
t.id AS task_id,
t.title,
t.priority,
t.date_end,
p.name AS project_name,
s.name AS status_name,
s.color AS status_color
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() OR t.date_start = CURDATE())
AND (s.code IS NULL OR s.code < 100)
ORDER BY u.id, t.priority DESC, t.date_end ASC, t.position ASC
# Grouper par user_id pour envoyer 1 email par utilisateur
group_by: user_id
# Template email à utiliser
template: tasks_today

View File

@@ -0,0 +1,204 @@
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.19-11.8.3-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: prokov
-- ------------------------------------------------------
-- Server version 11.4.8-MariaDB-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;
--
-- Table structure for table `project_tags`
--
DROP TABLE IF EXISTS `project_tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `project_tags` (
`project_id` int(10) unsigned NOT NULL,
`tag_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`project_id`,`tag_id`),
KEY `tag_id` (`tag_id`),
CONSTRAINT `project_tags_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
CONSTRAINT `project_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `projects`
--
DROP TABLE IF EXISTS `projects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `projects` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
`name` varchar(100) NOT NULL,
`description` text DEFAULT NULL,
`position` int(10) unsigned DEFAULT 0,
`created_at` timestamp NULL DEFAULT current_timestamp(),
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
KEY `parent_id` (`parent_id`),
KEY `idx_projects_parent` (`user_id`,`parent_id`),
CONSTRAINT `projects_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `projects_ibfk_2` FOREIGN KEY (`parent_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `statuses`
--
DROP TABLE IF EXISTS `statuses`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `statuses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`project_id` int(10) unsigned DEFAULT NULL,
`code` int(10) unsigned NOT NULL,
`name` varchar(50) NOT NULL,
`color` varchar(7) DEFAULT '#6B7280',
`position` int(10) unsigned DEFAULT 0,
`created_at` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
KEY `idx_statuses_user_project` (`user_id`,`project_id`),
CONSTRAINT `statuses_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tags`
--
DROP TABLE IF EXISTS `tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `tags` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`name` varchar(50) NOT NULL,
`color` varchar(7) DEFAULT '#3B82F6',
`created_at` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `unique_tag_per_user` (`user_id`,`name`),
CONSTRAINT `tags_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `task_tags`
--
DROP TABLE IF EXISTS `task_tags`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `task_tags` (
`task_id` int(10) unsigned NOT NULL,
`tag_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`task_id`,`tag_id`),
KEY `tag_id` (`tag_id`),
CONSTRAINT `task_tags_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE,
CONSTRAINT `task_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tasks`
--
DROP TABLE IF EXISTS `tasks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `tasks` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`project_id` int(10) unsigned NOT NULL,
`status_id` int(10) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text DEFAULT NULL,
`priority` tinyint(3) unsigned DEFAULT 5,
`date_start` date DEFAULT NULL,
`date_end` date DEFAULT NULL,
`time_estimated` int(10) unsigned DEFAULT 0,
`time_spent` int(10) unsigned DEFAULT 0,
`billing` decimal(10,2) DEFAULT 0.00,
`position` int(10) unsigned DEFAULT 0,
`created_at` timestamp NULL DEFAULT current_timestamp(),
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
KEY `status_id` (`status_id`),
KEY `idx_tasks_project_status` (`project_id`,`status_id`),
KEY `idx_tasks_user_status` (`user_id`,`status_id`),
KEY `idx_tasks_dates` (`date_start`,`date_end`),
CONSTRAINT `tasks_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `tasks_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
CONSTRAINT `tasks_ibfk_3` FOREIGN KEY (`status_id`) REFERENCES `statuses` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `users`
--
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`name` varchar(100) NOT NULL,
`language` varchar(5) NOT NULL DEFAULT 'fr',
`timezone` varchar(50) NOT NULL DEFAULT 'Europe/Paris',
`role_id` int(10) unsigned NOT NULL DEFAULT 1,
`created_at` timestamp NULL DEFAULT current_timestamp(),
`updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
KEY `role_id` (`role_id`),
CONSTRAINT `users_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `users_roles` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `users_roles`
--
DROP TABLE IF EXISTS `users_roles`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `users_roles` (
`id` int(10) unsigned NOT NULL,
`name` varchar(50) NOT NULL,
`created_at` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping routines for database 'prokov'
--
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
-- Dump completed on 2025-12-18 10:24:52

View File

@@ -0,0 +1,255 @@
app: prokov
tables:
project_tags:
columns:
project_id:
foreign: projects.id
primary: true
required: true
type: int
tag_id:
foreign: tags.id
primary: true
required: true
type: int
crud: []
primary:
- project_id
- tag_id
projects:
columns:
created_at:
default: current_timestamp()
type: datetime
description:
default: "NULL"
length: 65535
type: text
id:
auto: true
primary: true
required: true
type: int
name:
length: 100
required: true
type: string
parent_id:
default: "NULL"
foreign: projects.id
type: int
position:
default: "0"
type: int
updated_at:
default: current_timestamp()
type: datetime
user_id:
filter: owner
foreign: users.id
required: true
type: int
crud:
- list
- show
- create
- update
- delete
statuses:
columns:
code:
required: true
type: int
color:
default: '''#6B7280'''
length: 7
type: string
created_at:
default: current_timestamp()
type: datetime
id:
auto: true
primary: true
required: true
type: int
name:
length: 50
required: true
type: string
position:
default: "0"
type: int
project_id:
default: "NULL"
type: int
user_id:
filter: owner
foreign: users.id
required: true
type: int
crud:
- list
- show
- create
- update
- delete
tags:
columns:
color:
default: '''#3B82F6'''
length: 7
type: string
created_at:
default: current_timestamp()
type: datetime
id:
auto: true
primary: true
required: true
type: int
name:
length: 50
required: true
type: string
unique: true
user_id:
filter: owner
foreign: users.id
required: true
type: int
unique: true
crud:
- list
- show
- create
- update
- delete
task_tags:
columns:
tag_id:
foreign: tags.id
primary: true
required: true
type: int
task_id:
foreign: tasks.id
primary: true
required: true
type: int
crud: []
primary:
- task_id
- tag_id
tasks:
columns:
billing:
default: "0.00"
type: float
created_at:
default: current_timestamp()
type: datetime
date_end:
default: "NULL"
type: date
date_start:
default: "NULL"
type: date
description:
default: "NULL"
length: 65535
type: text
id:
auto: true
primary: true
required: true
type: int
position:
default: "0"
type: int
priority:
default: "5"
type: int
project_id:
foreign: projects.id
required: true
type: int
status_id:
foreign: statuses.id
required: true
type: int
time_estimated:
default: "0"
type: int
time_spent:
default: "0"
type: int
title:
length: 255
required: true
type: string
updated_at:
default: current_timestamp()
type: datetime
user_id:
filter: owner
foreign: users.id
required: true
type: int
crud:
- list
- show
- create
- update
- delete
users:
columns:
created_at:
default: current_timestamp()
type: datetime
email:
length: 255
required: true
type: string
unique: true
id:
auto: true
primary: true
required: true
type: int
name:
length: 100
required: true
type: string
password:
length: 255
required: true
type: string
role_id:
default: "1"
foreign: users_roles.id
required: true
type: int
updated_at:
default: current_timestamp()
type: datetime
crud:
- list
- show
- create
- update
- delete
users_roles:
columns:
created_at:
default: current_timestamp()
type: datetime
id:
primary: true
required: true
type: int
name:
length: 50
required: true
type: string
crud: []
version: "1.0"

View File

@@ -43,6 +43,49 @@ services:
depends_on:
- sogoms-logs
sogoms-cron:
binary: /opt/sogoms/bin/sogoms-cron
args:
- "-config"
- "/config"
- "-socket"
- "/run/sogoms-cron.1.sock"
- "-db-socket"
- "/run/sogoms-db.1.sock"
- "-smtp-socket"
- "/run/sogoms-smtp.1.sock"
- "-logs-socket"
- "/run/sogoms-logs.1.sock"
health_socket: /run/sogoms-cron.1.sock
depends_on:
- sogoms-db
- sogoms-smtp
- sogoms-logs
sogoms-admin:
binary: /opt/sogoms/bin/sogoms-admin
args:
- "-config"
- "/config"
- "-secrets"
- "/secrets"
- "-templates"
- "/config/admin/templates"
- "-dev"
- "-port"
- "9000"
- "-db-socket"
- "/run/sogoms-db.1.sock"
- "-logs-socket"
- "/run/sogoms-logs.1.sock"
- "-cron-socket"
- "/run/sogoms-cron.1.sock"
health_url: http://localhost:9000/admin/login
depends_on:
- sogoms-db
- sogoms-logs
- sogoms-cron
sogoway:
binary: /opt/sogoms/bin/sogoway
args: