Compare commits

..

2 commits

Author SHA1 Message Date
grngxd
9ef7760967 split single file id "<uid_name>" into 2 properties 2025-06-10 00:10:03 +01:00
grngxd
2b64d64f80 sanitise file name 2025-06-09 23:11:32 +01:00
2 changed files with 106 additions and 40 deletions

View file

@ -47,7 +47,7 @@ func RegisterFileRoutes(cfg *types.StereoConfig, api *gin.RouterGroup) {
} }
fileMeta := types.File{ fileMeta := types.File{
ID: uid + "_" + file.Filename, Name: file.Filename,
Owner: uid, Owner: uid,
CreatedAt: time.Now(), CreatedAt: time.Now(),
Size: file.Size, Size: file.Size,
@ -58,68 +58,134 @@ func RegisterFileRoutes(cfg *types.StereoConfig, api *gin.RouterGroup) {
return return
} }
c.JSON(200, gin.H{"message": "file uploaded successfully", "file_id": fileMeta.ID}) c.JSON(200, gin.H{"message": "file uploaded successfully", "name": fileMeta.Name})
}) })
api.DELETE("/delete", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) { // api.DELETE("/delete", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) {
// claims := c.MustGet("claims").(jwt.MapClaims)
// user := claims["user"].(auth.User)
// uid := user.ID
// if uid == "" {
// c.JSON(401, gin.H{"error": "unauthorized"})
// return
// }
// var response struct {
// Name string `json:"name" binding:"required"`
// ID string `json:"id" binding:"required"`
// }
// if err := c.ShouldBindJSON(&response); err != nil {
// c.JSON(400, gin.H{"error": "name is required"})
// return
// }
// name := response.Name
// if name == "" {
// c.JSON(400, gin.H{"error": "name cannot be empty"})
// return
// }
// resID := response.ID
// if resID == "" {
// c.JSON(400, gin.H{"error": "id cannot be empty"})
// return
// }
// if resID != uid {
// c.JSON(403, gin.H{"error": "you can only delete your own files"})
// return
// }
// filePath := filepath.Join(cfg.ImagePath, uid, name)
// if err := os.Remove(filePath); err != nil {
// c.JSON(500, gin.H{"error": "failed to delete file"})
// return
// }
// if err := cfg.Database.Where("name = ?", name).Where("owner = ?", uid).Delete(&types.File{}).Error; err != nil {
// c.JSON(500, gin.H{"error": "failed to delete file metadata"})
// return
// }
// c.JSON(200, gin.H{"message": "file deleted successfully"})
// })
api.DELETE("/:uid/:name", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) {
claims := c.MustGet("claims").(jwt.MapClaims) claims := c.MustGet("claims").(jwt.MapClaims)
user := claims["user"].(auth.User) user := claims["user"].(auth.User)
uid := user.ID uid := c.Param("uid")
uid = strings.TrimSpace(uid)
if uid == "" { if uid == "" {
c.JSON(401, gin.H{"error": "unauthorized"}) c.JSON(400, gin.H{"error": "uid is required"})
return return
} }
var response struct { if uid != user.ID {
FileID string `json:"file_id" binding:"required"`
}
if err := c.ShouldBindJSON(&response); err != nil {
c.JSON(400, gin.H{"error": "file_id is required"})
return
}
resfID := response.FileID
if resfID == "" {
c.JSON(400, gin.H{"error": "file_id cannot be empty"})
return
}
parts := strings.SplitN(resfID, "_", 2)
if len(parts) != 2 {
c.JSON(400, gin.H{"error": "invalid file_id format"})
return
}
fileID, filename := parts[0], parts[1]
if fileID != uid {
c.JSON(403, gin.H{"error": "you can only delete your own files"}) c.JSON(403, gin.H{"error": "you can only delete your own files"})
return return
} }
filePath := filepath.Join(cfg.ImagePath, uid, filename) filename := c.Param("name")
if err := os.Remove(filePath); err != nil { filename = strings.TrimSpace(filename)
if filename == "" {
c.JSON(400, gin.H{"error": "filename is required"})
return
}
path := filepath.Join(cfg.ImagePath, uid, filename)
if _, err := os.Stat(path); os.IsNotExist(err) {
c.JSON(404, gin.H{"error": "file not found"})
return
}
if err := os.Remove(path); err != nil {
c.JSON(500, gin.H{"error": "failed to delete file"}) c.JSON(500, gin.H{"error": "failed to delete file"})
return return
} }
if err := cfg.Database.Where("owner = ? AND name = ?", uid, filename).Delete(&types.File{}).Error; err != nil {
if err := cfg.Database.Where("id = ?", resfID).Delete(&types.File{}).Error; err != nil {
c.JSON(500, gin.H{"error": "failed to delete file metadata"}) c.JSON(500, gin.H{"error": "failed to delete file metadata"})
return return
} }
c.JSON(200, gin.H{"message": "file deleted successfully"}) c.JSON(200, gin.H{"message": "file deleted successfully"})
}) })
api.GET("/:name", func(c *gin.Context) { api.GET("/:uid/:name", func(c *gin.Context) {
name := c.Param("name") uid := c.Param("uid")
parts := strings.SplitN(name, "_", 2) uid = strings.TrimSpace(uid)
if len(parts) != 2 { if uid == "" {
c.JSON(400, gin.H{"error": "invalid file name"}) c.JSON(400, gin.H{"error": "uid is required"})
return return
} }
uid, filename := parts[0], parts[1]
filename := c.Param("name")
filename = strings.TrimSpace(filename)
safe := ""
for _, r := range filename {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9') || r == '_' || r == '.' || r == '-' {
safe += string(r)
} else {
safe += "_"
}
}
filename = safe
// parts := strings.SplitN(name, "_", 2)
// if len(parts) != 2 {
// c.JSON(400, gin.H{"error": "invalid file name"})
// return
// }
// uid, filename := parts[0], parts[1]
// if uid == "" || filename == "" {
// c.JSON(400, gin.H{"error": "invalid file name"})
// return
// }
path := filepath.Join(cfg.ImagePath, uid, filename) path := filepath.Join(cfg.ImagePath, uid, filename)
if _, err := os.Stat(path); err != nil { if _, err := os.Stat(path); err != nil {
c.JSON(404, gin.H{"error": "file not found"}) c.JSON(404, gin.H{"error": "file not found"})

View file

@ -25,7 +25,7 @@ type StereoConfig struct {
} }
type File struct { type File struct {
ID string `gorm:"primaryKey"` Name string `gorm:"primaryKey"`
Owner string `gorm:"not null;index"` Owner string `gorm:"not null;index"`
Size int64 `gorm:"not null;type:bigint"` Size int64 `gorm:"not null;type:bigint"`
CreatedAt time.Time `gorm:"autoCreateTime"` CreatedAt time.Time `gorm:"autoCreateTime"`