add: new data uplaod and download service

This commit is contained in:
Tower committed 2026-08-05 20:47:15 +02:00
1 parent d88db54635
commit a6992ed5d9
6 files changed
+382 -1

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
RewriteEngine On
RewriteRule ^(Commands|config_backups|controller|modules)($|/.*) - [F,L,NC]
RewriteRule ^(Commands|config_backups|controller|modules|uploads)($|/.*) - [F,L,NC]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
@@ -0,0 +1,92 @@
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
header('Content-Type: application/json');
error_reporting(E_ALL);
ini_set('display_errors', '0');
if (empty($_SESSION['user_logged_in'])) {
http_response_code(401);
echo json_encode(['error' => 'Nicht eingeloggt']);
exit;
}
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
$UPLOAD_DIR = __DIR__ . '/../../../uploads/';
$daten = json_decode(file_get_contents('php://input'), true) ?? [];
$action = $daten['action'] ?? '';
try {
switch ($action) {
case 'get_entries': {
$entries = DatabaseShow(
"Dateien",
"ID, ORIGINALNAME, GROESSE, HOCHGELADEN_AM",
"HOCHGELADEN_VON = ?",
$_SESSION['username']
);
usort($entries, fn($a, $b) => strtotime($b['HOCHGELADEN_AM']) <=> strtotime($a['HOCHGELADEN_AM']));
echo json_encode(['success' => true, 'entries' => array_values($entries)]);
break;
}
case 'delete_entry': {
$id = $daten['id'] ?? null;
if (!is_numeric($id)) {
throw new InvalidArgumentException('Ungültige ID');
}
// Nur eigene Dateien löschen dürfen
$rows = DatabaseShow(
"Dateien",
"SPEICHERNAME, HOCHGELADEN_VON",
"ID = ?",
(int)$id
);
if (empty($rows)) {
throw new InvalidArgumentException('Datei nicht gefunden');
}
if ($rows[0]['HOCHGELADEN_VON'] !== $_SESSION['username']) {
http_response_code(403);
echo json_encode(['error' => 'Keine Berechtigung']);
exit;
}
$pfad = $UPLOAD_DIR . $rows[0]['SPEICHERNAME'];
if (file_exists($pfad)) {
unlink($pfad);
}
DatabaseDelete("Dateien", "ID = ?", (int)$id);
echo json_encode(['success' => true]);
break;
}
default:
throw new InvalidArgumentException("Unbekannte Aktion: $action");
}
} catch (InvalidArgumentException $e) {
http_response_code(400);
echo json_encode(['error' => $e->getMessage()]);
} catch (Throwable $e) {
http_response_code(500);
error_log($e->getMessage());
echo json_encode(['error' => 'Serverfehler']);
}
@@ -0,0 +1,59 @@
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
if (empty($_SESSION['user_logged_in'])) {
http_response_code(401);
die('Nicht eingeloggt');
}
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
$UPLOAD_DIR = __DIR__ . '/../../../uploads/';
$id = $_GET['id'] ?? null;
if (!is_numeric($id)) {
http_response_code(400);
die('Ungültige ID');
}
$rows = DatabaseShow(
"Dateien",
"ORIGINALNAME, SPEICHERNAME, MIME_TYPE, HOCHGELADEN_VON",
"ID = ?",
(int)$id
);
if (empty($rows)) {
http_response_code(404);
die('Datei nicht gefunden');
}
$datei = $rows[0];
// Nur eigene Dateien herunterladen dürfen (oder Admin, falls gewünscht - siehe Hinweis unten)
if ($datei['HOCHGELADEN_VON'] !== $_SESSION['username']) {
http_response_code(403);
die('Keine Berechtigung');
}
$pfad = $UPLOAD_DIR . $datei['SPEICHERNAME'];
if (!file_exists($pfad)) {
http_response_code(404);
die('Datei nicht mehr vorhanden');
}
header('Content-Type: ' . $datei['MIME_TYPE']);
header('Content-Disposition: attachment; filename="' . basename($datei['ORIGINALNAME']) . '"');
header('Content-Length: ' . filesize($pfad));
readfile($pfad);
exit;
@@ -0,0 +1,79 @@
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
session_start();
header('Content-Type: application/json');
error_reporting(E_ALL);
ini_set('display_errors', '0');
if (empty($_SESSION['user_logged_in'])) {
http_response_code(401);
echo json_encode(['error' => 'Nicht eingeloggt']);
exit;
}
include_once(__DIR__ . '/../../../controller/DatabaseController.php');
$UPLOAD_DIR = __DIR__ . '/../../../uploads/';
$MAX_GROESSE = 20 * 1024 * 1024; // 20 MB
$VERBOTENE_ENDUNGEN = ['php', 'phtml', 'php3', 'php4', 'php5', 'phar', 'exe', 'sh', 'bat', 'cgi', 'htaccess'];
try {
if (empty($_FILES['datei'])) {
throw new InvalidArgumentException('Keine Datei übermittelt');
}
$datei = $_FILES['datei'];
if ($datei['error'] !== UPLOAD_ERR_OK) {
throw new InvalidArgumentException('Fehler beim Upload (Code ' . $datei['error'] . ')');
}
if ($datei['size'] > $MAX_GROESSE) {
throw new InvalidArgumentException('Datei zu groß (max. 20 MB)');
}
$originalName = basename($datei['name']); // gegen Path Traversal im Namen
$endung = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (in_array($endung, $VERBOTENE_ENDUNGEN, true)) {
throw new InvalidArgumentException('Dieser Dateityp ist nicht erlaubt');
}
// Zufälliger, kollisionssicherer Speichername - Original bleibt nur in der DB erhalten
$speicherName = bin2hex(random_bytes(16)) . ($endung !== '' ? '.' . $endung : '');
$zielPfad = $UPLOAD_DIR . $speicherName;
if (!move_uploaded_file($datei['tmp_name'], $zielPfad)) {
throw new Exception('Datei konnte nicht gespeichert werden');
}
$mimeType = mime_content_type($zielPfad) ?: 'application/octet-stream';
DatabaseInsert(
"Dateien",
"ORIGINALNAME, SPEICHERNAME, GROESSE, MIME_TYPE, HOCHGELADEN_VON",
$originalName,
$speicherName,
$datei['size'],
$mimeType,
$_SESSION['username']
);
echo json_encode(['success' => true]);
} catch (InvalidArgumentException $e) {
http_response_code(400);
echo json_encode(['error' => $e->getMessage()]);
} catch (Throwable $e) {
http_response_code(500);
error_log($e->getMessage());
echo json_encode(['error' => 'Serverfehler']);
}
+29
View File
@@ -0,0 +1,29 @@
const DATEIEN_HANDLER_URL = '/api/js/handler/DateienAjaxHandler';
async function dateienAjaxRequest(payload) {
try {
const response = await fetch(DATEIEN_HANDLER_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const data = await parseJsonResponse(response);
if (data.error) {
throw new Error(data.error);
}
return data;
} catch (error) {
console.error('Dateien-Ajax-Fehler:', error.message);
throw error;
}
}
function getDateienEntries() {
return dateienAjaxRequest({ action: 'get_entries' });
}
function deleteDateiEntry(id) {
return dateienAjaxRequest({ action: 'delete_entry', id });
}
+122
View File
@@ -0,0 +1,122 @@
<div class="arbeitsberichte-wrapper">
<div class="vergabe-form">
<h3>Datei hochladen</h3>
<form id="uploadForm" enctype="multipart/form-data">
<div class="form-group">
<label>Datei auswählen</label>
<input type="file" name="datei" id="in-datei" required>
</div>
<button type="submit" class="btn-submit-vergabe">⬆️ Hochladen</button>
</form>
<p id="uploadStatus" style="margin-top: 10px;"></p>
</div>
<div class="table-container" style="margin-top: 30px;">
<h3>Meine Dateien</h3>
<div id="tableWrapper">
<p>Lade Daten...</p>
</div>
</div>
</div>
<script src="/js/AjaxMethods.js"></script>
<script src="/js/ajaxApi/DateienAjaxMethods.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const tableWrapper = document.getElementById('tableWrapper');
const uploadStatus = document.getElementById('uploadStatus');
function formatBytes(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
}
function renderDateienTabelle(entries) {
if (!entries || entries.length === 0) {
tableWrapper.innerHTML = '<p>Keine Dateien vorhanden.</p>';
return;
}
let html = `
<table class="db-table mobile-responsive-table">
<thead>
<tr>
<th>Dateiname</th><th>Größe</th><th>Hochgeladen am</th>
<th style="width: 180px;">Aktion</th>
</tr>
</thead>
<tbody>
`;
for (const e of entries) {
html += `
<tr data-id="${escapeHtml(e.ID)}">
<td data-label="Dateiname">${escapeHtml(e.ORIGINALNAME)}</td>
<td data-label="Größe">${formatBytes(e.GROESSE)}</td>
<td data-label="Hochgeladen am">${escapeHtml(e.HOCHGELADEN_AM)}</td>
<td data-label="Aktion">
<div style="display:flex; gap:6px;">
<a href="/api/js/handler/DateienDownloadHandler.php?id=${escapeHtml(e.ID)}" class="btn-submit-vergabe" style="text-decoration:none; padding:6px 12px;">⬇️ Download</a>
<button type="button" class="btn-delete" data-id="${escapeHtml(e.ID)}">🗑️</button>
</div>
</td>
</tr>`;
}
html += `</tbody></table>`;
tableWrapper.innerHTML = html;
tableWrapper.querySelectorAll('.btn-delete').forEach(btn => {
btn.addEventListener('click', async () => {
if (!confirm('Diese Datei wirklich löschen?')) return;
try {
await deleteDateiEntry(btn.dataset.id);
await ladeListe();
} catch (err) {
alert('Fehler beim Löschen: ' + err.message);
}
});
});
}
async function ladeListe() {
try {
const data = await getDateienEntries();
renderDateienTabelle(data.entries);
} catch (err) {
tableWrapper.innerHTML = `<p>❌ ${escapeHtml(err.message)}</p>`;
}
}
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('in-datei');
if (!fileInput.files.length) return;
const formData = new FormData();
formData.append('datei', fileInput.files[0]);
uploadStatus.textContent = 'Lade hoch...';
try {
const response = await fetch('/api/js/handler/DateienUploadHandler.php', {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
body: formData
});
const data = await parseJsonResponse(response);
if (data.error) throw new Error(data.error);
uploadStatus.textContent = '✅ Erfolgreich hochgeladen';
fileInput.value = '';
await ladeListe();
} catch (err) {
uploadStatus.textContent = '❌ ' + err.message;
}
});
ladeListe();
});
</script>