diff --git a/go_cloud/internal/http/routes.go b/go_cloud/internal/http/routes.go index 8fa25b3..a564361 100644 --- a/go_cloud/internal/http/routes.go +++ b/go_cloud/internal/http/routes.go @@ -196,6 +196,10 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut r.Post("/user/files/{fileId}/wopi-session", func(w http.ResponseWriter, req *http.Request) { wopiSessionHandler(w, req, db, jwtManager, "https://of.b0esche.cloud") }) + // User file editor + r.Get("/user/files/{fileId}/edit", func(w http.ResponseWriter, req *http.Request) { + userEditorHandler(w, req, db, auditLogger) + }) // Collabora form proxy for user files r.Get("/user/files/{fileId}/collabora-proxy", func(w http.ResponseWriter, req *http.Request) { collaboraProxyHandler(w, req, db, jwtManager, "https://of.b0esche.cloud") @@ -681,6 +685,43 @@ func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi orgID := r.Context().Value(middleware.OrgKey).(uuid.UUID) fileId := chi.URLParam(r, "fileId") + // Log activity + db.LogActivity(r.Context(), userID, orgID, &fileId, "edit_file", map[string]interface{}{}) + + // Generate WOPI access token (1 hour duration) + token, _ := middleware.GetToken(r.Context()) + + // Build WOPISrc URL + wopiSrc := fmt.Sprintf("https://go.b0esche.cloud/wopi/files/%s?access_token=%s", fileId, token) + + // Build Collabora editor URL + collaboraUrl := fmt.Sprintf("https://of.b0esche.cloud/lool/dist/mobile/cool.html?WOPISrc=%s", url.QueryEscape(wopiSrc)) + + // Check if user can edit (for now, all org members can edit) + readOnly := false + + session := struct { + EditUrl string `json:"editUrl"` + Token string `json:"token"` + ReadOnly bool `json:"readOnly"` + ExpiresAt string `json:"expiresAt"` + }{ + EditUrl: collaboraUrl, + Token: token, // JWT token for authenticating file access + ReadOnly: readOnly, + ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339), + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(session) +} + +// userEditorHandler handles GET /user/files/{fileId}/edit +func userEditorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) { + userIDStr, _ := middleware.GetUserID(r.Context()) + userID, _ := uuid.Parse(userIDStr) + fileId := chi.URLParam(r, "fileId") + // Get file metadata to determine path and type fileUUID, err := uuid.Parse(fileId) if err != nil { @@ -695,19 +736,27 @@ func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi return } + // Verify user owns this file + if file.UserID == nil || *file.UserID != userID { + errors.WriteError(w, errors.CodePermissionDenied, "Access denied", http.StatusForbidden) + return + } + // Log activity - db.LogActivity(r.Context(), userID, orgID, &fileId, "edit_file", map[string]interface{}{}) + db.LogActivity(r.Context(), userID, uuid.Nil, &fileId, "edit_file", map[string]interface{}{}) - // Build Collabora editor URL - Collabora needs the file download URL as the WOPISrc parameter - editUrl := fmt.Sprintf("https://go.b0esche.cloud/orgs/%s/files/download?path=%s", orgID.String(), url.QueryEscape(file.Path)) - collaboraUrl := fmt.Sprintf("https://of.b0esche.cloud/lool/dist/mobile/cool.html?WOPISrc=%s", url.QueryEscape(editUrl)) - - // Check if user can edit (for now, all org members can edit) - readOnly := false - - // Get JWT token from context + // Generate WOPI access token (1 hour duration) token, _ := middleware.GetToken(r.Context()) + // Build WOPISrc URL + wopiSrc := fmt.Sprintf("https://go.b0esche.cloud/wopi/files/%s?access_token=%s", fileId, token) + + // Build Collabora editor URL + collaboraUrl := fmt.Sprintf("https://of.b0esche.cloud/lool/dist/mobile/cool.html?WOPISrc=%s", url.QueryEscape(wopiSrc)) + + // Check if user can edit (for now, all users can edit their own files) + readOnly := false + session := struct { EditUrl string `json:"editUrl"` Token string `json:"token"` diff --git a/go_cloud/internal/http/wopi_handlers.go b/go_cloud/internal/http/wopi_handlers.go index 045267d..429389b 100644 --- a/go_cloud/internal/http/wopi_handlers.go +++ b/go_cloud/internal/http/wopi_handlers.go @@ -245,6 +245,8 @@ func wopiCheckFileInfoHandler(w http.ResponseWriter, r *http.Request, db *databa return } + fmt.Printf("[WOPI-CheckFileInfo] START: file=%s user=%s size=%d path=%s\n", fileID, userID.String(), file.Size, file.Path) + // Verify user has access to this file canAccess := false var ownerID string @@ -313,6 +315,8 @@ func wopiGetFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, return } + fmt.Printf("[WOPI-GetFile] START: file=%s\n", fileID) + // Get access token from query parameter accessToken := r.URL.Query().Get("access_token") if accessToken == "" { @@ -393,6 +397,8 @@ func wopiGetFileHandler(w http.ResponseWriter, r *http.Request, db *database.DB, } defer resp.Body.Close() + fmt.Printf("[WOPI-STORAGE] Download response status: %d\n", resp.StatusCode) + // Set response headers contentType := getMimeType(file.Name) w.Header().Set("Content-Type", contentType)