2026-05-31 02:14:10 +03:00
{{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" >
2026-05-31 04:54:27 +03:00
< 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 >
2026-05-31 02:14:10 +03:00
</ nav >
< hr class = "sidebar-sep" >
2026-05-31 04:54:27 +03:00
< nav class = "sidebar-nav" >
< a class = "sidebar-link" href = "/app" > {{template "icon-home-simple" .}}< span > My Files</ span ></ a >
</ nav >
2026-05-31 02:14:10 +03:00
< hr class = "sidebar-sep" >
< form class = "sidebar-logout" action = "/admin/logout" method = "post" >
< input type = "hidden" name = "csrf_token" value = "{{.CSRFToken}}" >
2026-05-31 04:54:27 +03:00
< button class = "button button-outline" type = "submit" > {{template "icon-log-out" .}}< span > Sign out</ span ></ button >
2026-05-31 02:14:10 +03:00
</ 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 >
2026-05-31 04:54:27 +03:00
< p class = "muted-copy" > Local storage is always active. Remote backends are proxied through Warpbox.</ p >
2026-05-31 02:14:10 +03:00
</ div >
</ div >
{{if .Data.Error}}< p class = "form-error" > {{.Data.Error}}</ p > {{end}}
2026-05-31 04:54:27 +03:00
< div class = "storage-stack" >
2026-05-31 02:14:10 +03:00
2026-05-31 04:54:27 +03:00
{{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 .UsageLabel}}< span class = "storage-card-usage" > {{.UsageLabel}}</ span > {{end}}
</ div >
</ div >
</ div >
< div class = "storage-card-actions" >
< 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 >
{{if ne .Config.ID "local"}}
< button class = "button button-outline button-sm storage-edit-trigger" type = "button" > Edit</ button >
{{if .Config.Enabled}}
< form action = "/admin/storage/{{.Config.ID}}/disable" method = "post" >
< input type = "hidden" name = "csrf_token" value = "{{$.CSRFToken}}" >
< button class = "button button-outline button-sm" type = "submit" {{ if . InUse }} disabled title = "Backend is in use" {{ end }} > Disable</ button >
</ form >
{{end}}
< 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" {{ if . InUse }} disabled title = "Backend is in use" {{ end }} > Delete</ button >
</ form >
{{end}}
2026-05-31 02:14:10 +03:00
</ div >
</ div >
2026-05-31 04:54:27 +03:00
{{/* View-mode summary */}}
< 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 >
2026-05-31 02:14:10 +03:00
</ div >
2026-05-31 04:54:27 +03:00
{{end}}
</ div >
{{/* Edit-mode form — hidden via CSS until .is-editing */}}
{{if ne .Config.ID "local"}}
< div class = "storage-card-body" >
< form action = "/admin/storage/{{.Config.ID}}/edit" method = "post" class = "storage-card-fields" >
< input type = "hidden" name = "csrf_token" value = "{{$.CSRFToken}}" >
< label >< span > Storage kind</ span >
< select name = "provider" data-storage-provider >
< option value = "s3" {{ if or ( eq . Config . Provider " s3 ") ( eq . Config . Provider "")}} selected {{ end }} > S3 bucket</ option >
< option value = "contabo" {{ if eq . Config . Provider " contabo "}} selected {{ end }} > Contabo Object Storage</ option >
< option value = "sftp" {{ if eq . Config . Provider " sftp "}} selected {{ end }} > SFTP</ option >
< option value = "smb" {{ if eq . Config . Provider " smb "}} selected {{ end }} > Samba</ option >
< option value = "webdav" {{ if eq . Config . Provider " webdav "}} selected {{ end }} > WebDAV</ option >
</ select >
</ label >
< label >< span > Name</ span >< input name = "name" value = "{{.Config.Name}}" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Endpoint</ span >< input name = "endpoint" value = "{{.Config.Endpoint}}" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Region</ span >< input name = "region" value = "{{.Config.Region}}" ></ label >
< label data-provider-fields = "s3 contabo" >< span > Bucket</ span >< input name = "bucket" value = "{{.Config.Bucket}}" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Access key</ span >< input name = "access_key" value = "{{.Config.AccessKey}}" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Secret key</ span >< input name = "secret_key" type = "password" placeholder = "Leave unchanged" ></ label >
< label class = "checkbox-field" data-provider-fields = "s3 contabo" >< input type = "checkbox" name = "use_ssl" {{ if . Config . UseSSL }} checked {{ end }} >< span > Use TLS</ span ></ label >
< label class = "checkbox-field" data-provider-fields = "s3 contabo" >< input type = "checkbox" name = "path_style" {{ if . Config . PathStyle }} checked {{ end }} >< span > Path-style lookup</ span ></ label >
< label data-provider-fields = "sftp smb" >< span > Host</ span >< input name = "host" value = "{{.Config.Host}}" required ></ label >
< label data-provider-fields = "sftp smb" >< span > Port</ span >< input type = "number" name = "port" min = "1" value = "{{.Config.Port}}" ></ label >
< label data-provider-fields = "smb" >< span > Share</ span >< input name = "share" value = "{{.Config.Share}}" required ></ label >
< label data-provider-fields = "smb" >< span > Domain</ span >< input name = "domain" value = "{{.Config.Domain}}" placeholder = "Optional" ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Username</ span >< input name = "username" value = "{{.Config.Username}}" required ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Password</ span >< input name = "password" type = "password" placeholder = "Leave unchanged" ></ label >
< label data-provider-fields = "sftp" >< span > Private key</ span >< textarea name = "private_key" rows = "4" placeholder = "Leave unchanged" ></ textarea ></ label >
< label data-provider-fields = "sftp" >< span > SSH host key</ span >< textarea name = "host_key" rows = "3" placeholder = "Optional" > {{.Config.HostKey}}</ textarea ></ label >
< label data-provider-fields = "webdav" >< span > WebDAV URL</ span >< input name = "endpoint" value = "{{.Config.Endpoint}}" placeholder = "https://files.example.com/webdav" ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Remote path</ span >< input name = "remote_path" value = "{{.Config.RemotePath}}" placeholder = "/srv/warpbox" ></ label >
< div class = "storage-card-edit-bar" >
< button class = "button button-primary button-sm" type = "submit" > Save changes</ button >
< button class = "button button-outline button-sm storage-cancel-trigger" type = "button" > Cancel</ button >
</ div >
</ form >
</ div >
{{end}}
2026-05-31 02:14:10 +03:00
</ div >
2026-05-31 04:54:27 +03:00
{{end}}
{{/* Add storage section */}}
< div class = "storage-add-section" >
< div class = "storage-add-controls" >
< button class = "button button-outline storage-add-trigger" type = "button" >
{{template "icon-plus-circle" .}}
< span > Add storage</ span >
</ button >
</ div >
< div class = "storage-type-picker" hidden >
< p class = "muted-copy" style = "margin:0 0 0.75rem" > Choose a backend type</ p >
< div class = "storage-type-grid" >
< button class = "storage-type-option" type = "button" data-provider = "s3" >
{{template "icon-cloud-upload" .}}
< strong > S3 Bucket</ strong >
< span > Generic S3-compatible object storage</ span >
</ button >
< button class = "storage-type-option" type = "button" data-provider = "contabo" >
{{template "icon-cloud-upload" .}}
< strong > Contabo Object Storage</ strong >
< span > Optimized settings for Contabo COS</ span >
</ button >
< button class = "storage-type-option" type = "button" data-provider = "sftp" >
{{template "icon-database" .}}
< strong > SFTP</ strong >
< span > SSH file transfer to a server or NAS</ span >
</ button >
< button class = "storage-type-option" type = "button" data-provider = "smb" >
{{template "icon-folder" .}}
< strong > Samba / SMB</ strong >
< span > Windows share or network attached storage</ span >
</ button >
< button class = "storage-type-option" type = "button" data-provider = "webdav" >
{{template "icon-cloud-sync" .}}
< strong > WebDAV</ strong >
< span > Nextcloud, ownCloud, or any WebDAV server</ span >
</ button >
</ div >
</ div >
< div class = "storage-new-card storage-card is-editing" hidden >
< div class = "storage-card-header" >
< div class = "storage-card-identity" >
< div class = "storage-card-icon storage-new-icon" > {{template "icon-cloud-upload" .}}</ div >
< div >
< strong class = "storage-card-name storage-new-label" > New storage backend</ strong >
< div class = "storage-card-meta" >
< span class = "badge storage-new-type-badge" > S3 bucket</ span >
</ div >
</ div >
</ div >
</ div >
< div class = "storage-card-body" >
< form action = "/admin/storage/s3" method = "post" class = "storage-card-fields" >
< input type = "hidden" name = "csrf_token" value = "{{.CSRFToken}}" >
< label >< span > Storage kind</ span >
< select name = "provider" data-storage-provider >
< option value = "s3" > S3 bucket</ option >
< option value = "contabo" > Contabo Object Storage</ option >
< option value = "sftp" > SFTP</ option >
< option value = "smb" > Samba</ option >
< option value = "webdav" > WebDAV</ option >
</ select >
</ label >
< label >< span > Name</ span >< input name = "name" placeholder = "My storage" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Endpoint</ span >< input name = "endpoint" placeholder = "s3.example.com" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Region</ span >< input name = "region" placeholder = "us-east-1" ></ label >
< label data-provider-fields = "s3 contabo" >< span > Bucket</ span >< input name = "bucket" placeholder = "my-bucket" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Access key</ span >< input name = "access_key" required ></ label >
< label data-provider-fields = "s3 contabo" >< span > Secret key</ span >< input name = "secret_key" type = "password" required ></ label >
< label class = "checkbox-field" data-provider-fields = "s3 contabo" >< input type = "checkbox" name = "use_ssl" checked >< span > Use TLS</ span ></ label >
< label class = "checkbox-field" data-provider-fields = "s3 contabo" >< input type = "checkbox" name = "path_style" >< span > Path-style lookup</ span ></ label >
< label data-provider-fields = "sftp smb" >< span > Host</ span >< input name = "host" placeholder = "files.example.com" required ></ label >
< label data-provider-fields = "sftp smb" >< span > Port</ span >< input type = "number" name = "port" min = "1" ></ label >
< label data-provider-fields = "smb" >< span > Share</ span >< input name = "share" placeholder = "uploads" required ></ label >
< label data-provider-fields = "smb" >< span > Domain</ span >< input name = "domain" placeholder = "Optional" ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Username</ span >< input name = "username" required ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Password</ span >< input name = "password" type = "password" ></ label >
< label data-provider-fields = "sftp" >< span > Private key</ span >< textarea name = "private_key" rows = "4" placeholder = "Optional private key" ></ textarea ></ label >
< label data-provider-fields = "sftp" >< span > SSH host key</ span >< textarea name = "host_key" rows = "3" placeholder = "Optional pinned host key" ></ textarea ></ label >
< label data-provider-fields = "webdav" >< span > WebDAV URL</ span >< input name = "endpoint" placeholder = "https://files.example.com/webdav" ></ label >
< label data-provider-fields = "sftp smb webdav" >< span > Remote path</ span >< input name = "remote_path" placeholder = "/srv/warpbox" ></ label >
< div class = "storage-card-edit-bar" >
< button class = "button button-primary button-sm" type = "submit" > Add storage</ button >
< button class = "button button-outline button-sm storage-new-cancel" type = "button" > Cancel</ button >
</ div >
</ form >
</ div >
</ div >
</ div >
2026-05-31 02:14:10 +03:00
</ div >
</ div >
</ section >
{{end}}