|
|
|
@@ -361,9 +361,29 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi
|
|
|
|
orgID := r.Context().Value("org").(uuid.UUID)
|
|
|
|
orgID := r.Context().Value("org").(uuid.UUID)
|
|
|
|
fileId := chi.URLParam(r, "fileId")
|
|
|
|
fileId := chi.URLParam(r, "fileId")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get file metadata to determine path and type
|
|
|
|
|
|
|
|
fileUUID, err := uuid.Parse(fileId)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid file ID", http.StatusBadRequest)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file, err := db.GetFileByID(r.Context(), fileUUID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
errors.LogError(r, err, "Failed to get file metadata")
|
|
|
|
|
|
|
|
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Log activity
|
|
|
|
// Log activity
|
|
|
|
db.LogActivity(r.Context(), userID, orgID, &fileId, "view_file", map[string]interface{}{})
|
|
|
|
db.LogActivity(r.Context(), userID, orgID, &fileId, "view_file", map[string]interface{}{})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Build download URL - using full path for frontend to fetch with auth headers
|
|
|
|
|
|
|
|
viewUrl := fmt.Sprintf("https://b0esche.cloud/api/orgs/%s/files/download?path=%s", orgID.String(), file.Path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Determine if it's a PDF based on file extension
|
|
|
|
|
|
|
|
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
|
|
|
|
|
|
|
|
|
|
|
session := struct {
|
|
|
|
session := struct {
|
|
|
|
ViewUrl string `json:"viewUrl"`
|
|
|
|
ViewUrl string `json:"viewUrl"`
|
|
|
|
Capabilities struct {
|
|
|
|
Capabilities struct {
|
|
|
|
@@ -373,13 +393,13 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, audi
|
|
|
|
} `json:"capabilities"`
|
|
|
|
} `json:"capabilities"`
|
|
|
|
ExpiresAt string `json:"expiresAt"`
|
|
|
|
ExpiresAt string `json:"expiresAt"`
|
|
|
|
}{
|
|
|
|
}{
|
|
|
|
ViewUrl: "https://view.example.com/" + fileId,
|
|
|
|
ViewUrl: viewUrl,
|
|
|
|
Capabilities: struct {
|
|
|
|
Capabilities: struct {
|
|
|
|
CanEdit bool `json:"canEdit"`
|
|
|
|
CanEdit bool `json:"canEdit"`
|
|
|
|
CanAnnotate bool `json:"canAnnotate"`
|
|
|
|
CanAnnotate bool `json:"canAnnotate"`
|
|
|
|
IsPdf bool `json:"isPdf"`
|
|
|
|
IsPdf bool `json:"isPdf"`
|
|
|
|
}{CanEdit: true, CanAnnotate: true, IsPdf: true},
|
|
|
|
}{CanEdit: false, CanAnnotate: isPdf, IsPdf: isPdf},
|
|
|
|
ExpiresAt: "2023-01-01T01:00:00Z",
|
|
|
|
ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
@@ -392,7 +412,29 @@ func userViewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB,
|
|
|
|
userID, _ := uuid.Parse(userIDStr)
|
|
|
|
userID, _ := uuid.Parse(userIDStr)
|
|
|
|
fileId := chi.URLParam(r, "fileId")
|
|
|
|
fileId := chi.URLParam(r, "fileId")
|
|
|
|
|
|
|
|
|
|
|
|
// For now, return a synthetic viewer session similar to org viewer
|
|
|
|
// Get file metadata to determine path and type
|
|
|
|
|
|
|
|
fileUUID, err := uuid.Parse(fileId)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
errors.WriteError(w, errors.CodeInvalidArgument, "Invalid file ID", http.StatusBadRequest)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
file, err := db.GetFileByID(r.Context(), fileUUID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
errors.LogError(r, err, "Failed to get file metadata")
|
|
|
|
|
|
|
|
errors.WriteError(w, errors.CodeNotFound, "File not found", http.StatusNotFound)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Optionally log activity without org id
|
|
|
|
|
|
|
|
db.LogActivity(r.Context(), userID, uuid.Nil, &fileId, "view_user_file", map[string]interface{}{})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Build download URL for user files - using full path for frontend to fetch with auth headers
|
|
|
|
|
|
|
|
viewUrl := fmt.Sprintf("https://b0esche.cloud/api/user/files/download?path=%s", file.Path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Determine if it's a PDF based on file extension
|
|
|
|
|
|
|
|
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
|
|
|
|
|
|
|
|
|
|
|
session := struct {
|
|
|
|
session := struct {
|
|
|
|
ViewUrl string `json:"viewUrl"`
|
|
|
|
ViewUrl string `json:"viewUrl"`
|
|
|
|
Capabilities struct {
|
|
|
|
Capabilities struct {
|
|
|
|
@@ -402,22 +444,19 @@ func userViewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB,
|
|
|
|
} `json:"capabilities"`
|
|
|
|
} `json:"capabilities"`
|
|
|
|
ExpiresAt string `json:"expiresAt"`
|
|
|
|
ExpiresAt string `json:"expiresAt"`
|
|
|
|
}{
|
|
|
|
}{
|
|
|
|
ViewUrl: "https://view.example.com/" + fileId,
|
|
|
|
ViewUrl: viewUrl,
|
|
|
|
Capabilities: struct {
|
|
|
|
Capabilities: struct {
|
|
|
|
CanEdit bool `json:"canEdit"`
|
|
|
|
CanEdit bool `json:"canEdit"`
|
|
|
|
CanAnnotate bool `json:"canAnnotate"`
|
|
|
|
CanAnnotate bool `json:"canAnnotate"`
|
|
|
|
IsPdf bool `json:"isPdf"`
|
|
|
|
IsPdf bool `json:"isPdf"`
|
|
|
|
}{
|
|
|
|
}{
|
|
|
|
CanEdit: false,
|
|
|
|
CanEdit: false,
|
|
|
|
CanAnnotate: true,
|
|
|
|
CanAnnotate: isPdf,
|
|
|
|
IsPdf: strings.HasSuffix(strings.ToLower(fileId), ".pdf"),
|
|
|
|
IsPdf: isPdf,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339),
|
|
|
|
ExpiresAt: time.Now().Add(15 * time.Minute).UTC().Format(time.RFC3339),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Optionally log activity without org id
|
|
|
|
|
|
|
|
db.LogActivity(r.Context(), userID, uuid.Nil, &fileId, "view_user_file", map[string]interface{}{})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(session)
|
|
|
|
json.NewEncoder(w).Encode(session)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1319,10 +1358,19 @@ func downloadOrgFileHandler(w http.ResponseWriter, r *http.Request, db *database
|
|
|
|
if err == nil {
|
|
|
|
if err == nil {
|
|
|
|
defer reader.Close()
|
|
|
|
defer reader.Close()
|
|
|
|
|
|
|
|
|
|
|
|
// Set appropriate headers
|
|
|
|
// Set appropriate headers for inline viewing
|
|
|
|
fileName := path.Base(filePath)
|
|
|
|
fileName := path.Base(filePath)
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
|
|
|
// Determine content type based on file extension
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
contentType := "application/octet-stream"
|
|
|
|
|
|
|
|
if strings.HasSuffix(strings.ToLower(fileName), ".pdf") {
|
|
|
|
|
|
|
|
contentType = "application/pdf"
|
|
|
|
|
|
|
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".png") {
|
|
|
|
|
|
|
|
contentType = "image/png"
|
|
|
|
|
|
|
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".jpg") || strings.HasSuffix(strings.ToLower(fileName), ".jpeg") {
|
|
|
|
|
|
|
|
contentType = "image/jpeg"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName))
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", contentType)
|
|
|
|
if size > 0 {
|
|
|
|
if size > 0 {
|
|
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
|
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1364,10 +1412,19 @@ func downloadUserFileHandler(w http.ResponseWriter, r *http.Request, db *databas
|
|
|
|
if err == nil {
|
|
|
|
if err == nil {
|
|
|
|
defer reader.Close()
|
|
|
|
defer reader.Close()
|
|
|
|
|
|
|
|
|
|
|
|
// Set appropriate headers
|
|
|
|
// Set appropriate headers for inline viewing
|
|
|
|
fileName := path.Base(filePath)
|
|
|
|
fileName := path.Base(filePath)
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
|
|
|
|
// Determine content type based on file extension
|
|
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
contentType := "application/octet-stream"
|
|
|
|
|
|
|
|
if strings.HasSuffix(strings.ToLower(fileName), ".pdf") {
|
|
|
|
|
|
|
|
contentType = "application/pdf"
|
|
|
|
|
|
|
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".png") {
|
|
|
|
|
|
|
|
contentType = "image/png"
|
|
|
|
|
|
|
|
} else if strings.HasSuffix(strings.ToLower(fileName), ".jpg") || strings.HasSuffix(strings.ToLower(fileName), ".jpeg") {
|
|
|
|
|
|
|
|
contentType = "image/jpeg"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName))
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", contentType)
|
|
|
|
if size > 0 {
|
|
|
|
if size > 0 {
|
|
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
|
|
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|