HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //home/panomity.de/vr.panomity.com/api/objects.php
<?php
/**
 * Virtual-Staging-Objekte einer Szene.
 *
 * GET  ?pano_id=N                      → { objects: [...] } (öffentlich)
 * POST { edit_key, action: add|update|delete, ... }  (Editor)
 */

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Content-Type: application/json; charset=utf-8');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

$db = new SQLite3(__DIR__ . '/../admin/data/vrm.db');
$db->busyTimeout(10000);
$db->exec('CREATE TABLE IF NOT EXISTS scene_objects (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    pano_id INTEGER NOT NULL,
    url TEXT NOT NULL,
    name TEXT,
    tx REAL DEFAULT 0, ty REAL DEFAULT 0, tz REAL DEFAULT 250,
    rx REAL DEFAULT 0, ry REAL DEFAULT 0, rz REAL DEFAULT 0,
    scale REAL DEFAULT 1.0,
    visible INTEGER DEFAULT 1,
    created_at TEXT
)');

function object_row_to_json(array $row): array
{
    return [
        'id' => (int)$row['id'],
        'pano_id' => (int)$row['pano_id'],
        'url' => (string)$row['url'],
        'name' => (string)($row['name'] ?? ''),
        'tx' => (float)$row['tx'], 'ty' => (float)$row['ty'], 'tz' => (float)$row['tz'],
        'rx' => (float)$row['rx'], 'ry' => (float)$row['ry'], 'rz' => (float)$row['rz'],
        'scale' => (float)$row['scale'],
    ];
}

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $panoId = (int)($_GET['pano_id'] ?? 0);
    if ($panoId <= 0) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'Invalid pano_id']);
        exit;
    }
    $stmt = $db->prepare('SELECT * FROM scene_objects WHERE pano_id = :id AND visible = 1 ORDER BY id');
    $stmt->bindValue(':id', $panoId, SQLITE3_INTEGER);
    $result = $stmt->execute();
    $objects = [];
    while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
        $objects[] = object_row_to_json($row);
    }
    echo json_encode(['ok' => true, 'objects' => $objects], JSON_UNESCAPED_UNICODE);
    exit;
}

// POST: Editor-Aktionen
$data = json_decode(file_get_contents('php://input') ?: '', true);
if (!is_array($data)) {
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'Invalid JSON']);
    exit;
}

$config = require __DIR__ . '/config.gpuq.php';
$editKey = (string)($data['edit_key'] ?? '');
if ($editKey === '' || !hash_equals((string)($config['staging_edit_key'] ?? ''), $editKey)) {
    http_response_code(403);
    echo json_encode(['ok' => false, 'error' => 'Forbidden']);
    exit;
}

$action = (string)($data['action'] ?? '');

if ($action === 'add') {
    $panoId = (int)($data['pano_id'] ?? 0);
    $url = (string)($data['url'] ?? '');
    // Nur Objekte aus der lokalen Bibliothek zulassen.
    if ($panoId <= 0 || !preg_match('#^media/objects3d/[A-Za-z0-9._-]+\.glb$#', $url)) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'Invalid pano_id or url']);
        exit;
    }
    $stmt = $db->prepare('INSERT INTO scene_objects (pano_id, url, name, tx, ty, tz, rx, ry, rz, scale, visible, created_at)
        VALUES (:pid, :url, :name, :tx, :ty, :tz, :rx, :ry, :rz, :scale, 1, :now)');
    $stmt->bindValue(':pid', $panoId, SQLITE3_INTEGER);
    $stmt->bindValue(':url', $url, SQLITE3_TEXT);
    $stmt->bindValue(':name', basename($url, '.glb'), SQLITE3_TEXT);
    foreach (['tx' => 0, 'ty' => 110, 'tz' => 250, 'rx' => 0, 'ry' => 0, 'rz' => 0] as $keyName => $default) {
        $stmt->bindValue(':' . $keyName, (float)($data[$keyName] ?? $default));
    }
    $stmt->bindValue(':scale', max(0.001, min(100, (float)($data['scale'] ?? 1.0))));
    $stmt->bindValue(':now', gmdate('c'), SQLITE3_TEXT);
    $stmt->execute();
    $id = $db->lastInsertRowID();
    $row = $db->querySingle('SELECT * FROM scene_objects WHERE id = ' . (int)$id, true);
    echo json_encode(['ok' => true, 'object' => object_row_to_json($row)], JSON_UNESCAPED_UNICODE);
    exit;
}

if ($action === 'update') {
    $id = (int)($data['id'] ?? 0);
    if ($id <= 0) {
        http_response_code(400);
        echo json_encode(['ok' => false, 'error' => 'Invalid id']);
        exit;
    }
    $stmt = $db->prepare('UPDATE scene_objects SET tx=:tx, ty=:ty, tz=:tz, rx=:rx, ry=:ry, rz=:rz, scale=:scale WHERE id=:id');
    foreach (['tx', 'ty', 'tz', 'rx', 'ry', 'rz'] as $keyName) {
        $stmt->bindValue(':' . $keyName, (float)($data[$keyName] ?? 0));
    }
    $stmt->bindValue(':scale', max(0.001, min(100, (float)($data['scale'] ?? 1.0))));
    $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
    $stmt->execute();
    echo json_encode(['ok' => true, 'id' => $id]);
    exit;
}

if ($action === 'delete') {
    $id = (int)($data['id'] ?? 0);
    $stmt = $db->prepare('DELETE FROM scene_objects WHERE id = :id');
    $stmt->bindValue(':id', $id, SQLITE3_INTEGER);
    $stmt->execute();
    echo json_encode(['ok' => true, 'id' => $id]);
    exit;
}

http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Unknown action']);