- Front controller and UI in [public/index.php](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/public/index.php) and [views/dashboard.php](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/views/dashboard.php)

R2 S3 Signature V4 client in 
[src/R2/R2Client.php](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/src/R2/R2Client.php)
SQLite mirror, usage ledger, settings, local tags, permission labels, 
and signed URL history under 
[src/Repository](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/src/Repository)
Simple password auth and CSRF protection
Uploads, folders, sync, copy/move/delete, downloads, presigned URLs, 
CORS JSON management, and usage/billing-style summaries
Setup docs in 
[README.md](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/README.md) 
and config template in 
[.env.example](/Users/tyemeclifford/Documents/GH/CloudflareR2-Manager/.env.example)
Important R2 caveat: Cloudflare’s current R2 S3 compatibility docs say 
ACLs and object-tagging APIs are not implemented, so the app stores tags 
and permission labels locally in SQLite while keeping file bytes on R2. 
I used Cloudflare’s official docs for S3 compatibility, presigned URLs, 
and CORS.
This commit is contained in:
Ty Clifford
2026-06-20 13:57:21 -04:00
parent 1c7f697913
commit 3518555d1e
29 changed files with 3598 additions and 1 deletions
+44
View File
@@ -0,0 +1,44 @@
<?php
/** @var string $content */
/** @var string $appName */
/** @var array<int, array{type: string, message: string}> $flash */
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($appName) ?></title>
<link rel="stylesheet" href="assets/app.css">
</head>
<body>
<header class="topbar">
<a class="brand" href="index.php">
<span class="brand-mark">R2</span>
<span><?= htmlspecialchars($appName) ?></span>
</a>
<?php if (!empty($_SESSION['authenticated'])): ?>
<form method="post" class="logout-form">
<input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf ?? '') ?>">
<input type="hidden" name="action" value="logout">
<button type="submit" class="button button-ghost">Sign out</button>
</form>
<?php endif; ?>
</header>
<?php if (!empty($flash)): ?>
<section class="flash-stack" aria-live="polite">
<?php foreach ($flash as $message): ?>
<div class="flash flash-<?= htmlspecialchars($message['type']) ?>">
<?= htmlspecialchars($message['message']) ?>
</div>
<?php endforeach; ?>
</section>
<?php endif; ?>
<?= $content ?>
<script src="assets/app.js"></script>
</body>
</html>