work
This commit is contained in:
@@ -78,16 +78,20 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.Get("/user/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
userFilesHandler(w, req, db)
|
||||
})
|
||||
// Download user file
|
||||
r.Get("/user/files/download", func(w http.ResponseWriter, req *http.Request) {
|
||||
downloadUserFileHandler(w, req, db, storageClient)
|
||||
})
|
||||
// Create / delete in user workspace
|
||||
r.Post("/user/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
createUserFileHandler(w, req, db, auditLogger, storageClient)
|
||||
})
|
||||
r.Delete("/user/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
deleteUserFileHandler(w, req, db, auditLogger)
|
||||
deleteUserFileHandler(w, req, db, auditLogger, storageClient)
|
||||
})
|
||||
// POST wrapper for delete
|
||||
r.Post("/user/files/delete", func(w http.ResponseWriter, req *http.Request) {
|
||||
deleteUserFilePostHandler(w, req, db, auditLogger)
|
||||
deleteUserFilePostHandler(w, req, db, auditLogger, storageClient)
|
||||
})
|
||||
|
||||
// Org routes
|
||||
@@ -106,6 +110,10 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.With(middleware.Permission(db, auditLogger, permission.FileRead)).Get("/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
listFilesHandler(w, req, db)
|
||||
})
|
||||
// Download org file
|
||||
r.With(middleware.Permission(db, auditLogger, permission.FileRead)).Get("/files/download", func(w http.ResponseWriter, req *http.Request) {
|
||||
downloadOrgFileHandler(w, req, db, storageClient)
|
||||
})
|
||||
|
||||
// Create file/folder in org workspace
|
||||
r.With(middleware.Permission(db, auditLogger, permission.FileWrite)).Post("/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -113,12 +121,12 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
})
|
||||
// Also accept POST delete for clients that cannot send DELETE with body
|
||||
r.With(middleware.Permission(db, auditLogger, permission.FileWrite)).Post("/files/delete", func(w http.ResponseWriter, req *http.Request) {
|
||||
deleteOrgFilePostHandler(w, req, db, auditLogger)
|
||||
deleteOrgFilePostHandler(w, req, db, auditLogger, storageClient)
|
||||
})
|
||||
|
||||
// Delete file/folder in org workspace (body: {"path":"/path"})
|
||||
r.With(middleware.Permission(db, auditLogger, permission.FileWrite)).Delete("/files", func(w http.ResponseWriter, req *http.Request) {
|
||||
deleteOrgFileHandler(w, req, db, auditLogger)
|
||||
deleteOrgFileHandler(w, req, db, auditLogger, storageClient)
|
||||
})
|
||||
r.Route("/files/{fileId}", func(r chi.Router) {
|
||||
r.With(middleware.Permission(db, auditLogger, permission.DocumentView)).Get("/view", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -991,7 +999,7 @@ func createOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.D
|
||||
}
|
||||
|
||||
// deleteOrgFileHandler deletes a file/folder in org workspace by path
|
||||
func deleteOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
|
||||
func deleteOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger, storageClient *storage.WebDAVClient) {
|
||||
orgID := r.Context().Value("org").(uuid.UUID)
|
||||
userIDStr, _ := r.Context().Value("user").(string)
|
||||
userID, _ := uuid.Parse(userIDStr)
|
||||
@@ -1004,6 +1012,16 @@ func deleteOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.D
|
||||
return
|
||||
}
|
||||
|
||||
// Delete from Nextcloud if configured
|
||||
if storageClient != nil {
|
||||
rel := strings.TrimPrefix(req.Path, "/")
|
||||
remotePath := path.Join("/orgs", orgID.String(), rel)
|
||||
if err := storageClient.Delete(r.Context(), remotePath); err != nil {
|
||||
errors.LogError(r, err, "Failed to delete from Nextcloud (continuing anyway)")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
if err := db.DeleteFileByPath(r.Context(), &orgID, nil, req.Path); err != nil {
|
||||
errors.LogError(r, err, "Failed to delete org file")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
@@ -1022,8 +1040,8 @@ func deleteOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.D
|
||||
}
|
||||
|
||||
// Also accept POST /orgs/{orgId}/files/delete for clients that cannot send DELETE with body
|
||||
func deleteOrgFilePostHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
|
||||
deleteOrgFileHandler(w, r, db, auditLogger)
|
||||
func deleteOrgFilePostHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger, storageClient *storage.WebDAVClient) {
|
||||
deleteOrgFileHandler(w, r, db, auditLogger, storageClient)
|
||||
}
|
||||
|
||||
// createUserFileHandler creates a file or folder record for the authenticated user's personal workspace.
|
||||
@@ -1154,12 +1172,12 @@ func createUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.
|
||||
}
|
||||
|
||||
// Also accept POST /user/files/delete
|
||||
func deleteUserFilePostHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
|
||||
deleteUserFileHandler(w, r, db, auditLogger)
|
||||
func deleteUserFilePostHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger, storageClient *storage.WebDAVClient) {
|
||||
deleteUserFileHandler(w, r, db, auditLogger, storageClient)
|
||||
}
|
||||
|
||||
// deleteUserFileHandler deletes a file/folder in user's personal workspace by path
|
||||
func deleteUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
|
||||
func deleteUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger, storageClient *storage.WebDAVClient) {
|
||||
userIDStr, ok := r.Context().Value("user").(string)
|
||||
if !ok || userIDStr == "" {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Unauthorized", http.StatusUnauthorized)
|
||||
@@ -1175,6 +1193,16 @@ func deleteUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.
|
||||
return
|
||||
}
|
||||
|
||||
// Delete from Nextcloud if configured
|
||||
if storageClient != nil {
|
||||
rel := strings.TrimPrefix(req.Path, "/")
|
||||
remotePath := path.Join("/user", userID.String(), rel)
|
||||
if err := storageClient.Delete(r.Context(), remotePath); err != nil {
|
||||
errors.LogError(r, err, "Failed to delete from Nextcloud (continuing anyway)")
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
if err := db.DeleteFileByPath(r.Context(), nil, &userID, req.Path); err != nil {
|
||||
errors.LogError(r, err, "Failed to delete user file")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
@@ -1190,3 +1218,86 @@ func deleteUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`{"status":"ok"}`))
|
||||
}
|
||||
|
||||
// downloadOrgFileHandler downloads a file from org workspace
|
||||
func downloadOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, storageClient *storage.WebDAVClient) {
|
||||
orgID := r.Context().Value("org").(uuid.UUID)
|
||||
|
||||
filePath := r.URL.Query().Get("path")
|
||||
if filePath == "" {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Missing path parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Try to download from Nextcloud first
|
||||
if storageClient != nil {
|
||||
rel := strings.TrimPrefix(filePath, "/")
|
||||
remotePath := path.Join("/orgs", orgID.String(), rel)
|
||||
|
||||
reader, size, err := storageClient.Download(r.Context(), remotePath)
|
||||
if err == nil {
|
||||
defer reader.Close()
|
||||
|
||||
// Set appropriate headers
|
||||
fileName := path.Base(filePath)
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
if size > 0 {
|
||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
||||
}
|
||||
|
||||
// Stream the file
|
||||
io.Copy(w, reader)
|
||||
return
|
||||
}
|
||||
|
||||
errors.LogError(r, err, "Failed to download from Nextcloud, trying local storage")
|
||||
}
|
||||
|
||||
// Fallback to local storage (if implemented)
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
}
|
||||
|
||||
// downloadUserFileHandler downloads a file from user's personal workspace
|
||||
func downloadUserFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, storageClient *storage.WebDAVClient) {
|
||||
userIDStr, ok := r.Context().Value("user").(string)
|
||||
if !ok || userIDStr == "" {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
userID, _ := uuid.Parse(userIDStr)
|
||||
|
||||
filePath := r.URL.Query().Get("path")
|
||||
if filePath == "" {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Missing path parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Try to download from Nextcloud first
|
||||
if storageClient != nil {
|
||||
rel := strings.TrimPrefix(filePath, "/")
|
||||
remotePath := path.Join("/user", userID.String(), rel)
|
||||
|
||||
reader, size, err := storageClient.Download(r.Context(), remotePath)
|
||||
if err == nil {
|
||||
defer reader.Close()
|
||||
|
||||
// Set appropriate headers
|
||||
fileName := path.Base(filePath)
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
if size > 0 {
|
||||
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
||||
}
|
||||
|
||||
// Stream the file
|
||||
io.Copy(w, reader)
|
||||
return
|
||||
}
|
||||
|
||||
errors.LogError(r, err, "Failed to download from Nextcloud, trying local storage")
|
||||
}
|
||||
|
||||
// Fallback to local storage (if implemented)
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user