1.0.0
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
# Защита базы данных
|
||||||
|
<Files "datas.db">
|
||||||
|
Require all denied
|
||||||
|
</Files>
|
||||||
|
|
||||||
|
# Перенаправление WebDAV
|
||||||
|
RewriteRule ^wd(/.*)?$ wd.php [L,E=PATH_INFO:$1]
|
||||||
|
|
||||||
|
# Перенаправление UI (Веб-интерфейс пользователя)
|
||||||
|
RewriteRule ^ui$ ui.php [L]
|
||||||
|
|
||||||
|
# Для поддержки HTTP авторизации на некоторых хостингах
|
||||||
|
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
$db_file = __DIR__ . '/datas.db';
|
||||||
|
if (!file_exists($db_file)) die("Сначала запустите <a href='install.php'>install.php</a>");
|
||||||
|
|
||||||
|
$db = new PDO('sqlite:' . $db_file);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
// Авторизация
|
||||||
|
if (isset($_POST['login'])) {
|
||||||
|
$stmt = $db->prepare("SELECT password FROM admin WHERE username = ?");
|
||||||
|
$stmt->execute([$_POST['username']]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($row && password_verify($_POST['password'], $row['password'])) {
|
||||||
|
$_SESSION['admin'] = true;
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
sleep(2); // Задержка при неверном пароле (мера безопасности)
|
||||||
|
$error = "Неверный логин или пароль";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выход
|
||||||
|
if (isset($_GET['logout'])) {
|
||||||
|
session_destroy();
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка сессии
|
||||||
|
if (!isset($_SESSION['admin'])) {
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><title>Вход | S3 WebDAV</title>
|
||||||
|
<style>
|
||||||
|
:root { --md-sys-color-background: #1c1b1f; --md-sys-color-surface: #2b2930; --md-sys-color-primary: #d0bcff; --md-sys-color-on-primary: #381e72; --md-sys-color-on-surface: #e6e1e5; }
|
||||||
|
body { background: var(--md-sys-color-background); color: var(--md-sys-color-on-surface); font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
||||||
|
.card { background: var(--md-sys-color-surface); padding: 32px; border-radius: 24px; box-shadow: 0 4px 8px rgba(0,0,0,0.3); text-align: center; }
|
||||||
|
input { display: block; width: 100%; margin: 16px 0; padding: 16px; background: transparent; border: 1px solid #938f99; color: white; border-radius: 4px; box-sizing: border-box; }
|
||||||
|
button { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); border: none; padding: 12px 24px; border-radius: 100px; font-weight: 600; cursor: pointer; width: 100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h2>Вход в панель</h2>
|
||||||
|
<?php if(isset($error)) echo "<p style='color:#ffb4ab;'>$error</p>"; ?>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="text" name="username" placeholder="Логин" required>
|
||||||
|
<input type="password" name="password" placeholder="Пароль" required>
|
||||||
|
<button type="submit" name="login">Войти</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление нового S3
|
||||||
|
if (isset($_POST['add_s3'])) {
|
||||||
|
$stmt = $db->prepare("INSERT INTO s3_mounts (dav_user, dav_pass, s3_key, s3_secret, s3_region, s3_endpoint, s3_bucket) VALUES (?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([
|
||||||
|
$_POST['dav_user'], password_hash($_POST['dav_pass'], PASSWORD_DEFAULT),
|
||||||
|
$_POST['s3_key'], $_POST['s3_secret'], $_POST['s3_region'], $_POST['s3_endpoint'], $_POST['s3_bucket']
|
||||||
|
]);
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаление S3
|
||||||
|
if (isset($_GET['delete'])) {
|
||||||
|
$stmt = $db->prepare("DELETE FROM s3_mounts WHERE id = ?");
|
||||||
|
$stmt->execute([$_GET['delete']]);
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mounts = $db->query("SELECT * FROM s3_mounts")->fetchAll();
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"><title>Управление S3 WebDAV</title>
|
||||||
|
<style>
|
||||||
|
:root { --bg: #1c1b1f; --surface: #2b2930; --primary: #d0bcff; --on-primary: #381e72; --text: #e6e1e5; --error: #ffb4ab; }
|
||||||
|
body { background: var(--bg); color: var(--text); font-family: system-ui, sans-serif; margin: 0; padding: 20px; }
|
||||||
|
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
|
||||||
|
.card { background: var(--surface); padding: 24px; border-radius: 24px; margin-bottom: 24px; }
|
||||||
|
table { width: 100%; border-collapse: collapse; margin-top: 16px; }
|
||||||
|
th, td { text-align: left; padding: 12px; border-bottom: 1px solid #49454f; }
|
||||||
|
a.btn, button { background: var(--primary); color: var(--on-primary); text-decoration: none; padding: 10px 20px; border-radius: 100px; border: none; cursor: pointer; font-weight: bold; }
|
||||||
|
a.btn-error { background: var(--error); color: #690005; }
|
||||||
|
input { padding: 12px; margin: 8px 0; background: transparent; border: 1px solid #938f99; color: white; border-radius: 4px; width: calc(50% - 24px); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h1>S3 → WebDAV Gateway</h1>
|
||||||
|
<a href="?logout=1" class="btn">Выход</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Добавить S3 Подключение (WebDAV User)</h2>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="text" name="dav_user" placeholder="WebDAV Логин" required>
|
||||||
|
<input type="password" name="dav_pass" placeholder="WebDAV Пароль" required><br>
|
||||||
|
<input type="text" name="s3_key" placeholder="S3 Access Key" required>
|
||||||
|
<input type="text" name="s3_secret" placeholder="S3 Secret Key" required><br>
|
||||||
|
<input type="text" name="s3_region" placeholder="S3 Region (например, us-east-1)" required>
|
||||||
|
<input type="url" name="s3_endpoint" placeholder="S3 Endpoint URL (с https://)" required><br>
|
||||||
|
<input type="text" name="s3_bucket" placeholder="S3 Bucket Name" required><br>
|
||||||
|
<button type="submit" name="add_s3" style="margin-top: 16px;">Добавить</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Подключенные S3</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>WebDAV Логин</th><th>Bucket</th><th>Endpoint</th><th>Действия</th></tr>
|
||||||
|
<?php foreach($mounts as $m): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($m['dav_user']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($m['s3_bucket']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($m['s3_endpoint']) ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="?delete=<?= $m['id'] ?>" class="btn btn-error" onclick="return confirm('Удалить?')">Удалить</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
$db_file = __DIR__ . '/datas.db';
|
||||||
|
|
||||||
|
// Если БД уже существует — блокируем установку
|
||||||
|
if (file_exists($db_file)) {
|
||||||
|
http_response_code(403);
|
||||||
|
die("403 Forbidden: База данных уже существует. Удалите install.php.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если форма не отправлена — показываем ввод
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
?>
|
||||||
|
<h1>Установка системы</h1>
|
||||||
|
<form method="post">
|
||||||
|
<label>Логин администратора:</label><br>
|
||||||
|
<input type="text" name="username" required><br><br>
|
||||||
|
|
||||||
|
<label>Пароль администратора:</label><br>
|
||||||
|
<input type="password" name="password" required><br><br>
|
||||||
|
|
||||||
|
<button type="submit">Установить</button>
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if ($username === '' || $password === '') {
|
||||||
|
die("Ошибка: логин и пароль не могут быть пустыми.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = new PDO('sqlite:' . $db_file);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
// Таблица администратора UI
|
||||||
|
$db->exec("CREATE TABLE admin (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
username TEXT,
|
||||||
|
password TEXT
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Таблица подключений S3 / WebDAV
|
||||||
|
$db->exec("CREATE TABLE s3_mounts (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
dav_user TEXT UNIQUE,
|
||||||
|
dav_pass TEXT,
|
||||||
|
s3_key TEXT,
|
||||||
|
s3_secret TEXT,
|
||||||
|
s3_region TEXT,
|
||||||
|
s3_endpoint TEXT,
|
||||||
|
s3_bucket TEXT
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Хешируем введённый пароль
|
||||||
|
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$stmt = $db->prepare("INSERT INTO admin (username, password) VALUES (:username, :password)");
|
||||||
|
$stmt->execute([
|
||||||
|
':username' => $username,
|
||||||
|
':password' => $hash
|
||||||
|
]);
|
||||||
|
|
||||||
|
echo "<h1>Установка завершена!</h1>";
|
||||||
|
echo "<p>Создан файл datas.db.</p>";
|
||||||
|
echo "<p><b>Логин:</b> " . htmlspecialchars($username) . "</p>";
|
||||||
|
echo "<p style='color:red;'>В целях безопасности удалите файл install.php!</p>";
|
||||||
|
echo "<a href='index.php'>Перейти в панель управления</a>";
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
die("Ошибка БД: " . $e->getMessage());
|
||||||
|
}
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (file_exists('aws.phar')) {
|
||||||
|
require 'aws.phar';
|
||||||
|
} else {
|
||||||
|
die("Требуется aws.phar (версия 3)");
|
||||||
|
}
|
||||||
|
|
||||||
|
use Aws\S3\S3Client;
|
||||||
|
use Aws\Exception\AwsException;
|
||||||
|
|
||||||
|
$db_file = __DIR__ . '/datas.db';
|
||||||
|
if (!file_exists($db_file)) die("База данных не найдена. Сначала запустите установку.");
|
||||||
|
|
||||||
|
$db = new PDO('sqlite:' . $db_file);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
// Выход
|
||||||
|
if (isset($_GET['logout'])) {
|
||||||
|
session_destroy();
|
||||||
|
header("Location: /ui");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Авторизация
|
||||||
|
if (isset($_POST['login'])) {
|
||||||
|
$stmt = $db->prepare("SELECT * FROM s3_mounts WHERE dav_user = ?");
|
||||||
|
$stmt->execute([$_POST['username']]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($user && password_verify($_POST['password'], $user['dav_pass'])) {
|
||||||
|
$_SESSION['ui_user'] = $user;
|
||||||
|
header("Location: /ui");
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
sleep(2); // Защита от брутфорса
|
||||||
|
$error = "Неверный логин или пароль";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Экран входа
|
||||||
|
if (!isset($_SESSION['ui_user'])) {
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" data-bs-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Вход | S3 Web UI</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||||
|
</head>
|
||||||
|
<body class="d-flex align-items-center py-4 bg-body-tertiary" style="height: 100vh;">
|
||||||
|
<main class="form-signin w-100 m-auto" style="max-width: 400px;">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body p-4 text-center">
|
||||||
|
<i class="bi bi-cloud-arrow-up text-primary" style="font-size: 3rem;"></i>
|
||||||
|
<h1 class="h3 mb-3 fw-normal mt-2">Вход в хранилище</h1>
|
||||||
|
<?php if(isset($error)) echo "<div class='alert alert-danger'>$error</div>"; ?>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="form-floating mb-2">
|
||||||
|
<input type="text" class="form-control" id="floatingInput" name="username" placeholder="Логин" required>
|
||||||
|
<label for="floatingInput">Логин (WebDAV)</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-floating mb-3">
|
||||||
|
<input type="password" class="form-control" id="floatingPassword" name="password" placeholder="Пароль" required>
|
||||||
|
<label for="floatingPassword">Пароль</label>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary w-100 py-2" type="submit" name="login"><i class="bi bi-box-arrow-in-right"></i> Войти</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mount = $_SESSION['ui_user'];
|
||||||
|
|
||||||
|
// Инициализация S3
|
||||||
|
$s3 = new S3Client([
|
||||||
|
'version' => 'latest',
|
||||||
|
'region' => $mount['s3_region'],
|
||||||
|
'endpoint' => $mount['s3_endpoint'],
|
||||||
|
'credentials' => [
|
||||||
|
'key' => $mount['s3_key'],
|
||||||
|
'secret' => $mount['s3_secret'],
|
||||||
|
],
|
||||||
|
'use_path_style_endpoint' => true
|
||||||
|
]);
|
||||||
|
$bucket = $mount['s3_bucket'];
|
||||||
|
|
||||||
|
$prefix = isset($_GET['path']) ? $_GET['path'] : '';
|
||||||
|
if ($prefix !== '' && substr($prefix, -1) !== '/') $prefix .= '/';
|
||||||
|
|
||||||
|
// --- Обработка действий ---
|
||||||
|
try {
|
||||||
|
if (isset($_FILES['file'])) {
|
||||||
|
$s3->putObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Key' => $prefix . $_FILES['file']['name'],
|
||||||
|
'SourceFile' => $_FILES['file']['tmp_name']
|
||||||
|
]);
|
||||||
|
header("Location: /ui?path=" . urlencode($prefix));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['new_folder'])) {
|
||||||
|
$folderName = trim($_POST['new_folder']);
|
||||||
|
if (!empty($folderName)) {
|
||||||
|
$s3->putObject(['Bucket' => $bucket, 'Key' => $prefix . $folderName . '/', 'Body' => '']);
|
||||||
|
}
|
||||||
|
header("Location: /ui?path=" . urlencode($prefix));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['delete_key'])) {
|
||||||
|
$s3->deleteObject(['Bucket' => $bucket, 'Key' => $_POST['delete_key']]);
|
||||||
|
header("Location: /ui?path=" . urlencode($prefix));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
$action_error = "Ошибка S3: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение списка файлов
|
||||||
|
$objects = [];
|
||||||
|
$folders = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = $s3->listObjectsV2([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Prefix' => $prefix,
|
||||||
|
'Delimiter' => '/'
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isset($result['CommonPrefixes'])) {
|
||||||
|
foreach ($result['CommonPrefixes'] as $p) $folders[] = $p['Prefix'];
|
||||||
|
}
|
||||||
|
if (isset($result['Contents'])) {
|
||||||
|
foreach ($result['Contents'] as $c) {
|
||||||
|
if ($c['Key'] !== $prefix) $objects[] = $c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
die("Ошибка подключения к S3: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPresignedUrl($s3, $bucket, $key) {
|
||||||
|
$cmd = $s3->getCommand('GetObject', ['Bucket' => $bucket, 'Key' => $key]);
|
||||||
|
$request = $s3->createPresignedRequest($cmd, '+2 hours');
|
||||||
|
return (string) $request->getUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Хлебные крошки
|
||||||
|
$pathParts = array_filter(explode('/', $prefix));
|
||||||
|
$breadcrumbsHTML = "<li class='breadcrumb-item'><a href='/ui'><i class='bi bi-house-door'></i> Корень</a></li>";
|
||||||
|
$accPath = '';
|
||||||
|
foreach ($pathParts as $part) {
|
||||||
|
$accPath .= $part . '/';
|
||||||
|
$breadcrumbsHTML .= "<li class='breadcrumb-item'><a href='/ui?path=" . urlencode($accPath) . "'>" . htmlspecialchars($part) . "</a></li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Категории файлов для UI
|
||||||
|
$imgExt = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
|
||||||
|
$vidExt = ['mp4', 'webm', 'ogg', 'mov', 'avi'];
|
||||||
|
$audExt = ['mp3', 'wav', 'ogg', 'flac'];
|
||||||
|
$txtExt = ['txt', 'md', 'json', 'csv', 'log', 'xml', 'php', 'html', 'css', 'js'];
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru" data-bs-theme="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>S3 Файловый менеджер</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||||
|
<style>
|
||||||
|
.icon-large { font-size: 1.5rem; vertical-align: middle; }
|
||||||
|
.table-hover tbody tr:hover { cursor: pointer; }
|
||||||
|
/* Исправление белого фона для txt во фрейме */
|
||||||
|
.text-iframe { width: 100%; height: 60vh; border: none; background: #fff; border-radius: 8px;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-body-tertiary">
|
||||||
|
|
||||||
|
<!-- Навбар -->
|
||||||
|
<nav class="navbar navbar-expand-lg bg-dark border-bottom border-secondary mb-4 shadow-sm">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#"><i class="bi bi-cloud-check text-primary"></i> <?= htmlspecialchars($mount['s3_bucket']) ?></a>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="text-light me-3"><i class="bi bi-person-circle"></i> <?= htmlspecialchars($mount['dav_user']) ?></span>
|
||||||
|
<a href="?logout=1" class="btn btn-sm btn-outline-danger"><i class="bi bi-box-arrow-right"></i> Выйти</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container-fluid px-4">
|
||||||
|
<?php if(isset($action_error)) echo "<div class='alert alert-danger'>$action_error</div>"; ?>
|
||||||
|
|
||||||
|
<!-- Панель управления (Загрузка / Папки) -->
|
||||||
|
<div class="row mb-3 g-2">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<form method="POST" enctype="multipart/form-data" class="d-flex">
|
||||||
|
<input type="file" name="file" class="form-control me-2" required>
|
||||||
|
<button type="submit" class="btn btn-primary text-nowrap"><i class="bi bi-upload"></i> Загрузить</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<form method="POST" class="input-group">
|
||||||
|
<input type="text" name="new_folder" class="form-control" placeholder="Имя новой папки" required>
|
||||||
|
<button type="submit" class="btn btn-success"><i class="bi bi-folder-plus"></i> Создать</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Основная карточка с файлами -->
|
||||||
|
<div class="card shadow-sm mb-5">
|
||||||
|
<div class="card-header bg-dark">
|
||||||
|
<nav aria-label="breadcrumb" class="mt-2">
|
||||||
|
<ol class="breadcrumb mb-0">
|
||||||
|
<?= $breadcrumbsHTML ?>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-dark">
|
||||||
|
<tr>
|
||||||
|
<th scope="col" style="width: 50%;">Имя</th>
|
||||||
|
<th scope="col">Размер</th>
|
||||||
|
<th scope="col" class="d-none d-md-table-cell">Изменен</th>
|
||||||
|
<th scope="col" class="text-end">Действия</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Папки -->
|
||||||
|
<?php foreach ($folders as $f):
|
||||||
|
$folderName = basename($f);
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="/ui?path=<?= urlencode($f) ?>" class="text-decoration-none text-light fw-bold">
|
||||||
|
<i class="bi bi-folder-fill text-warning icon-large me-2"></i> <?= htmlspecialchars($folderName) ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-muted">-</td>
|
||||||
|
<td class="text-muted d-none d-md-table-cell">-</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<form method="POST" class="d-inline" onsubmit="return confirm('Удалить папку?');">
|
||||||
|
<input type="hidden" name="delete_key" value="<?= htmlspecialchars($f) ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<!-- Файлы -->
|
||||||
|
<?php foreach ($objects as $obj):
|
||||||
|
$fileName = basename($obj['Key']);
|
||||||
|
$size = round($obj['Size'] / 1024 / 1024, 2) . ' MB';
|
||||||
|
$date = date('d.m.Y H:i', strtotime($obj['LastModified']->__toString()));
|
||||||
|
$url = getPresignedUrl($s3, $bucket, $obj['Key']);
|
||||||
|
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
|
// Определяем тип файла для иконок и логики открытия
|
||||||
|
$viewType = 'download';
|
||||||
|
$icon = 'bi-file-earmark text-secondary';
|
||||||
|
|
||||||
|
if (in_array($ext, $imgExt)) { $viewType = 'image'; $icon = 'bi-file-image text-info'; }
|
||||||
|
elseif (in_array($ext, $vidExt)) { $viewType = 'video'; $icon = 'bi-file-play-fill text-primary'; }
|
||||||
|
elseif (in_array($ext, $audExt)) { $viewType = 'audio'; $icon = 'bi-file-music text-success'; }
|
||||||
|
elseif (in_array($ext, $txtExt)) { $viewType = 'text'; $icon = 'bi-file-text text-light'; }
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<i class="bi <?= $icon ?> icon-large me-2"></i>
|
||||||
|
<?php if($viewType !== 'download'): ?>
|
||||||
|
<a href="javascript:void(0)" onclick="openPreview('<?= htmlspecialchars($url) ?>', '<?= $viewType ?>', '<?= htmlspecialchars($fileName) ?>', '<?= $ext ?>')" class="text-decoration-none text-light">
|
||||||
|
<?= htmlspecialchars($fileName) ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="text-light"><?= htmlspecialchars($fileName) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td><span class="badge bg-secondary"><?= $size ?></span></td>
|
||||||
|
<td class="text-muted d-none d-md-table-cell"><small><?= $date ?></small></td>
|
||||||
|
<td class="text-end text-nowrap">
|
||||||
|
<?php if($viewType !== 'download'): ?>
|
||||||
|
<button class="btn btn-sm btn-outline-primary" onclick="openPreview('<?= htmlspecialchars($url) ?>', '<?= $viewType ?>', '<?= htmlspecialchars($fileName) ?>', '<?= $ext ?>')"><i class="bi bi-eye"></i></button>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="<?= htmlspecialchars($url) ?>" target="_blank" class="btn btn-sm btn-outline-success"><i class="bi bi-download"></i></a>
|
||||||
|
<form method="POST" class="d-inline" onsubmit="return confirm('Удалить файл?');">
|
||||||
|
<input type="hidden" name="delete_key" value="<?= htmlspecialchars($obj['Key']) ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php if (empty($folders) && empty($objects)): ?>
|
||||||
|
<tr><td colspan="4" class="text-center py-5 text-muted"><i class="bi bi-inbox fs-1 d-block mb-2"></i> Папка пуста</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Модальное окно предпросмотра -->
|
||||||
|
<div class="modal fade" id="previewModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title text-truncate" id="previewTitle" style="max-width: 90%;">Файл</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-center p-0 bg-black rounded-bottom" id="previewBody" style="overflow: hidden;">
|
||||||
|
<!-- Контент загружается через JS -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
const previewModal = new bootstrap.Modal(document.getElementById('previewModal'));
|
||||||
|
const previewModalEl = document.getElementById('previewModal');
|
||||||
|
const previewBody = document.getElementById('previewBody');
|
||||||
|
const previewTitle = document.getElementById('previewTitle');
|
||||||
|
|
||||||
|
// Остановка медиа при закрытии окна
|
||||||
|
previewModalEl.addEventListener('hidden.bs.modal', () => {
|
||||||
|
previewBody.innerHTML = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
function openPreview(url, type, filename, ext) {
|
||||||
|
previewTitle.innerText = filename;
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
if (type === 'image') {
|
||||||
|
html = `<img src="${url}" class="img-fluid" style="max-height: 80vh;" alt="preview">`;
|
||||||
|
} else if (type === 'video') {
|
||||||
|
html = `<video controls autoplay class="w-100" style="max-height: 80vh;"><source src="${url}" type="video/${ext}">Ваш браузер не поддерживает видео.</video>`;
|
||||||
|
} else if (type === 'audio') {
|
||||||
|
html = `<div class="p-5 bg-dark"><i class="bi bi-music-note-beamed text-success mb-3 d-block" style="font-size: 4rem;"></i><audio controls autoplay class="w-100"><source src="${url}" type="audio/${ext}"></audio></div>`;
|
||||||
|
} else if (type === 'text') {
|
||||||
|
// iframe с белым фоном для текста (т.к. бразуер рендерит TXT черным шрифтом)
|
||||||
|
html = `<iframe src="${url}" class="text-iframe"></iframe>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
previewBody.innerHTML = html;
|
||||||
|
previewModal.show();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
<?php
|
||||||
|
if (file_exists('aws.phar')) {
|
||||||
|
require 'aws.phar';
|
||||||
|
} else {
|
||||||
|
http_response_code(500);
|
||||||
|
die("Требуется aws.phar");
|
||||||
|
}
|
||||||
|
|
||||||
|
use Aws\S3\S3Client;
|
||||||
|
use Aws\Exception\AwsException;
|
||||||
|
|
||||||
|
$db_file = __DIR__ . '/datas.db';
|
||||||
|
$db = new PDO('sqlite:' . $db_file);
|
||||||
|
|
||||||
|
// --- 1. Авторизация ---
|
||||||
|
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
|
||||||
|
header('WWW-Authenticate: Basic realm="WebDAV S3 Gateway"');
|
||||||
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dav_user = $_SERVER['PHP_AUTH_USER'];
|
||||||
|
$dav_pass = $_SERVER['PHP_AUTH_PW'];
|
||||||
|
|
||||||
|
$stmt = $db->prepare("SELECT * FROM s3_mounts WHERE dav_user = ?");
|
||||||
|
$stmt->execute([$dav_user]);
|
||||||
|
$mount = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$mount || !password_verify($dav_pass, $mount['dav_pass'])) {
|
||||||
|
sleep(2);
|
||||||
|
header('WWW-Authenticate: Basic realm="WebDAV S3 Gateway"');
|
||||||
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. Инициализация S3 ---
|
||||||
|
$s3 = new S3Client([
|
||||||
|
'version' => 'latest',
|
||||||
|
'region' => $mount['s3_region'],
|
||||||
|
'endpoint' => $mount['s3_endpoint'],
|
||||||
|
'credentials' => [
|
||||||
|
'key' => $mount['s3_key'],
|
||||||
|
'secret' => $mount['s3_secret'],
|
||||||
|
],
|
||||||
|
'use_path_style_endpoint' => true
|
||||||
|
]);
|
||||||
|
$bucket = $mount['s3_bucket'];
|
||||||
|
|
||||||
|
// --- 3. Парсинг путей ---
|
||||||
|
$baseUri = '/wd';
|
||||||
|
$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||||
|
// Вырезаем /wd из начала пути
|
||||||
|
$path = urldecode(substr($requestUri, strlen($baseUri)));
|
||||||
|
$path = ltrim($path, '/');
|
||||||
|
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
|
||||||
|
// Форматы дат для Windows WebDAV
|
||||||
|
function getDavDates($timestamp = null) {
|
||||||
|
if (!$timestamp) $timestamp = time();
|
||||||
|
return [
|
||||||
|
'lastmod' => gmdate('D, d M Y H:i:s \G\M\T', $timestamp), // RFC 1123
|
||||||
|
'created' => gmdate('Y-m-d\TH:i:s\Z', $timestamp) // ISO 8601
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генератор XML-узла для файла/папки (для идеальной совместимости с Windows)
|
||||||
|
function buildPropResponse($href, $isFolder, $size = 0, $lastModTime = null) {
|
||||||
|
$dates = getDavDates($lastModTime);
|
||||||
|
$href = implode('/', array_map('rawurlencode', explode('/', $href))); // Кодируем пути корректно
|
||||||
|
|
||||||
|
$xml = "<D:response>\n";
|
||||||
|
$xml .= " <D:href>{$href}</D:href>\n";
|
||||||
|
$xml .= " <D:propstat>\n";
|
||||||
|
$xml .= " <D:prop>\n";
|
||||||
|
$xml .= " <D:creationdate>{$dates['created']}</D:creationdate>\n";
|
||||||
|
$xml .= " <D:getlastmodified>{$dates['lastmod']}</D:getlastmodified>\n";
|
||||||
|
|
||||||
|
if ($isFolder) {
|
||||||
|
$xml .= " <D:resourcetype><D:collection/></D:resourcetype>\n";
|
||||||
|
} else {
|
||||||
|
$xml .= " <D:resourcetype/>\n";
|
||||||
|
$xml .= " <D:getcontentlength>{$size}</D:getcontentlength>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фейковые блокировки, чтобы Windows разрешал запись
|
||||||
|
$xml .= " <D:supportedlock>\n";
|
||||||
|
$xml .= " <D:lockentry><D:lockscope><D:exclusive/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry>\n";
|
||||||
|
$xml .= " <D:lockentry><D:lockscope><D:shared/></D:lockscope><D:locktype><D:write/></D:locktype></D:lockentry>\n";
|
||||||
|
$xml .= " </D:supportedlock>\n";
|
||||||
|
|
||||||
|
$xml .= " </D:prop>\n";
|
||||||
|
$xml .= " <D:status>HTTP/1.1 200 OK</D:status>\n";
|
||||||
|
$xml .= " </D:propstat>\n";
|
||||||
|
$xml .= "</D:response>\n";
|
||||||
|
return $xml;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 4. Обработка методов ---
|
||||||
|
switch ($method) {
|
||||||
|
case 'OPTIONS':
|
||||||
|
header('Allow: OPTIONS, GET, HEAD, PUT, DELETE, MKCOL, PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK');
|
||||||
|
header('DAV: 1, 2');
|
||||||
|
header('MS-Author-Via: DAV');
|
||||||
|
http_response_code(200);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PROPFIND':
|
||||||
|
$depth = isset($_SERVER['HTTP_DEPTH']) ? $_SERVER['HTTP_DEPTH'] : '1';
|
||||||
|
|
||||||
|
$prefix = $path;
|
||||||
|
if ($prefix !== '' && substr($prefix, -1) !== '/') {
|
||||||
|
$prefix .= '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$params = [
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Prefix' => $prefix === '/' ? '' : $prefix,
|
||||||
|
'Delimiter' => '/'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Если запрашивают корень или папку с Depth: 0 (Windows делает это для проверки существования)
|
||||||
|
if ($depth == '0') {
|
||||||
|
$reqHref = $baseUri . '/' . $path;
|
||||||
|
if ($path === '' || substr($reqHref, -1) !== '/') $reqHref .= '/'; // Корректировка слешей для папок
|
||||||
|
|
||||||
|
header('Content-Type: application/xml; charset="utf-8"');
|
||||||
|
http_response_code(207);
|
||||||
|
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
||||||
|
echo '<D:multistatus xmlns:D="DAV:">'."\n";
|
||||||
|
echo buildPropResponse($reqHref, true);
|
||||||
|
echo "</D:multistatus>\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем список файлов из S3
|
||||||
|
$result = $s3->listObjectsV2($params);
|
||||||
|
|
||||||
|
header('Content-Type: application/xml; charset="utf-8"');
|
||||||
|
http_response_code(207);
|
||||||
|
|
||||||
|
echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
||||||
|
echo '<D:multistatus xmlns:D="DAV:">'."\n";
|
||||||
|
|
||||||
|
// 1. Текущая директория
|
||||||
|
$reqHref = $baseUri . '/' . $path;
|
||||||
|
if ($path !== '' && substr($reqHref, -1) !== '/') $reqHref .= '/';
|
||||||
|
echo buildPropResponse($reqHref, true);
|
||||||
|
|
||||||
|
// 2. Вложенные папки
|
||||||
|
if (isset($result['CommonPrefixes'])) {
|
||||||
|
foreach ($result['CommonPrefixes'] as $p) {
|
||||||
|
$folderHref = $baseUri . '/' . $p['Prefix'];
|
||||||
|
echo buildPropResponse($folderHref, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Файлы
|
||||||
|
if (isset($result['Contents'])) {
|
||||||
|
foreach ($result['Contents'] as $c) {
|
||||||
|
if ($c['Key'] === $prefix) continue; // Пропускаем саму папку (она создается как пустой объект в S3)
|
||||||
|
$fileHref = $baseUri . '/' . $c['Key'];
|
||||||
|
$lastModTime = strtotime($c['LastModified']->__toString());
|
||||||
|
echo buildPropResponse($fileHref, false, $c['Size'], $lastModTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "</D:multistatus>\n";
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'GET':
|
||||||
|
case 'HEAD':
|
||||||
|
// ИСПРАВЛЕНИЕ ДЛЯ БРАУЗЕРА (Firefox):
|
||||||
|
// Если путь пустой или заканчивается на /, значит это папка. S3 не может скачать папку.
|
||||||
|
if ($path === '' || substr($path, -1) === '/') {
|
||||||
|
if ($method === 'GET') {
|
||||||
|
header('Content-Type: text/html; charset="utf-8"');
|
||||||
|
echo "<!DOCTYPE html><html><body style='background:#1c1b1f;color:white;font-family:sans-serif;text-align:center;padding:50px;'>";
|
||||||
|
echo "<h2>Это эндпойнт WebDAV.</h2>";
|
||||||
|
echo "<p>Для просмотра файлов через браузер используйте веб-интерфейс:</p>";
|
||||||
|
echo "<a href='/ui' style='background:#d0bcff;color:#381e72;padding:10px 20px;border-radius:20px;text-decoration:none;font-weight:bold;display:inline-block;margin-top:20px;'>Перейти в UI</a>";
|
||||||
|
echo "</body></html>";
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$params = ['Bucket' => $bucket, 'Key' => $path];
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||||
|
$params['Range'] = $_SERVER['HTTP_RANGE'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = $s3->getObject($params);
|
||||||
|
|
||||||
|
header('Content-Type: ' . $object['ContentType']);
|
||||||
|
header('Content-Length: ' . $object['ContentLength']);
|
||||||
|
header('Accept-Ranges: bytes');
|
||||||
|
|
||||||
|
// Заголовки кеширования, которые любит Windows
|
||||||
|
header('Cache-Control: no-cache');
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_RANGE']) && isset($object['ContentRange'])) {
|
||||||
|
http_response_code(206);
|
||||||
|
header('Content-Range: ' . $object['ContentRange']);
|
||||||
|
} else {
|
||||||
|
http_response_code(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'GET') {
|
||||||
|
echo $object['Body'];
|
||||||
|
}
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
if ($e->getAwsErrorCode() === 'NoSuchKey') {
|
||||||
|
http_response_code(404);
|
||||||
|
} else {
|
||||||
|
http_response_code(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'PUT':
|
||||||
|
// Снимаем лимит времени выполнения скрипта для загрузки больших файлов
|
||||||
|
set_time_limit(0);
|
||||||
|
ignore_user_abort(true);
|
||||||
|
|
||||||
|
$tempFile = tempnam(sys_get_temp_dir(), 'dav_put_');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Сохраняем поток WebDAV во временный локальный файл
|
||||||
|
$input = fopen('php://input', 'r');
|
||||||
|
$output = fopen($tempFile, 'w');
|
||||||
|
|
||||||
|
// stream_copy_to_stream не забивает оперативную память, а перекачивает данные напрямую
|
||||||
|
stream_copy_to_stream($input, $output);
|
||||||
|
|
||||||
|
fclose($input);
|
||||||
|
fclose($output);
|
||||||
|
|
||||||
|
// 2. Отправляем физический файл в S3
|
||||||
|
// Используем 'SourceFile' вместо 'Body', это включает Multipart-загрузку S3 под капотом
|
||||||
|
$s3->putObject([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Key' => $path,
|
||||||
|
'SourceFile' => $tempFile
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. Удаляем временный файл
|
||||||
|
if (file_exists($tempFile)) {
|
||||||
|
unlink($tempFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(201); // Created
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Обязательно удаляем мусор, если произошла ошибка
|
||||||
|
if (file_exists($tempFile)) {
|
||||||
|
unlink($tempFile);
|
||||||
|
}
|
||||||
|
http_response_code(500);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'DELETE':
|
||||||
|
try {
|
||||||
|
// Для папок в S3 нужно удалять все вложенные объекты
|
||||||
|
if (substr($path, -1) === '/') {
|
||||||
|
$objects = $s3->listObjectsV2(['Bucket' => $bucket, 'Prefix' => $path]);
|
||||||
|
if (isset($objects['Contents'])) {
|
||||||
|
$deleteList = [];
|
||||||
|
foreach ($objects['Contents'] as $obj) {
|
||||||
|
$deleteList[] = ['Key' => $obj['Key']];
|
||||||
|
}
|
||||||
|
$s3->deleteObjects([
|
||||||
|
'Bucket' => $bucket,
|
||||||
|
'Delete' => ['Objects' => $deleteList]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$s3->deleteObject(['Bucket' => $bucket, 'Key' => $path]);
|
||||||
|
}
|
||||||
|
http_response_code(204);
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'MKCOL':
|
||||||
|
if (substr($path, -1) !== '/') $path .= '/';
|
||||||
|
try {
|
||||||
|
$s3->putObject(['Bucket' => $bucket, 'Key' => $path, 'Body' => '']);
|
||||||
|
http_response_code(201);
|
||||||
|
} catch (AwsException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'LOCK':
|
||||||
|
$token = 'urn:uuid:' . sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
|
||||||
|
header('Lock-Token: <' . $token . '>');
|
||||||
|
header('Content-Type: application/xml; charset="utf-8"');
|
||||||
|
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:prop xmlns:D=\"DAV:\"><D:lockdiscovery><D:activelock><D:locktype><D:write/></D:locktype><D:lockscope><D:exclusive/></D:lockscope><D:depth>Infinity</D:depth><D:locktoken><D:href>$token</D:href></D:locktoken></D:activelock></D:lockdiscovery></D:prop>";
|
||||||
|
http_response_code(200);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'UNLOCK':
|
||||||
|
case 'PROPPATCH': // Пропускаем запрос изменения свойств от Windows
|
||||||
|
http_response_code(204);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
http_response_code(405);
|
||||||
|
break;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user