- sogoctl: supervisor avec health checks et restart auto - sogoway: gateway HTTP, auth JWT, routing par hostname - sogoms-db: microservice MariaDB avec pool par application - Protocol IPC Unix socket JSON length-prefixed - Config YAML multi-application (prokov) - Deploy script pour container Alpine gw3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
232 lines
6.1 KiB
PHP
232 lines
6.1 KiB
PHP
<?php
|
|
/**
|
|
* Contrôleur des statuts
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
class StatusController extends Controller
|
|
{
|
|
/**
|
|
* GET /statuses
|
|
* ?project_id=X - statuts d'un projet spécifique
|
|
* ?global=1 - uniquement les statuts globaux
|
|
*/
|
|
public function index(): void
|
|
{
|
|
$this->requireAuth();
|
|
|
|
$db = Database::getInstance();
|
|
|
|
$where = ['user_id = :user_id'];
|
|
$params = ['user_id' => $this->getUserId()];
|
|
|
|
$projectId = $this->request->get('project_id');
|
|
$globalOnly = $this->request->get('global');
|
|
|
|
if ($projectId !== null) {
|
|
// Statuts du projet + statuts globaux
|
|
$where = ['user_id = :user_id AND (project_id = :project_id OR project_id IS NULL)'];
|
|
$params['project_id'] = (int) $projectId;
|
|
} elseif ($globalOnly !== null) {
|
|
$where[] = 'project_id IS NULL';
|
|
}
|
|
|
|
$sql = '
|
|
SELECT s.*,
|
|
(SELECT COUNT(*) FROM tasks t WHERE t.status_id = s.id) as task_count
|
|
FROM statuses s
|
|
WHERE ' . implode(' AND ', $where) . '
|
|
ORDER BY s.position ASC, s.code ASC
|
|
';
|
|
|
|
$stmt = $db->prepare($sql);
|
|
$stmt->execute($params);
|
|
$statuses = $stmt->fetchAll();
|
|
|
|
Response::success($statuses);
|
|
}
|
|
|
|
/**
|
|
* GET /statuses/{id}
|
|
*/
|
|
public function show(): void
|
|
{
|
|
$this->requireAuth();
|
|
|
|
$id = (int) $this->request->getParam('id');
|
|
$status = $this->findOrFail($id);
|
|
|
|
// Nombre de tâches avec ce statut
|
|
$db = Database::getInstance();
|
|
$stmt = $db->prepare('SELECT COUNT(*) as count FROM tasks WHERE status_id = :id');
|
|
$stmt->execute(['id' => $id]);
|
|
$status['task_count'] = (int) $stmt->fetch()['count'];
|
|
|
|
Response::success($status);
|
|
}
|
|
|
|
/**
|
|
* POST /statuses
|
|
*/
|
|
public function store(): void
|
|
{
|
|
$this->requireAuth();
|
|
|
|
$data = $this->validate([
|
|
'code' => 'required|int',
|
|
'name' => 'required|min:1|max:50',
|
|
'color' => 'max:7',
|
|
'project_id' => 'int',
|
|
'position' => 'int',
|
|
]);
|
|
|
|
// Si project_id fourni, vérifier qu'il appartient à l'utilisateur
|
|
if (!empty($data['project_id'])) {
|
|
$this->verifyProject((int) $data['project_id']);
|
|
}
|
|
|
|
$db = Database::getInstance();
|
|
|
|
$stmt = $db->prepare('
|
|
INSERT INTO statuses (user_id, project_id, code, name, color, position)
|
|
VALUES (:user_id, :project_id, :code, :name, :color, :position)
|
|
');
|
|
|
|
$stmt->execute([
|
|
'user_id' => $this->getUserId(),
|
|
'project_id' => $data['project_id'] ?: null,
|
|
'code' => $data['code'],
|
|
'name' => $data['name'],
|
|
'color' => $data['color'] ?? '#6B7280',
|
|
'position' => $data['position'] ?? $data['code'],
|
|
]);
|
|
|
|
$statusId = (int) $db->lastInsertId();
|
|
$status = $this->findOrFail($statusId);
|
|
|
|
Response::success($status, 'Statut créé', 201);
|
|
}
|
|
|
|
/**
|
|
* PUT /statuses/{id}
|
|
*/
|
|
public function update(): void
|
|
{
|
|
$this->requireAuth();
|
|
|
|
$id = (int) $this->request->getParam('id');
|
|
$this->findOrFail($id);
|
|
|
|
$data = $this->validate([
|
|
'code' => 'int',
|
|
'name' => 'min:1|max:50',
|
|
'color' => 'max:7',
|
|
'position' => 'int',
|
|
]);
|
|
|
|
$db = Database::getInstance();
|
|
|
|
$fields = [];
|
|
$params = ['id' => $id];
|
|
|
|
if (isset($data['code'])) {
|
|
$fields[] = 'code = :code';
|
|
$params['code'] = $data['code'];
|
|
}
|
|
|
|
if (isset($data['name'])) {
|
|
$fields[] = 'name = :name';
|
|
$params['name'] = $data['name'];
|
|
}
|
|
|
|
if (isset($data['color'])) {
|
|
$fields[] = 'color = :color';
|
|
$params['color'] = $data['color'];
|
|
}
|
|
|
|
if (isset($data['position'])) {
|
|
$fields[] = 'position = :position';
|
|
$params['position'] = $data['position'];
|
|
}
|
|
|
|
if (!empty($fields)) {
|
|
$sql = 'UPDATE statuses SET ' . implode(', ', $fields) . ' WHERE id = :id';
|
|
$stmt = $db->prepare($sql);
|
|
$stmt->execute($params);
|
|
}
|
|
|
|
$status = $this->findOrFail($id);
|
|
|
|
Response::success($status, 'Statut mis à jour');
|
|
}
|
|
|
|
/**
|
|
* DELETE /statuses/{id}
|
|
*/
|
|
public function destroy(): void
|
|
{
|
|
$this->requireAuth();
|
|
|
|
$id = (int) $this->request->getParam('id');
|
|
$status = $this->findOrFail($id);
|
|
|
|
$db = Database::getInstance();
|
|
|
|
// Vérifier qu'aucune tâche n'utilise ce statut
|
|
$stmt = $db->prepare('SELECT COUNT(*) as count FROM tasks WHERE status_id = :id');
|
|
$stmt->execute(['id' => $id]);
|
|
$count = (int) $stmt->fetch()['count'];
|
|
|
|
if ($count > 0) {
|
|
Response::error("Impossible de supprimer : {$count} tâche(s) utilisent ce statut", 409);
|
|
}
|
|
|
|
$stmt = $db->prepare('DELETE FROM statuses WHERE id = :id');
|
|
$stmt->execute(['id' => $id]);
|
|
|
|
Response::success(null, 'Statut supprimé');
|
|
}
|
|
|
|
/**
|
|
* Trouver un statut ou retourner 404
|
|
*/
|
|
private function findOrFail(int $id): array
|
|
{
|
|
$db = Database::getInstance();
|
|
|
|
$stmt = $db->prepare('
|
|
SELECT * FROM statuses
|
|
WHERE id = :id AND user_id = :user_id
|
|
');
|
|
|
|
$stmt->execute([
|
|
'id' => $id,
|
|
'user_id' => $this->getUserId(),
|
|
]);
|
|
|
|
$status = $stmt->fetch();
|
|
|
|
if (!$status) {
|
|
Response::notFound('Statut non trouvé');
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Vérifier qu'un projet appartient à l'utilisateur
|
|
*/
|
|
private function verifyProject(int $projectId): void
|
|
{
|
|
$db = Database::getInstance();
|
|
|
|
$stmt = $db->prepare('SELECT id FROM projects WHERE id = :id AND user_id = :user_id');
|
|
$stmt->execute(['id' => $projectId, 'user_id' => $this->getUserId()]);
|
|
|
|
if (!$stmt->fetch()) {
|
|
Response::error('Projet invalide', 422);
|
|
}
|
|
}
|
|
}
|