Files
copymailrpi/web/api/upload.php
Jules 8dd0e471fd feat: téléversement mobile par QR code — alternative AirDrop pour clients en magasin
Page publique /upload.php (mobile-first, sans auth) : le client scanne un QR code
au comptoir, choisit le poste de destination, et envoie ses documents.
Les fichiers sont déposés dans le share SMB du compte, enregistrés en BDD
(mêmes tables que les mails) et déclenchent le webhook si configuré.

Page admin /admin/qrcode.php pour générer/imprimer les QR codes par poste.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017SChGCatJdbmZKCyYvTJMf
2026-09-05 15:13:37 +02:00

105 lines
3.2 KiB
PHP

<?php
require_once __DIR__ . '/../includes/db.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Méthode non autorisée']);
exit;
}
$accountId = (int)($_POST['account_id'] ?? 0);
if (!$accountId) {
http_response_code(400);
echo json_encode(['error' => 'Compte non spécifié']);
exit;
}
$st = db()->prepare('SELECT id, username, smb_path, webhook_url FROM accounts WHERE id = ? AND active = 1');
$st->execute([$accountId]);
$account = $st->fetch();
if (!$account) {
http_response_code(404);
echo json_encode(['error' => 'Compte introuvable ou inactif']);
exit;
}
if (empty($_FILES['files']) || !is_array($_FILES['files']['name'])) {
http_response_code(400);
echo json_encode(['error' => 'Aucun fichier envoyé']);
exit;
}
$destDir = $account['smb_path'];
if (!is_dir($destDir)) mkdir($destDir, 0750, true);
$st = db()->prepare('INSERT INTO source_file (account_id, title, sender, file_name) VALUES (?, ?, ?, ?)');
$st->execute([$account['id'], 'Dépôt comptoir', 'Téléversement mobile', 'upload_' . time()]);
$sourceId = (int)db()->lastInsertId();
$uploaded = 0;
$errors = [];
$fileCount = count($_FILES['files']['name']);
for ($i = 0; $i < $fileCount; $i++) {
if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) {
$errors[] = $_FILES['files']['name'][$i] . ' : erreur upload';
continue;
}
$origName = $_FILES['files']['name'][$i];
$tmpPath = $_FILES['files']['tmp_name'][$i];
$safeName = preg_replace('/[^a-zA-Z0-9._\- ]/', '_', $origName);
$destPath = $destDir . '/' . $safeName;
if (file_exists($destPath)) {
$ext = pathinfo($safeName, PATHINFO_EXTENSION);
$base = pathinfo($safeName, PATHINFO_FILENAME);
$destPath = $destDir . '/' . $base . '_' . time() . ($ext ? '.' . $ext : '');
$safeName = basename($destPath);
}
if (move_uploaded_file($tmpPath, $destPath)) {
chmod($destPath, 0644);
db()->prepare('INSERT INTO files (source_file_id, file_name, path) VALUES (?, ?, ?)')
->execute([$sourceId, $safeName, $destPath]);
$uploaded++;
} else {
$errors[] = $origName . ' : échec écriture';
}
}
if ($uploaded === 0) {
db()->prepare('DELETE FROM source_file WHERE id = ?')->execute([$sourceId]);
http_response_code(500);
echo json_encode(['error' => 'Aucun fichier enregistré', 'details' => $errors]);
exit;
}
if (!empty($account['webhook_url'])) {
$ctx = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode([
'account' => $account['username'],
'subject' => 'Dépôt comptoir',
'from' => 'Téléversement mobile',
'source_id' => $sourceId,
'files' => $uploaded,
'received_at' => date('c'),
]),
'timeout' => 5,
'ignore_errors' => true,
]]);
@file_get_contents($account['webhook_url'], false, $ctx);
}
echo json_encode([
'success' => true,
'uploaded' => $uploaded,
'account' => $account['username'],
'errors' => $errors,
]);