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

@@ -33,6 +33,10 @@ func (a *App) Register(w http.ResponseWriter, r *http.Request) {
}
func (a *App) RegisterPost(w http.ResponseWriter, r *http.Request) {
if !a.rateLimiter.Allow("register:"+uploadClientIP(r), 10, time.Minute, time.Now().UTC()) {
a.renderAuth(w, r, http.StatusTooManyRequests, authPageData{Mode: "register", Error: "Too many registration attempts."})
return
}
if err := r.ParseForm(); err != nil {
a.renderAuth(w, r, http.StatusBadRequest, authPageData{Mode: "register", Error: "Unable to read form."})
return
@@ -55,6 +59,10 @@ func (a *App) Login(w http.ResponseWriter, r *http.Request) {
}
func (a *App) LoginPost(w http.ResponseWriter, r *http.Request) {
if !a.rateLimiter.Allow("login:"+uploadClientIP(r), 10, time.Minute, time.Now().UTC()) {
a.renderAuth(w, r, http.StatusTooManyRequests, authPageData{Mode: "login", Error: "Too many login attempts."})
return
}
if err := r.ParseForm(); err != nil {
a.renderAuth(w, r, http.StatusBadRequest, authPageData{Mode: "login", Error: "Unable to read form."})
return
@@ -75,6 +83,9 @@ func (a *App) LoginPost(w http.ResponseWriter, r *http.Request) {
}
func (a *App) Logout(w http.ResponseWriter, r *http.Request) {
if !a.validateCSRF(w, r) {
return
}
if cookie, err := r.Cookie(userSessionCookieName); err == nil {
_ = a.authService.Logout(cookie.Value)
}
@@ -126,7 +137,7 @@ func (a *App) AccountSettings(w http.ResponseWriter, r *http.Request) {
func (a *App) ChangePassword(w http.ResponseWriter, r *http.Request) {
user, ok := a.requireUser(w, r)
if !ok {
if !ok || !a.validateCSRF(w, r) {
return
}
if err := r.ParseForm(); err != nil {