diff --git a/processor/process_mail.php b/processor/process_mail.php index cbcf087..7dd7fc2 100644 --- a/processor/process_mail.php +++ b/processor/process_mail.php @@ -91,5 +91,29 @@ function process_account(array $account, MailMimeParser $parser): void { // Déplace le mail vers cur/ (marque comme lu) rename($filePath, "/var/mail/vhosts/{$domain}/{$account['username']}/Maildir/cur/{$fileName}:2,S"); + + // Webhook + if (!empty($account['webhook_url'])) { + fire_webhook($account['webhook_url'], [ + 'account' => $account['username'], + 'email' => $account['email'], + 'subject' => $subject, + 'from' => $from, + 'source_id' => $sourceId, + 'files' => $attachments ? count($attachments) : 0, + 'received_at'=> date('c'), + ]); + } } } + +function fire_webhook(string $url, array $payload): void { + $ctx = stream_context_create(['http' => [ + 'method' => 'POST', + 'header' => "Content-Type: application/json\r\n", + 'content' => json_encode($payload), + 'timeout' => 5, + 'ignore_errors' => true, + ]]); + @file_get_contents($url, false, $ctx); +} diff --git a/sql/schema.sql b/sql/schema.sql index 9486b53..557532e 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -27,6 +27,7 @@ CREATE TABLE IF NOT EXISTS accounts ( email VARCHAR(200) NOT NULL UNIQUE, display_name VARCHAR(100), smb_path VARCHAR(255) NOT NULL, + webhook_url VARCHAR(500) NULL, active TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME DEFAULT NOW() ); diff --git a/web/admin/accounts.php b/web/admin/accounts.php index 58369b2..2a81fb9 100644 --- a/web/admin/accounts.php +++ b/web/admin/accounts.php @@ -29,9 +29,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { if ($st->fetch()) { $error = "Le compte « {$username} » existe déjà."; } else { + $webhook = trim($_POST['webhook_url'] ?? ''); db()->prepare( - 'INSERT INTO accounts (username, email, display_name, smb_path) VALUES (?, ?, ?, ?)' - )->execute([$username, $email, $display, $smbPath]); + 'INSERT INTO accounts (username, email, display_name, smb_path, webhook_url) VALUES (?, ?, ?, ?, ?)' + )->execute([$username, $email, $display, $smbPath, $webhook ?: null]); // Crée le dossier, l'utilisateur Dovecot et le share Samba $cmd = sprintf( @@ -114,6 +115,11 @@ html_navbar('accounts'); +
+ + +
POST JSON à chaque réception de mail.
+
diff --git a/web/api/mails.php b/web/api/mails.php new file mode 100644 index 0000000..1ec3b2e --- /dev/null +++ b/web/api/mails.php @@ -0,0 +1,34 @@ +query( + 'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username, + GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files + FROM source_file sf + LEFT JOIN accounts a ON sf.account_id = a.id + LEFT JOIN files f ON sf.id = f.source_file_id + GROUP BY sf.id + ORDER BY sf.date_processing DESC' +)->fetchAll(); + +// Vérifie l'existence physique des fichiers +foreach ($rows as &$r) { + $r['files_list'] = []; + if ($r['files']) { + foreach (explode('|', $r['files']) as $f) { + [$fid, $fname, $fpath] = array_pad(explode(':', $f, 3), 3, ''); + $r['files_list'][] = [ + 'id' => (int)$fid, + 'name' => $fname, + 'exists' => file_exists($fpath), + ]; + } + } + unset($r['files']); +} + +echo json_encode($rows); diff --git a/web/api/transfer.php b/web/api/transfer.php new file mode 100644 index 0000000..b738b91 --- /dev/null +++ b/web/api/transfer.php @@ -0,0 +1,60 @@ + 'Method not allowed']); exit; +} + +$sourceId = (int)($_POST['source_id'] ?? 0); +$targetId = (int)($_POST['target_id'] ?? 0); + +if (!$sourceId || !$targetId) { + http_response_code(400); echo json_encode(['error' => 'Paramètres manquants']); exit; +} + +// Récupère le compte destination +$target = db()->prepare('SELECT * FROM accounts WHERE id = ? AND active = 1'); +$target->execute([$targetId]); +$targetAccount = $target->fetch(); +if (!$targetAccount) { + http_response_code(404); echo json_encode(['error' => 'Compte destination introuvable']); exit; +} + +// Récupère les fichiers à transférer +$files = db()->prepare('SELECT * FROM files WHERE source_file_id = ?'); +$files->execute([$sourceId]); +$fileList = $files->fetchAll(); + +$destDir = $targetAccount['smb_path']; +if (!is_dir($destDir)) mkdir($destDir, 0750, true); + +$moved = 0; +foreach ($fileList as $file) { + $newPath = $destDir . '/' . basename($file['path']); + // Évite collision + if (file_exists($newPath)) { + $ext = pathinfo($newPath, PATHINFO_EXTENSION); + $base = pathinfo($newPath, PATHINFO_FILENAME); + $newPath = $destDir . '/' . $base . '_' . time() . '.' . $ext; + } + if (file_exists($file['path'])) { + rename($file['path'], $newPath); + db()->prepare('UPDATE files SET path = ? WHERE id = ?') + ->execute([$newPath, $file['id']]); + $moved++; + } +} + +// Met à jour le compte source_file +db()->prepare('UPDATE source_file SET account_id = ? WHERE id = ?') + ->execute([$targetId, $sourceId]); + +echo json_encode([ + 'success' => true, + 'moved' => $moved, + 'target' => $targetAccount['username'], +]); diff --git a/web/index.php b/web/index.php index caadb48..0ffe376 100644 --- a/web/index.php +++ b/web/index.php @@ -5,12 +5,10 @@ require_once __DIR__ . '/includes/layout.php'; auth_check(); -$message = ''; - -// Suppression d'un email traité +// Suppression if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_source'])) { $id = (int)$_POST['delete_source']; - $st = db()->prepare('SELECT sf.title, CONCAT(sf.file_name) as fname, GROUP_CONCAT(f.path) as fpaths + $st = db()->prepare('SELECT sf.title, GROUP_CONCAT(f.path) as fpaths FROM source_file sf LEFT JOIN files f ON sf.id = f.source_file_id WHERE sf.id = ? GROUP BY sf.id'); $st->execute([$id]); @@ -22,21 +20,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_source'])) { } } db()->prepare('DELETE FROM source_file WHERE id = ?')->execute([$id]); - flash('success', "Entrée « " . htmlspecialchars($row['title']) . " » supprimée."); + flash('success', "Entrée supprimée."); } header('Location: /'); exit; } -$rows = db()->query( - 'SELECT sf.id, sf.title, sf.sender, sf.date_processing, a.username, - GROUP_CONCAT(f.id, ":", f.file_name, ":", f.path ORDER BY f.id SEPARATOR "|") as files - FROM source_file sf - LEFT JOIN accounts a ON sf.account_id = a.id - LEFT JOIN files f ON sf.id = f.source_file_id - GROUP BY sf.id - ORDER BY sf.date_processing DESC' -)->fetchAll(); +// Comptes pour le dropdown transfert +$otherAccounts = db()->query('SELECT id, username, display_name FROM accounts WHERE active = 1 ORDER BY username')->fetchAll(); html_head('Courriers'); html_navbar('mail'); @@ -45,15 +36,13 @@ html_navbar('mail');
Courriers traités
- entrée(s) +
+ — + 10s +
- -
- Aucun courrier traité pour l'instant. -
- -
+
@@ -63,60 +52,158 @@ html_navbar('mail'); - + - - - - - - - - - - - + +
Sujet Expéditeur Pièces jointesActionActions
- - - - - - — - - - - - - - - - - - - - - - - - Aucune pièce jointe - - -
- - -
-
Chargement…
- + +
+ + +
+