Deploy HouseHub / deploy (push) Successful in 1s
- printvault.php: SPA with model grid, sidebar (categories + type filters), search - modules/printvault/api.php: PHP proxy to PrintVault API (auth + CORS) - modules/printvault/assets/printvault.js: model grid, upload modal, detail modal - modules/printvault/assets/printvault.css: full light/dark theme - Upload: drag&drop + form (name, category, tags) - Detail modal: metadata, dimensions, GCode info, download, delete, "Voir en 3D" → PrintVault - Integrated in header nav, home page, settings; translations fr/en/ca Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
112 lines
4.8 KiB
PHP
112 lines
4.8 KiB
PHP
<?php
|
|
ob_start();
|
|
require_once dirname(__DIR__, 2) . '/includes/auth.php';
|
|
require_login();
|
|
|
|
header('Content-Type: application/json');
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }
|
|
|
|
set_exception_handler(function(\Throwable $e) {
|
|
if (!headers_sent()) { header('Content-Type: application/json'); http_response_code(500); }
|
|
echo json_encode(['ok' => false, 'error' => $e->getMessage()]); exit;
|
|
});
|
|
|
|
define('PV_BASE', 'http://192.168.1.29:9500');
|
|
|
|
function pvOk($d) { echo json_encode(['ok' => true, 'data' => $d], JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE); exit; }
|
|
function pvErr($m, $c = 400) { http_response_code($c); echo json_encode(['ok' => false, 'error' => $m]); exit; }
|
|
|
|
function pvProxy(string $url, string $method = 'GET', ?string $body = null, string $contentType = 'application/json'): array {
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 15,
|
|
CURLOPT_CUSTOMREQUEST => $method,
|
|
]);
|
|
if ($body !== null) {
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: ' . $contentType]);
|
|
}
|
|
$resp = curl_exec($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
return ['body' => $resp, 'code' => $code];
|
|
}
|
|
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$action = $_GET['action'] ?? '';
|
|
|
|
// ── MODELS ────────────────────────────────────────────────────────────────────
|
|
if ($action === 'models') {
|
|
if ($method === 'GET') {
|
|
$qs = http_build_query(array_intersect_key($_GET, array_flip(['category','file_type','search','limit','offset'])));
|
|
$r = pvProxy(PV_BASE . '/api/models?' . $qs);
|
|
if ($r['code'] !== 200) pvErr('PrintVault inaccessible', 502);
|
|
pvOk(json_decode($r['body'], true));
|
|
}
|
|
if ($method === 'DELETE') {
|
|
$id = $_GET['id'] ?? null; if (!$id) pvErr('ID manquant');
|
|
$r = pvProxy(PV_BASE . '/api/models/' . intval($id), 'DELETE');
|
|
if ($r['code'] === 200) pvOk(['deleted' => true]);
|
|
pvErr('Erreur suppression', 500);
|
|
}
|
|
if ($method === 'POST') {
|
|
// Upload: forward multipart to PrintVault
|
|
if (empty($_FILES['file'])) pvErr('Fichier manquant');
|
|
$f = $_FILES['file'];
|
|
if ($f['error'] !== UPLOAD_ERR_OK) pvErr('Erreur upload: ' . $f['error']);
|
|
|
|
$postFields = [
|
|
'file' => new CURLFile($f['tmp_name'], $f['type'] ?: 'application/octet-stream', $f['name']),
|
|
'name' => $_POST['name'] ?? pathinfo($f['name'], PATHINFO_FILENAME),
|
|
'description' => $_POST['description'] ?? '',
|
|
'category' => $_POST['category'] ?? 'Non classé',
|
|
'tags' => $_POST['tags'] ?? '',
|
|
];
|
|
|
|
$ch = curl_init(PV_BASE . '/api/models');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 60,
|
|
CURLOPT_POSTFIELDS => $postFields,
|
|
]);
|
|
$resp = curl_exec($ch);
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($code === 200) pvOk(json_decode($resp, true));
|
|
pvErr('Upload échoué (' . $code . '): ' . substr($resp, 0, 200), 500);
|
|
}
|
|
}
|
|
|
|
// ── CATEGORIES ────────────────────────────────────────────────────────────────
|
|
if ($action === 'categories') {
|
|
$r = pvProxy(PV_BASE . '/api/categories');
|
|
if ($r['code'] !== 200) pvErr('PrintVault inaccessible', 502);
|
|
pvOk(json_decode($r['body'], true));
|
|
}
|
|
|
|
// ── FILE DOWNLOAD (proxy) ─────────────────────────────────────────────────────
|
|
if ($action === 'file') {
|
|
$id = intval($_GET['id'] ?? 0); if (!$id) pvErr('ID manquant');
|
|
$info = pvProxy(PV_BASE . '/api/models/' . $id);
|
|
if ($info['code'] !== 200) pvErr('Modèle introuvable', 404);
|
|
$m = json_decode($info['body'], true);
|
|
|
|
$ch = curl_init(PV_BASE . '/api/models/' . $id . '/file');
|
|
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30]);
|
|
$data = curl_exec($ch);
|
|
$mime = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: 'application/octet-stream';
|
|
curl_close($ch);
|
|
|
|
ob_end_clean();
|
|
header('Content-Type: ' . $mime);
|
|
header('Content-Disposition: attachment; filename="' . rawurlencode($m['original_name'] ?? 'model') . '"');
|
|
header('Cache-Control: no-cache');
|
|
echo $data;
|
|
exit;
|
|
}
|
|
|
|
pvErr('Action inconnue', 404);
|