All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m41s
- Implement storage backend deletion, which automatically resets default storage settings and user-specific overrides when a backend is removed. - Add unit tests covering the delete action and its cleanup side effects. - Improve admin UI responsiveness, fixing table scrolling, flex wrapping, and text truncation for long storage backend names. - Update security documentation to clarify trusted proxy configurations and explain how trusted proxies are protected from automatic bans.
137 lines
8.4 KiB
HTML
137 lines
8.4 KiB
HTML
{{define "admin_storage.html"}}{{template "base" .}}{{end}}
|
|
|
|
{{define "content"}}
|
|
<section class="app-shell admin-shell" aria-labelledby="admin-storage-title">
|
|
<aside class="app-sidebar">
|
|
<nav class="sidebar-nav">
|
|
<a class="sidebar-link" href="/admin">{{template "icon-dashboard" .}}<span>Overview</span></a>
|
|
<a class="sidebar-link" href="/admin/files">{{template "icon-folder" .}}<span>Files</span></a>
|
|
<a class="sidebar-link" href="/admin/users">{{template "icon-user-circle" .}}<span>Users</span></a>
|
|
<a class="sidebar-link" href="/admin/settings">{{template "icon-settings" .}}<span>Settings</span></a>
|
|
<a class="sidebar-link is-active" href="/admin/storage">{{template "icon-database" .}}<span>Storage</span></a>
|
|
<a class="sidebar-link" href="/admin/logs">{{template "icon-database" .}}<span>Logs</span></a>
|
|
<a class="sidebar-link" href="/admin/bans">{{template "icon-settings" .}}<span>Bans</span></a>
|
|
</nav>
|
|
<hr class="sidebar-sep">
|
|
<nav class="sidebar-nav">
|
|
<a class="sidebar-link" href="/app">{{template "icon-home-simple" .}}<span>My Files</span></a>
|
|
</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">{{template "icon-log-out" .}}<span>Sign out</span></button>
|
|
</form>
|
|
</aside>
|
|
|
|
<div class="app-main">
|
|
<div class="admin-header">
|
|
<div>
|
|
<p class="kicker">Operator console</p>
|
|
<h1 id="admin-storage-title">{{.Data.PageTitle}}</h1>
|
|
<p class="muted-copy">Local storage is always active. Remote backends are proxied through Warpbox.</p>
|
|
</div>
|
|
<a class="button button-primary" href="/admin/storage/new">{{template "icon-plus-circle" .}}<span>Add storage</span></a>
|
|
</div>
|
|
|
|
{{if .Data.Notice}}<p class="form-success">{{.Data.Notice}}</p>{{end}}
|
|
{{if .Data.Error}}<p class="form-error">{{.Data.Error}}</p>{{end}}
|
|
|
|
<div class="storage-ops-grid">
|
|
<form class="storage-op-card" action="/admin/storage/jobs/cleanup" method="post">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<strong>Run cleanup now</strong>
|
|
<span>Remove expired boxes and boxes that reached their download limit.</span>
|
|
<button class="button button-outline button-sm" type="submit">Run cleanup</button>
|
|
</form>
|
|
<form class="storage-op-card" action="/admin/storage/jobs/thumbnails" method="post">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<strong>Generate thumbnails now</strong>
|
|
<span>Scan active boxes and create missing image or video thumbnails.</span>
|
|
<button class="button button-outline button-sm" type="submit">Generate</button>
|
|
</form>
|
|
<form class="storage-op-card" action="/admin/storage/jobs/verify" method="post">
|
|
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
|
<strong>Verify storage now</strong>
|
|
<span>Test every enabled backend and update its last-test status.</span>
|
|
<button class="button button-outline button-sm" type="submit">Verify all</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="storage-stack">
|
|
{{range .Data.Storage}}
|
|
<div class="storage-card {{if eq .Config.ID "local"}}is-local{{end}}" data-storage-id="{{.Config.ID}}">
|
|
<div class="storage-card-header">
|
|
<div class="storage-card-identity">
|
|
<div class="storage-card-icon">
|
|
{{if eq .Config.Type "local"}}{{template "icon-hard-drive" $}}
|
|
{{else if eq .Config.Type "sftp"}}{{template "icon-database" $}}
|
|
{{else if eq .Config.Type "smb"}}{{template "icon-folder" $}}
|
|
{{else if eq .Config.Type "webdav"}}{{template "icon-cloud-sync" $}}
|
|
{{else}}{{template "icon-cloud-upload" $}}{{end}}
|
|
</div>
|
|
<div>
|
|
<strong class="storage-card-name">{{.Config.Name}}</strong>
|
|
<div class="storage-card-meta">
|
|
<span class="badge">{{if eq .Config.Provider "contabo"}}Contabo{{else if eq .Config.Type "sftp"}}SFTP{{else if eq .Config.Type "smb"}}Samba{{else if eq .Config.Type "webdav"}}WebDAV{{else if eq .Config.Type "s3"}}S3{{else if eq .Config.Type "local"}}Local files{{else}}{{.Config.Type}}{{end}}</span>
|
|
{{if eq .Config.ID "local"}}<span class="badge">Required</span>
|
|
{{else if .Config.Enabled}}<span class="badge badge-active">Enabled</span>
|
|
{{else}}<span class="badge badge-disabled">Disabled</span>{{end}}
|
|
{{if .InUseReason}}<span class="badge" title="{{.InUseReason}}">In use</span>{{end}}
|
|
{{if .UsageLabel}}<span class="storage-card-usage">{{.UsageLabel}}</span>{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="storage-card-actions">
|
|
{{if .CanSpeedTest}}
|
|
<a class="button button-outline button-sm" href="/admin/storage/{{.Config.ID}}/tests">Testing</a>
|
|
{{else}}
|
|
<form action="/admin/storage/{{.Config.ID}}/test" method="post">
|
|
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
|
<button class="button button-outline button-sm" type="submit">Test</button>
|
|
</form>
|
|
{{end}}
|
|
{{if ne .Config.ID "local"}}
|
|
<a class="button button-outline button-sm" href="/admin/storage/{{.Config.ID}}/edit">Edit</a>
|
|
<form action="/admin/storage/{{.Config.ID}}/delete" method="post">
|
|
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
|
<button class="button button-danger button-sm" type="submit" data-storage-delete-warning="{{.Config.Name}}">Delete</button>
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="storage-card-summary">
|
|
{{if eq .Config.Type "local"}}
|
|
<div class="storage-detail"><span>Path</span><code>{{.Config.LocalPath}}</code></div>
|
|
{{else if or (eq .Config.Type "s3") (eq .Config.Provider "contabo")}}
|
|
{{if .Config.Endpoint}}<div class="storage-detail"><span>Endpoint</span><span>{{.Config.Endpoint}}</span></div>{{end}}
|
|
{{if .Config.Bucket}}<div class="storage-detail"><span>Bucket</span><span>{{.Config.Bucket}}</span></div>{{end}}
|
|
{{if .Config.Region}}<div class="storage-detail"><span>Region</span><span>{{.Config.Region}}</span></div>{{end}}
|
|
{{if .Config.AccessKey}}<div class="storage-detail"><span>Access key</span><span>{{.Config.AccessKey}}</span></div>{{end}}
|
|
{{else if eq .Config.Type "sftp"}}
|
|
{{if .Config.Host}}<div class="storage-detail"><span>Host</span><span>{{.Config.Host}}{{if .Config.Port}}:{{.Config.Port}}{{end}}</span></div>{{end}}
|
|
{{if .Config.Username}}<div class="storage-detail"><span>Username</span><span>{{.Config.Username}}</span></div>{{end}}
|
|
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
|
|
{{else if eq .Config.Type "smb"}}
|
|
{{if .Config.Host}}<div class="storage-detail"><span>Host</span><span>{{if .Config.Domain}}{{.Config.Domain}}\{{end}}{{.Config.Username}}@{{.Config.Host}}/{{.Config.Share}}</span></div>{{end}}
|
|
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
|
|
{{else if eq .Config.Type "webdav"}}
|
|
{{if .Config.Endpoint}}<div class="storage-detail"><span>URL</span><span>{{.Config.Endpoint}}</span></div>{{end}}
|
|
{{if .Config.Username}}<div class="storage-detail"><span>Username</span><span>{{.Config.Username}}</span></div>{{end}}
|
|
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
|
|
{{end}}
|
|
{{if not (.Config.LastTestedAt.IsZero)}}
|
|
<div class="storage-detail storage-detail-test {{if .Config.LastTestSuccess}}is-ok{{else}}is-err{{end}}">
|
|
<span>Last test</span>
|
|
<span>{{.Config.LastTestedAt.Format "Jan 2, 15:04"}} · {{if .Config.LastTestSuccess}}Passed{{else}}{{if .Config.LastTestError}}{{.Config.LastTestError}}{{else}}Failed{{end}}{{end}}</span>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{{end}}
|