2026-05-25 15:36:49 +03:00
package handlers
import (
"net/http"
2026-05-31 02:14:10 +03:00
"strconv"
2026-05-25 15:36:49 +03:00
2026-05-30 15:42:35 +03:00
"warpbox.dev/backend/libs/services"
2026-05-25 15:36:49 +03:00
"warpbox.dev/backend/libs/web"
)
type homeData struct {
2026-05-31 15:30:53 +03:00
MaxUploadSize string
LimitSummary string
Collections [ ] collectionView
IsAdmin bool
AnonymousOpen bool
ExpiryOptions [ ] expiryOption
DefaultExpiryMinutes int
}
type expiryOption struct {
Minutes int
Label string
2026-05-25 15:36:49 +03:00
}
func ( a * App ) Home ( w http . ResponseWriter , r * http . Request ) {
2026-05-30 15:42:35 +03:00
currentUser := a . currentPublicUser ( r )
var collections [ ] collectionView
var isAdmin bool
2026-05-30 17:23:20 +03:00
var user services . User
var loggedIn bool
if current , ok := a . currentUser ( r ) ; ok {
user = current
loggedIn = true
2026-05-30 15:42:35 +03:00
isAdmin = user . Role == services . UserRoleAdmin
userCollections , err := a . authService . ListCollections ( user . ID )
if err == nil {
collections = make ( [ ] collectionView , 0 , len ( userCollections ) )
for _ , collection := range userCollections {
collections = append ( collections , collectionView { ID : collection . ID , Name : collection . Name } )
}
}
}
2026-05-30 17:23:20 +03:00
settings , err := a . settingsService . UploadPolicy ( )
if err != nil {
http . Error ( w , "unable to load upload policy" , http . StatusInternalServerError )
return
}
2026-06-01 11:30:38 +03:00
actor := "anonymous"
if loggedIn {
actor = "user"
}
a . logger . Info ( "upload page viewed" , withRequestLogAttrs ( r ,
"source" , "page" ,
"severity" , "user_activity" ,
"code" , 2500 ,
"actor" , actor ,
"user_id" , user . ID ,
) ... )
2026-05-30 17:23:20 +03:00
maxUploadSize , limitSummary := a . homeUploadPolicyLabels ( settings , user , loggedIn , isAdmin )
2026-05-31 15:30:53 +03:00
expiryOptions , defaultExpiry := a . homeExpiryOptions ( settings , user , loggedIn , isAdmin )
2026-05-30 17:23:20 +03:00
a . renderPage ( w , r , http . StatusOK , "home.html" , web . PageData {
2026-06-03 12:15:49 +03:00
Title : "Upload your files" ,
Description : "Upload and share files fast. Drop a file, get a link — private, temporary transfers that expire on your terms." ,
CanonicalURL : absoluteURL ( r , "/" ) ,
ImageURL : absoluteURL ( r , "/static/og-default.png" ) ,
ImageAlt : "Warp Box — simple file sharing and fast downloads" ,
CurrentUser : currentUser ,
2026-05-25 15:36:49 +03:00
Data : homeData {
2026-05-31 15:30:53 +03:00
MaxUploadSize : maxUploadSize ,
LimitSummary : limitSummary ,
Collections : collections ,
IsAdmin : isAdmin ,
AnonymousOpen : settings . AnonymousUploadsEnabled ,
ExpiryOptions : expiryOptions ,
DefaultExpiryMinutes : defaultExpiry ,
2026-05-25 15:36:49 +03:00
} ,
} )
}
2026-05-30 17:23:20 +03:00
2026-05-31 15:30:53 +03:00
// homeExpiryOptions builds the expiry ladder offered on the upload form, capped to
// the viewer's effective maximum retention. Admins have no cap (the dropdown is
// still capped at 365 days for sanity; the API accepts any value for admins).
func ( a * App ) homeExpiryOptions ( settings services . UploadPolicySettings , user services . User , loggedIn , isAdmin bool ) ( [ ] expiryOption , int ) {
maxDays := settings . AnonymousMaxDays
unlimited := false
switch {
case isAdmin :
unlimited = true
case loggedIn :
maxDays = a . settingsService . EffectivePolicyForUser ( settings , user ) . MaxDays
2026-05-31 22:40:48 +03:00
// A negative per-user MaxDays override means unlimited retention.
if maxDays < 0 {
unlimited = true
}
2026-05-31 15:30:53 +03:00
}
return buildExpiryOptions ( maxDays , unlimited )
}
func buildExpiryOptions ( maxDays int , unlimited bool ) ( [ ] expiryOption , int ) {
2026-06-02 22:41:59 +03:00
ladder := [ ] int { 60 , 360 , 720 , 1440 , 2880 , 4320 , 7200 , 10080 , 14400 , 20160 , 43200 , 86400 , 129600 , 259200 , 525600 }
2026-05-31 15:30:53 +03:00
capMinutes := maxDays * 24 * 60
if unlimited || capMinutes <= 0 {
capMinutes = 525600
}
options := make ( [ ] expiryOption , 0 , len ( ladder ) + 1 )
seen := make ( map [ int ] bool )
for _ , minutes := range ladder {
if minutes > capMinutes {
break
}
options = append ( options , expiryOption { Minutes : minutes , Label : expiryLabel ( minutes ) } )
seen [ minutes ] = true
}
// Always offer the exact cap as a final choice (e.g. a 15-day limit).
if ! unlimited && ! seen [ capMinutes ] {
options = append ( options , expiryOption { Minutes : capMinutes , Label : expiryLabel ( capMinutes ) } )
}
if len ( options ) == 0 {
options = append ( options , expiryOption { Minutes : capMinutes , Label : expiryLabel ( capMinutes ) } )
}
2026-05-31 22:40:48 +03:00
// Unlimited uploaders can pick "never expires" (sentinel -1) after the ladder.
if unlimited {
options = append ( options , expiryOption { Minutes : - 1 , Label : "Unlimited (never expires)" } )
}
2026-05-31 15:30:53 +03:00
// Default to 24h when available, otherwise the smallest option offered.
defaultMinutes := options [ 0 ] . Minutes
if seen [ 1440 ] {
defaultMinutes = 1440
}
return options , defaultMinutes
}
func expiryLabel ( minutes int ) string {
switch {
case minutes < 60 :
return strconv . Itoa ( minutes ) + " minutes"
case minutes < 1440 :
hours := minutes / 60
if hours == 1 {
return "1 hour"
}
return strconv . Itoa ( hours ) + " hours"
case minutes == 1440 :
return "24 hours"
default :
days := minutes / 1440
if days == 1 {
return "1 day"
}
return strconv . Itoa ( days ) + " days"
}
}
2026-05-30 17:23:20 +03:00
func ( a * App ) homeUploadPolicyLabels ( settings services . UploadPolicySettings , user services . User , loggedIn , isAdmin bool ) ( string , string ) {
if isAdmin {
return "No file size limit" , "Admin uploads bypass storage and daily caps."
}
if ! loggedIn {
if ! settings . AnonymousUploadsEnabled {
return "Anonymous uploads disabled" , "Sign in to upload files."
}
2026-05-31 02:14:10 +03:00
return services . FormatMegabytesLabel ( settings . AnonymousMaxUploadMB ) , "Daily anonymous cap: " + services . FormatMegabytesLabel ( settings . AnonymousDailyUploadMB ) + " per IP · " + strconv . Itoa ( settings . AnonymousMaxDays ) + " day max."
2026-05-30 17:23:20 +03:00
}
2026-05-31 02:14:10 +03:00
policy := a . settingsService . EffectivePolicyForUser ( settings , user )
maxUpload := a . uploadService . MaxUploadSizeLabel ( )
2026-05-31 14:01:38 +03:00
if policy . MaxUploadMB < 0 {
maxUpload = "unlimited"
} else if policy . MaxUploadMB > 0 {
2026-05-31 02:14:10 +03:00
maxUpload = services . FormatMegabytesLabel ( policy . MaxUploadMB )
2026-05-30 17:23:20 +03:00
}
2026-05-31 02:14:10 +03:00
quota := "unlimited"
if policy . StorageQuotaSet {
quota = services . FormatMegabytesLabel ( policy . StorageQuotaMB )
}
2026-05-31 22:40:48 +03:00
expiryLimit := strconv . Itoa ( policy . MaxDays ) + " day max."
if policy . MaxDays < 0 {
expiryLimit = "no expiry limit."
}
return maxUpload , "Daily cap: " + services . FormatMegabytesLabel ( policy . DailyUploadMB ) + " · Storage quota: " + quota + " · " + expiryLimit
2026-05-30 17:23:20 +03:00
}