package drive import ( "io" "net/http" "strconv" "strings" "github.com/go-chi/chi/v5" "github.com/ultisuite/ulti-backend/internal/api/apiresponse" "github.com/ultisuite/ulti-backend/internal/api/apivalidate" ) func (h *Handler) PublicRoutes() chi.Router { r := chi.NewRouter() r.Get("/shares/{token}", h.GetPublicShare) r.Get("/shares/{token}/preview", h.PreviewPublicShare) r.Get("/shares/{token}/download/*", h.DownloadPublicShare) r.Put("/shares/{token}/files/*", h.UploadPublicShare) r.Post("/shares/{token}/folders/*", h.CreatePublicShareFolder) r.Delete("/shares/{token}/files/*", h.DeletePublicShareItem) r.Post("/shares/{token}/rename", h.RenamePublicShareItem) if h.publicOffice != nil { h.publicOffice.RegisterPublicShareRoutes(r) } if h.publicRichText != nil { h.publicRichText.RegisterPublicShareRoutes(r) } if h.publicUltidraw != nil { h.publicUltidraw.RegisterPublicShareRoutes(r) } return r } func publicSharePassword(r *http.Request) string { return strings.TrimSpace(r.URL.Query().Get("password")) } func (h *Handler) GetPublicShare(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) if token == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "token", Message: "required"}, )) return } path := strings.TrimSpace(r.URL.Query().Get("path")) if path == "" { path = "/" } view, err := h.svc.GetPublicShare(r.Context(), token, path, publicSharePassword(r)) if err != nil { writeDriveError(w, r, err) return } apiresponse.WriteJSON(w, http.StatusOK, view) } func (h *Handler) DownloadPublicShare(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) if token == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "token", Message: "required"}, )) return } filePath := strings.TrimSpace(chi.URLParam(r, "*")) if filePath == "" { filePath = "/" } body, contentType, err := h.svc.DownloadPublicShare(r.Context(), token, filePath, publicSharePassword(r)) if err != nil { writeDriveError(w, r, err) return } defer body.Close() w.Header().Set("Content-Type", contentType) _, _ = io.Copy(w, body) } func (h *Handler) PreviewPublicShare(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) if token == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "token", Message: "required"}, )) return } filePath := strings.TrimSpace(r.URL.Query().Get("path")) if filePath == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "path", Message: "required"}, )) return } width, _ := strconv.Atoi(r.URL.Query().Get("w")) height, _ := strconv.Atoi(r.URL.Query().Get("h")) body, contentType, err := h.svc.PreviewPublicShare(r.Context(), token, filePath, publicSharePassword(r), width, height) if err != nil { writeDriveError(w, r, err) return } defer body.Close() if contentType != "" { w.Header().Set("Content-Type", contentType) } _, _ = io.Copy(w, body) } func (h *Handler) UploadPublicShare(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) filePath := strings.TrimSpace(chi.URLParam(r, "*")) if token == "" || filePath == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "path", Message: "required"}, )) return } if !strings.HasPrefix(filePath, "/") { filePath = "/" + filePath } if err := h.svc.UploadPublicShare(r.Context(), token, filePath, publicSharePassword(r), r.Body, r.Header.Get("Content-Type")); err != nil { writeDriveError(w, r, err) return } w.WriteHeader(http.StatusCreated) } func (h *Handler) CreatePublicShareFolder(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) folderPath := strings.TrimSpace(chi.URLParam(r, "*")) if token == "" || folderPath == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "path", Message: "required"}, )) return } if !strings.HasPrefix(folderPath, "/") { folderPath = "/" + folderPath } if err := h.svc.CreatePublicShareFolder(r.Context(), token, folderPath, publicSharePassword(r)); err != nil { writeDriveError(w, r, err) return } w.WriteHeader(http.StatusCreated) } func (h *Handler) DeletePublicShareItem(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) filePath := strings.TrimSpace(chi.URLParam(r, "*")) if token == "" || filePath == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "path", Message: "required"}, )) return } if !strings.HasPrefix(filePath, "/") { filePath = "/" + filePath } if err := h.svc.DeletePublicShareItem(r.Context(), token, filePath, publicSharePassword(r)); err != nil { writeDriveError(w, r, err) return } w.WriteHeader(http.StatusNoContent) } type publicRenameRequest struct { Path string `json:"path"` NewName string `json:"new_name"` } func (h *Handler) RenamePublicShareItem(w http.ResponseWriter, r *http.Request) { token := strings.TrimSpace(chi.URLParam(r, "token")) if token == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "token", Message: "required"}, )) return } var req publicRenameRequest if err := apivalidate.DecodeJSON(w, r, 32<<10, &req); err != nil { return } if strings.TrimSpace(req.Path) == "" || strings.TrimSpace(req.NewName) == "" { apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError( apivalidate.FieldDetail{Field: "path", Message: "required"}, apivalidate.FieldDetail{Field: "new_name", Message: "required"}, )) return } if err := h.svc.RenamePublicShareItem(r.Context(), token, req.Path, req.NewName, publicSharePassword(r)); err != nil { writeDriveError(w, r, err) return } w.WriteHeader(http.StatusNoContent) }