Implement user-specific share link management for files
This commit is contained in:
BIN
go_cloud/api
BIN
go_cloud/api
Binary file not shown.
@@ -210,6 +210,16 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.Get("/user/files/{fileId}/collabora-proxy", func(w http.ResponseWriter, req *http.Request) {
|
||||
collaboraProxyHandler(w, req, db, jwtManager, "https://of.b0esche.cloud")
|
||||
})
|
||||
// Share link management for user files
|
||||
r.Get("/user/files/{fileId}/share", func(w http.ResponseWriter, req *http.Request) {
|
||||
getUserFileShareLinkHandler(w, req, db)
|
||||
})
|
||||
r.Post("/user/files/{fileId}/share", func(w http.ResponseWriter, req *http.Request) {
|
||||
createUserFileShareLinkHandler(w, req, db)
|
||||
})
|
||||
r.Delete("/user/files/{fileId}/share", func(w http.ResponseWriter, req *http.Request) {
|
||||
revokeUserFileShareLinkHandler(w, req, db)
|
||||
})
|
||||
|
||||
// Org routes
|
||||
r.Get("/orgs", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -3028,3 +3038,156 @@ func publicFileDownloadHandler(w http.ResponseWriter, r *http.Request, db *datab
|
||||
// Copy body
|
||||
io.Copy(w, resp.Body)
|
||||
}
|
||||
|
||||
func getUserFileShareLinkHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {
|
||||
userIDStr, _ := middleware.GetUserID(r.Context())
|
||||
userID, _ := uuid.Parse(userIDStr)
|
||||
fileId := chi.URLParam(r, "fileId")
|
||||
|
||||
fileUUID, err := uuid.Parse(fileId)
|
||||
if err != nil {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid file ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if file exists and belongs to user
|
||||
file, err := db.GetFileByID(r.Context(), fileUUID)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to get file")
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if file.UserID == nil || *file.UserID != userID {
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
link, err := db.GetFileShareLinkByFileID(r.Context(), fileUUID)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// No share link exists
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"exists": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
errors.LogError(r, err, "Failed to get share link")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Build full URL
|
||||
scheme := "https"
|
||||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||
scheme = proto
|
||||
} else if r.TLS == nil {
|
||||
scheme = "http"
|
||||
}
|
||||
host := r.Host
|
||||
if host == "" {
|
||||
host = "go.b0esche.cloud"
|
||||
}
|
||||
fullURL := fmt.Sprintf("%s://%s/public/share/%s", scheme, host, link.Token)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"exists": true,
|
||||
"url": fullURL,
|
||||
"token": link.Token,
|
||||
})
|
||||
}
|
||||
|
||||
func createUserFileShareLinkHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {
|
||||
userIDStr, _ := middleware.GetUserID(r.Context())
|
||||
userID, _ := uuid.Parse(userIDStr)
|
||||
fileId := chi.URLParam(r, "fileId")
|
||||
|
||||
fileUUID, err := uuid.Parse(fileId)
|
||||
if err != nil {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid file ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if file exists and belongs to user
|
||||
file, err := db.GetFileByID(r.Context(), fileUUID)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to get file")
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if file.UserID == nil || *file.UserID != userID {
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Revoke existing link if any
|
||||
db.RevokeFileShareLink(r.Context(), fileUUID) // Ignore error
|
||||
|
||||
// Generate token
|
||||
token, err := storage.GenerateSecurePassword(32)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to generate token")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
link, err := db.CreateFileShareLink(r.Context(), token, fileUUID, userID, userID)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to create share link")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Build full URL
|
||||
scheme := "https"
|
||||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||
scheme = proto
|
||||
} else if r.TLS == nil {
|
||||
scheme = "http"
|
||||
}
|
||||
host := r.Host
|
||||
if host == "" {
|
||||
host = "go.b0esche.cloud"
|
||||
}
|
||||
fullURL := fmt.Sprintf("%s://%s/public/share/%s", scheme, host, link.Token)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"url": fullURL,
|
||||
"token": link.Token,
|
||||
})
|
||||
}
|
||||
|
||||
func revokeUserFileShareLinkHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {
|
||||
userIDStr, _ := middleware.GetUserID(r.Context())
|
||||
userID, _ := uuid.Parse(userIDStr)
|
||||
fileId := chi.URLParam(r, "fileId")
|
||||
|
||||
fileUUID, err := uuid.Parse(fileId)
|
||||
if err != nil {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid file ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if file exists and belongs to user
|
||||
file, err := db.GetFileByID(r.Context(), fileUUID)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to get file")
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if file.UserID == nil || *file.UserID != userID {
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
err = db.RevokeFileShareLink(r.Context(), fileUUID)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to revoke share link")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user