package handlers import ( "fmt" "net/http" "os" "warpbox.dev/backend/libs/helpers" "warpbox.dev/backend/libs/services" "warpbox.dev/backend/libs/web" ) type dashboardData struct { User services.PublicUser Collections []collectionView Boxes []userBoxView StorageUsed string MaxUploadSize string Selected string LastInviteURL string } type collectionView struct { ID string Name string } type userBoxView struct { ID string Title string CollectionID string CollectionName string FileCount int Size string CreatedAt string ExpiresAt string URL string } func (a *App) Dashboard(w http.ResponseWriter, r *http.Request) { user, ok := a.requireUser(w, r) if !ok { return } collections, err := a.authService.ListCollections(user.ID) if err != nil { http.Error(w, "unable to load collections", http.StatusInternalServerError) return } collectionNames := map[string]string{} collectionViews := make([]collectionView, 0, len(collections)) for _, collection := range collections { collectionNames[collection.ID] = collection.Name collectionViews = append(collectionViews, collectionView{ID: collection.ID, Name: collection.Name}) } boxes, err := a.uploadService.UserBoxes(user.ID, collectionNames) if err != nil { http.Error(w, "unable to load boxes", http.StatusInternalServerError) return } storageUsed, err := a.uploadService.UserStorageUsed(user.ID) if err != nil { http.Error(w, "unable to load storage usage", http.StatusInternalServerError) return } selected := r.URL.Query().Get("collection") boxViews := make([]userBoxView, 0, len(boxes)) for _, row := range boxes { if selected != "" && row.Box.CollectionID != selected { continue } title := row.Box.Title if title == "" { title = fmt.Sprintf("%d file upload", len(row.Box.Files)) } boxViews = append(boxViews, userBoxView{ ID: row.Box.ID, Title: title, CollectionID: row.Box.CollectionID, CollectionName: row.CollectionName, FileCount: len(row.Box.Files), Size: row.TotalSizeLabel, CreatedAt: row.Box.CreatedAt.Format("Jan 2 15:04"), ExpiresAt: row.Box.ExpiresAt.Format("Jan 2 15:04"), URL: "/d/" + row.Box.ID, }) } a.renderPage(w, r, http.StatusOK, "dashboard.html", web.PageData{ Title: "My files", Description: "Your Warpbox personal file space.", CurrentUser: a.authService.PublicUser(user), Data: dashboardData{ User: a.authService.PublicUser(user), Collections: collectionViews, Boxes: boxViews, StorageUsed: helpers.FormatBytes(storageUsed), MaxUploadSize: a.uploadService.MaxUploadSizeLabel(), Selected: selected, }, }) } func (a *App) CreateCollection(w http.ResponseWriter, r *http.Request) { user, ok := a.requireUser(w, r) if !ok || !a.validateCSRF(w, r) { return } if err := r.ParseForm(); err != nil { http.Redirect(w, r, "/app", http.StatusSeeOther) return } if _, err := a.authService.CreateCollection(user.ID, r.FormValue("name")); err != nil { a.logger.Warn("collection create failed", "source", "user_activity", "severity", "warn", "code", 4410, "user_id", user.ID, "error", err.Error()) } else { a.logger.Info("collection created", "source", "user_activity", "severity", "user_activity", "code", 2410, "user_id", user.ID, "name", r.FormValue("name")) } http.Redirect(w, r, "/app", http.StatusSeeOther) } func (a *App) RenameUserBox(w http.ResponseWriter, r *http.Request) { user, ok := a.requireUser(w, r) if !ok || !a.validateCSRF(w, r) { return } if err := r.ParseForm(); err != nil { http.Redirect(w, r, "/app", http.StatusSeeOther) return } if err := a.uploadService.RenameOwnedBox(r.PathValue("boxID"), user.ID, r.FormValue("title")); err != nil { a.logger.Warn("owned box rename failed", "source", "user_activity", "severity", "warn", "code", 4411, "user_id", user.ID, "box_id", r.PathValue("boxID"), "error", err.Error()) a.handleUserBoxError(w, r, err) return } a.logger.Info("owned box renamed", "source", "user_activity", "severity", "user_activity", "code", 2411, "user_id", user.ID, "box_id", r.PathValue("boxID")) http.Redirect(w, r, "/app", http.StatusSeeOther) } func (a *App) MoveUserBox(w http.ResponseWriter, r *http.Request) { user, ok := a.requireUser(w, r) if !ok || !a.validateCSRF(w, r) { return } if err := r.ParseForm(); err != nil { http.Redirect(w, r, "/app", http.StatusSeeOther) return } collectionID := r.FormValue("collection_id") if !a.authService.CollectionOwnedBy(collectionID, user.ID) { a.logger.Warn("owned box move invalid collection", "source", "user_activity", "severity", "warn", "code", 4412, "user_id", user.ID, "box_id", r.PathValue("boxID"), "collection_id", collectionID) http.Error(w, "collection not found", http.StatusForbidden) return } if err := a.uploadService.MoveOwnedBox(r.PathValue("boxID"), user.ID, collectionID); err != nil { a.logger.Warn("owned box move failed", "source", "user_activity", "severity", "warn", "code", 4413, "user_id", user.ID, "box_id", r.PathValue("boxID"), "error", err.Error()) a.handleUserBoxError(w, r, err) return } a.logger.Info("owned box moved", "source", "user_activity", "severity", "user_activity", "code", 2412, "user_id", user.ID, "box_id", r.PathValue("boxID"), "collection_id", collectionID) http.Redirect(w, r, "/app", http.StatusSeeOther) } func (a *App) DeleteUserBox(w http.ResponseWriter, r *http.Request) { user, ok := a.requireUser(w, r) if !ok || !a.validateCSRF(w, r) { return } if err := a.uploadService.DeleteOwnedBox(r.PathValue("boxID"), user.ID); err != nil { a.logger.Warn("owned box delete failed", "source", "user_activity", "severity", "warn", "code", 4414, "user_id", user.ID, "box_id", r.PathValue("boxID"), "error", err.Error()) a.handleUserBoxError(w, r, err) return } a.logger.Info("owned box deleted", "source", "user_activity", "severity", "user_activity", "code", 2413, "user_id", user.ID, "box_id", r.PathValue("boxID")) http.Redirect(w, r, "/app", http.StatusSeeOther) } func (a *App) handleUserBoxError(w http.ResponseWriter, r *http.Request, err error) { if os.IsPermission(err) { http.Error(w, "not allowed", http.StatusForbidden) return } if os.IsNotExist(err) { http.NotFound(w, r) return } http.Error(w, "unable to update box", http.StatusInternalServerError) }