feat(storage): add S3 backend support and advanced upload limits

- Introduce S3-compatible storage backend support using minio-go.
- Add configuration options for local storage limits, box limits, and rate limiting.
- Implement storage backend selection (local vs S3) for anonymous and registered users.
- Add an `/admin/storage` management interface.
- Update documentation and environment examples with the new configuration variables.
This commit is contained in:
2026-05-31 02:14:10 +03:00
parent 830d2a885c
commit c3558fd353
34 changed files with 2668 additions and 168 deletions

View File

@@ -8,6 +8,7 @@
<a class="sidebar-link" href="/admin/files">Files</a>
<a class="sidebar-link is-active" href="/admin/users">Users</a>
<a class="sidebar-link" href="/admin/settings">Settings</a>
<a class="sidebar-link" href="/admin/storage">Storage</a>
</nav>
<hr class="sidebar-sep">
<nav class="sidebar-nav">
@@ -15,6 +16,7 @@
</nav>
<hr class="sidebar-sep">
<form class="sidebar-logout" action="/admin/logout" method="post">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<button class="button button-outline" type="submit">Sign out</button>
</form>
</aside>
@@ -43,6 +45,7 @@
</div>
{{end}}
<form class="inline-controls" action="/admin/invites" method="post">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<label><span>Email</span><input type="email" name="email" required></label>
<label><span>Role</span><select name="role"><option value="user">User</option><option value="admin">Admin</option></select></label>
<button class="button button-primary" type="submit">Create invite</button>
@@ -66,6 +69,7 @@
<th>Status</th>
<th>Storage</th>
<th>Today</th>
<th>Storage backend</th>
<th>Joined</th>
<th>Actions</th>
</tr>
@@ -79,28 +83,29 @@
<td><span class="badge {{if eq .Status "active"}}badge-active{{else}}badge-disabled{{end}}">{{.Status}}</span></td>
<td>{{.StorageUsed}} / {{.StorageQuota}}</td>
<td>{{.DailyUsed}}</td>
<td>{{.StorageBackend}}</td>
<td>{{.CreatedAt}}</td>
<td class="table-actions">
{{if eq .Status "disabled"}}
<form action="/admin/users/{{.ID}}/disable?disabled=false" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-outline button-sm" type="submit">Reactivate</button>
</form>
{{else}}
<form action="/admin/users/{{.ID}}/disable" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-danger button-sm" type="submit">Disable</button>
</form>
{{end}}
<form action="/admin/users/{{.ID}}/reset" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-outline button-sm" type="submit">Reset link</button>
</form>
<form class="quota-form" action="/admin/users/{{.ID}}/quota" method="post">
<input name="storage_quota_mb" placeholder="Quota MB" title="Override storage quota in MB (leave blank to clear override)">
<button class="button button-outline button-sm" type="submit">Set</button>
</form>
<a class="button button-outline button-sm" href="/admin/users/{{.ID}}/edit">Edit</a>
</td>
</tr>
{{else}}
<tr><td colspan="8" class="muted-copy">No users yet.</td></tr>
<tr><td colspan="9" class="muted-copy">No users yet.</td></tr>
{{end}}
</tbody>
</table>