This commit is contained in:
@@ -65,13 +65,14 @@ func newApp(dbPath, dataDir string, web fs.FS) (*App, string, error) {
|
||||
}
|
||||
|
||||
func (a *App) ensureAdmin() (string, error) {
|
||||
var count int64
|
||||
if err := a.db.Model(&User{}).Count(&count).Error; err != nil {
|
||||
var admin User
|
||||
if err := a.db.First(&admin, 1).Error; err == nil {
|
||||
return "", a.db.Model(&admin).Updates(map[string]any{
|
||||
"can_manage_users": true, "can_manage_files": true, "can_upload": true, "can_advanced_upload": true,
|
||||
}).Error
|
||||
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return "", err
|
||||
}
|
||||
if count != 0 {
|
||||
return "", nil
|
||||
}
|
||||
password, err := randomToken(18)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -80,7 +81,7 @@ func (a *App) ensureAdmin() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
admin := User{ID: 1, Username: "admin", PasswordHash: string(hash), CanManageUsers: true, CanUpload: true, CanAdvancedUpload: true, MustChangePassword: true}
|
||||
admin = User{ID: 1, Username: "admin", PasswordHash: string(hash), CanManageUsers: true, CanManageFiles: true, CanUpload: true, CanAdvancedUpload: true, MustChangePassword: true}
|
||||
if err := a.db.Create(&admin).Error; err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -96,7 +97,9 @@ func (a *App) router() *gin.Engine {
|
||||
r.GET("/api/auth/me", a.auth(false), a.me)
|
||||
r.PUT("/api/auth/password", a.auth(false), a.changePassword)
|
||||
r.GET("/api/pickup/:hash", a.verifyPickup)
|
||||
r.GET("/api/files", a.auth(true), a.manageFiles, a.listManagedFiles)
|
||||
r.POST("/api/files", a.auth(true), a.uploadFile)
|
||||
r.DELETE("/api/files/:id", a.auth(true), a.manageFiles, a.deleteManagedFile)
|
||||
r.GET("/api/users", a.auth(true), a.manageUsers, a.listUsers)
|
||||
r.POST("/api/users", a.auth(true), a.manageUsers, a.createUser)
|
||||
r.PUT("/api/users/:id", a.auth(true), a.manageUsers, a.updateUser)
|
||||
@@ -146,10 +149,21 @@ func (a *App) manageUsers(c *gin.Context) {
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func permissionsWithin(actor *User, canManageUsers, canUpload, canAdvancedUpload bool) bool {
|
||||
func (a *App) manageFiles(c *gin.Context) {
|
||||
if !currentUser(c).CanManageFiles {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "没有文件管理权限"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
|
||||
func permissionsWithin(actor *User, canManageUsers, canManageFiles, canUpload, canAdvancedUpload bool) bool {
|
||||
if canManageUsers && !actor.CanManageUsers {
|
||||
return false
|
||||
}
|
||||
if canManageFiles && !actor.CanManageFiles {
|
||||
return false
|
||||
}
|
||||
if canAdvancedUpload && !actor.CanAdvancedUpload {
|
||||
return false
|
||||
}
|
||||
@@ -157,11 +171,11 @@ func permissionsWithin(actor *User, canManageUsers, canUpload, canAdvancedUpload
|
||||
}
|
||||
|
||||
func canManageTarget(actor, target *User) bool {
|
||||
return permissionsWithin(actor, target.CanManageUsers, target.CanUpload, target.CanAdvancedUpload)
|
||||
return permissionsWithin(actor, target.CanManageUsers, target.CanManageFiles, target.CanUpload, target.CanAdvancedUpload)
|
||||
}
|
||||
|
||||
func requireAssignablePermissions(c *gin.Context, canManageUsers, canUpload, canAdvancedUpload bool) bool {
|
||||
if permissionsWithin(currentUser(c), canManageUsers, canUpload, canAdvancedUpload) {
|
||||
func requireAssignablePermissions(c *gin.Context, canManageUsers, canManageFiles, canUpload, canAdvancedUpload bool) bool {
|
||||
if permissionsWithin(currentUser(c), canManageUsers, canManageFiles, canUpload, canAdvancedUpload) {
|
||||
return true
|
||||
}
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "不能授予自己不具备的权限"})
|
||||
@@ -308,6 +322,51 @@ func parseDownloads(value string) (*int, bool) {
|
||||
return &n, true
|
||||
}
|
||||
|
||||
func (a *App) listManagedFiles(c *gin.Context) {
|
||||
var files []SharedFile
|
||||
if err := a.db.Where("ready = ? AND (expires_at IS NULL OR expires_at > ?)", true, a.now()).Order("created_at DESC").Find(&files).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "读取文件列表失败"})
|
||||
return
|
||||
}
|
||||
ownerIDs := make([]uint, 0, len(files))
|
||||
for _, file := range files {
|
||||
ownerIDs = append(ownerIDs, file.OwnerID)
|
||||
}
|
||||
owners := make(map[uint]string, len(ownerIDs))
|
||||
if len(ownerIDs) > 0 {
|
||||
var users []User
|
||||
if err := a.db.Select("id", "username").Where("id IN ?", ownerIDs).Find(&users).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "读取文件列表失败"})
|
||||
return
|
||||
}
|
||||
for _, user := range users {
|
||||
owners[user.ID] = user.Username
|
||||
}
|
||||
}
|
||||
result := make([]fileResponse, 0, len(files))
|
||||
for _, file := range files {
|
||||
item := presentFile(file)
|
||||
item.OwnerUsername = owners[file.OwnerID]
|
||||
result = append(result, item)
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (a *App) deleteManagedFile(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || id == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件 ID"})
|
||||
return
|
||||
}
|
||||
var file SharedFile
|
||||
if err := a.db.First(&file, uint(id)).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "文件不存在或已失效"})
|
||||
return
|
||||
}
|
||||
a.removeFile(file)
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (a *App) uploadFile(c *gin.Context) {
|
||||
user := currentUser(c)
|
||||
if !user.CanUpload && !user.CanAdvancedUpload {
|
||||
@@ -672,6 +731,7 @@ func (a *App) createUser(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
CanManageUsers bool `json:"canManageUsers"`
|
||||
CanManageFiles bool `json:"canManageFiles"`
|
||||
CanUpload bool `json:"canUpload"`
|
||||
CanAdvancedUpload bool `json:"canAdvancedUpload"`
|
||||
}
|
||||
@@ -679,7 +739,7 @@ func (a *App) createUser(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "用户名需为 3 到 32 位字母、数字、点、横线或下划线"})
|
||||
return
|
||||
}
|
||||
if !requireAssignablePermissions(c, req.CanManageUsers, req.CanUpload, req.CanAdvancedUpload) {
|
||||
if !requireAssignablePermissions(c, req.CanManageUsers, req.CanManageFiles, req.CanUpload, req.CanAdvancedUpload) {
|
||||
return
|
||||
}
|
||||
password, err := randomToken(12)
|
||||
@@ -688,7 +748,7 @@ func (a *App) createUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
user := User{Username: req.Username, PasswordHash: string(hash), CanManageUsers: req.CanManageUsers, CanUpload: req.CanUpload, CanAdvancedUpload: req.CanAdvancedUpload, MustChangePassword: true}
|
||||
user := User{Username: req.Username, PasswordHash: string(hash), CanManageUsers: req.CanManageUsers, CanManageFiles: req.CanManageFiles, CanUpload: req.CanUpload, CanAdvancedUpload: req.CanAdvancedUpload, MustChangePassword: true}
|
||||
if err := a.db.Create(&user).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrDuplicatedKey) || strings.Contains(err.Error(), "UNIQUE") {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
|
||||
@@ -708,6 +768,7 @@ func (a *App) updateUser(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
CanManageUsers bool `json:"canManageUsers"`
|
||||
CanManageFiles bool `json:"canManageFiles"`
|
||||
CanUpload bool `json:"canUpload"`
|
||||
CanAdvancedUpload bool `json:"canAdvancedUpload"`
|
||||
}
|
||||
@@ -720,10 +781,10 @@ func (a *App) updateUser(c *gin.Context) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
if !requireManageableTarget(c, &user) || !requireAssignablePermissions(c, req.CanManageUsers, req.CanUpload, req.CanAdvancedUpload) {
|
||||
if !requireManageableTarget(c, &user) || !requireAssignablePermissions(c, req.CanManageUsers, req.CanManageFiles, req.CanUpload, req.CanAdvancedUpload) {
|
||||
return
|
||||
}
|
||||
err := a.db.Model(&user).Updates(map[string]any{"username": req.Username, "can_manage_users": req.CanManageUsers, "can_upload": req.CanUpload, "can_advanced_upload": req.CanAdvancedUpload}).Error
|
||||
err := a.db.Model(&user).Updates(map[string]any{"username": req.Username, "can_manage_users": req.CanManageUsers, "can_manage_files": req.CanManageFiles, "can_upload": req.CanUpload, "can_advanced_upload": req.CanAdvancedUpload}).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user