Reorganize into monorepo layout, move mitarbeiter-app to legacy reference
Consolidates the previously separate omsorgapp and omsorgCore repos (each had their own nested .git with GitHub history) plus the old root-level website/mitarbeiter-app into a single monorepo, matching the structure already documented in the root CLAUDE.md. Also moves the PHP employee app aside as omsorgWeb/mitarbeiter-app-legacy/ to serve as a template for a ground-up rewrite. Fixes .gitignore in the same pass: the config-secrets/uploads/data patterns were unanchored (relative to repo root, not depth-agnostic), so they silently stopped matching once the app moved under omsorgWeb/. Patterns are now **/-prefixed and cover both mitarbeiter-app and mitarbeiter-app-legacy, keeping DB/SMTP credentials and uploaded employee documents out of version control. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8beb0fcf52
commit
b6c1389c55
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/auth.php';
|
||||
|
||||
// Gemeinsame Endung→MIME-Whitelists.
|
||||
const UPLOAD_TYPES_DOCS = [
|
||||
'pdf' => ['application/pdf'],
|
||||
'doc' => ['application/msword'],
|
||||
'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
|
||||
'jpg' => ['image/jpeg'],
|
||||
'jpeg' => ['image/jpeg'],
|
||||
'png' => ['image/png'],
|
||||
];
|
||||
|
||||
const UPLOAD_TYPES_PDF_IMG = [
|
||||
'pdf' => ['application/pdf'],
|
||||
'jpg' => ['image/jpeg'],
|
||||
'jpeg' => ['image/jpeg'],
|
||||
'png' => ['image/png'],
|
||||
];
|
||||
|
||||
const UPLOAD_TYPES_IMAGES = [
|
||||
'jpg' => ['image/jpeg'],
|
||||
'jpeg' => ['image/jpeg'],
|
||||
'png' => ['image/png'],
|
||||
'gif' => ['image/gif'],
|
||||
'webp' => ['image/webp'],
|
||||
];
|
||||
|
||||
const UPLOAD_TYPES_PDF = [
|
||||
'pdf' => ['application/pdf'],
|
||||
];
|
||||
|
||||
const UPLOAD_TYPES_OFFICE = [
|
||||
'pdf' => ['application/pdf'],
|
||||
'doc' => ['application/msword'],
|
||||
'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
|
||||
'xls' => ['application/vnd.ms-excel'],
|
||||
'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
|
||||
'ppt' => ['application/vnd.ms-powerpoint'],
|
||||
'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
|
||||
'txt' => ['text/plain'],
|
||||
'zip' => ['application/zip', 'application/x-zip-compressed'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Validiert und speichert eine hochgeladene Datei.
|
||||
*
|
||||
* @param array $file Eintrag aus $_FILES, z.B. $_FILES['datei']
|
||||
* @param array $opts allowed (ext=>mimes, Pflicht), dir (Pflicht), prefix,
|
||||
* max_bytes (Default 12 MB), required (Default true),
|
||||
* naming ('dated' [Default] = Datum_Username_Prefix_Rand,
|
||||
* 'plain' = Username_Rand, 'hash' = nur Rand),
|
||||
* username (Default current_username(); für 'dated'/'plain'),
|
||||
* messages (Überschreibt Standardtexte: missing/upload/ext/mime/size/move)
|
||||
* @return array ['ok'=>bool, 'error'=>?string, 'filename'=>?string,
|
||||
* 'original_name'=>?string, 'mime'=>?string]
|
||||
*/
|
||||
function handle_upload(array $file, array $opts): array {
|
||||
$allowed = $opts['allowed'];
|
||||
$dir = rtrim($opts['dir'], '/') . '/';
|
||||
$maxBytes = $opts['max_bytes'] ?? 12 * 1024 * 1024;
|
||||
$required = $opts['required'] ?? true;
|
||||
$naming = $opts['naming'] ?? 'dated';
|
||||
$prefix = $opts['prefix'] ?? 'datei';
|
||||
$username = $opts['username'] ?? current_username();
|
||||
|
||||
$msg = array_merge([
|
||||
'missing' => 'Bitte eine Datei auswählen.',
|
||||
'upload' => 'Fehler beim Hochladen der Datei.',
|
||||
'ext' => 'Dateityp nicht erlaubt.',
|
||||
'mime' => 'Dateityp nicht erlaubt.',
|
||||
'size' => 'Datei ist zu groß (max. 12 MB).',
|
||||
'move' => 'Fehler beim Hochladen der Datei.',
|
||||
], $opts['messages'] ?? []);
|
||||
|
||||
$err = $file['error'] ?? UPLOAD_ERR_NO_FILE;
|
||||
|
||||
if ($err === UPLOAD_ERR_NO_FILE || !isset($file['tmp_name'])) {
|
||||
if ($required) return ['ok' => false, 'error' => $msg['missing']];
|
||||
return ['ok' => true, 'error' => null, 'filename' => null, 'original_name' => null, 'mime' => null];
|
||||
}
|
||||
if ($err !== UPLOAD_ERR_OK) {
|
||||
return ['ok' => false, 'error' => $msg['upload']];
|
||||
}
|
||||
|
||||
$originalName = $file['name'];
|
||||
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
|
||||
if (!isset($allowed[$ext])) {
|
||||
return ['ok' => false, 'error' => $msg['ext']];
|
||||
}
|
||||
|
||||
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||||
$mime = $finfo->file($file['tmp_name']);
|
||||
if (!in_array($mime, $allowed[$ext], true)) {
|
||||
return ['ok' => false, 'error' => $msg['mime']];
|
||||
}
|
||||
|
||||
if ($file['size'] > $maxBytes) {
|
||||
return ['ok' => false, 'error' => $msg['size']];
|
||||
}
|
||||
|
||||
$safeName = match ($naming) {
|
||||
'hash' => bin2hex(random_bytes(16)) . '.' . $ext,
|
||||
'plain' => $username . '_' . bin2hex(random_bytes(8)) . '.' . $ext,
|
||||
default => date('Y-m-d') . '_' . $username . '_' . $prefix . '_' . bin2hex(random_bytes(8)) . '.' . $ext,
|
||||
};
|
||||
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
if (!move_uploaded_file($file['tmp_name'], $dir . $safeName)) {
|
||||
return ['ok' => false, 'error' => $msg['move']];
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'error' => null,
|
||||
'filename' => $safeName,
|
||||
'original_name' => $originalName,
|
||||
'mime' => $mime,
|
||||
];
|
||||
}
|
||||
|
||||
function redirect_error(string $back, string $msg): never {
|
||||
header('Location: ' . $back . '?error=' . urlencode($msg));
|
||||
exit;
|
||||
}
|
||||
|
||||
function redirect_ok(string $back, string $param = 'success', string $val = '1'): never {
|
||||
header('Location: ' . $back . '?' . $param . '=' . $val);
|
||||
exit;
|
||||
}
|
||||
Reference in New Issue
Block a user