Reorder PublicFileViewer header: download button left, close button right, add 4px top padding
This commit is contained in:
@@ -359,6 +359,9 @@ func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, aut
|
||||
r.Get("/share/{token}/download", func(w http.ResponseWriter, req *http.Request) {
|
||||
publicFileDownloadHandler(w, req, db, cfg, jwtManager)
|
||||
})
|
||||
r.Get("/share/{token}/view", func(w http.ResponseWriter, req *http.Request) {
|
||||
publicFileViewHandler(w, req, db, cfg, jwtManager)
|
||||
})
|
||||
})
|
||||
|
||||
return r
|
||||
@@ -2915,7 +2918,7 @@ func publicFileShareHandler(w http.ResponseWriter, r *http.Request, db *database
|
||||
return
|
||||
}
|
||||
|
||||
// Build download URL
|
||||
// Build URLs
|
||||
scheme := "https"
|
||||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||||
scheme = proto
|
||||
@@ -2924,6 +2927,7 @@ func publicFileShareHandler(w http.ResponseWriter, r *http.Request, db *database
|
||||
}
|
||||
host := "www.b0esche.cloud"
|
||||
downloadPath := fmt.Sprintf("%s://%s/public/share/%s/download?token=%s", scheme, host, token, url.QueryEscape(viewerToken))
|
||||
viewPath := fmt.Sprintf("%s://%s/public/share/%s/view?token=%s", scheme, host, token, url.QueryEscape(viewerToken))
|
||||
|
||||
// Determine file type
|
||||
isPdf := strings.HasSuffix(strings.ToLower(file.Name), ".pdf")
|
||||
@@ -2933,6 +2937,7 @@ func publicFileShareHandler(w http.ResponseWriter, r *http.Request, db *database
|
||||
FileName string `json:"fileName"`
|
||||
FileSize int64 `json:"fileSize"`
|
||||
DownloadUrl string `json:"downloadUrl"`
|
||||
ViewUrl string `json:"viewUrl,omitempty"`
|
||||
Token string `json:"token"`
|
||||
Capabilities struct {
|
||||
CanEdit bool `json:"canEdit"`
|
||||
@@ -2946,6 +2951,12 @@ func publicFileShareHandler(w http.ResponseWriter, r *http.Request, db *database
|
||||
DownloadUrl: downloadPath,
|
||||
Token: viewerToken,
|
||||
}
|
||||
|
||||
// Set view URL for PDFs and videos (for inline viewing)
|
||||
if isPdf || strings.HasPrefix(mimeType, "video/") {
|
||||
viewerSession.ViewUrl = viewPath
|
||||
}
|
||||
|
||||
viewerSession.Capabilities.CanEdit = false
|
||||
viewerSession.Capabilities.CanAnnotate = false
|
||||
viewerSession.Capabilities.IsPdf = isPdf
|
||||
@@ -3050,6 +3061,102 @@ func publicFileDownloadHandler(w http.ResponseWriter, r *http.Request, db *datab
|
||||
io.Copy(w, resp.Body)
|
||||
}
|
||||
|
||||
func publicFileViewHandler(w http.ResponseWriter, r *http.Request, db *database.DB, cfg *config.Config, jwtManager *jwt.Manager) {
|
||||
token := chi.URLParam(r, "token")
|
||||
if token == "" {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Token required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
viewerToken := r.URL.Query().Get("token")
|
||||
if viewerToken == "" {
|
||||
errors.WriteError(w, errors.CodeInvalidArgument, "Viewer token required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
link, err := db.GetFileShareLinkByToken(r.Context(), token)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
errors.WriteError(w, errors.CodeNotFound, "Link not found or expired", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
errors.LogError(r, err, "Failed to get share link")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify viewer token (contains org ID for org files, empty for personal)
|
||||
claims, err := jwtManager.Validate(viewerToken)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Invalid viewer token")
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if link.OrgID == nil {
|
||||
if len(claims.OrgIDs) != 0 {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if len(claims.OrgIDs) == 0 {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
orgID, err := uuid.Parse(claims.OrgIDs[0])
|
||||
if err != nil {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if *link.OrgID != orgID {
|
||||
errors.WriteError(w, errors.CodeUnauthenticated, "Invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get file metadata
|
||||
file, err := db.GetFileByID(r.Context(), link.FileID)
|
||||
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 {
|
||||
errors.WriteError(w, errors.CodeNotFound, "File not accessible", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Get WebDAV client for the file's owner
|
||||
client, err := getUserWebDAVClient(r.Context(), db, *file.UserID, cfg.NextcloudURL, cfg.NextcloudUser, cfg.NextcloudPass)
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to get WebDAV client")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Stream file
|
||||
resp, err := client.Download(r.Context(), file.Path, "")
|
||||
if err != nil {
|
||||
errors.LogError(r, err, "Failed to download file")
|
||||
errors.WriteError(w, errors.CodeInternal, "Server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Copy headers
|
||||
for k, v := range resp.Header {
|
||||
w.Header()[k] = v
|
||||
}
|
||||
|
||||
// Ensure inline viewing behavior (no Content-Disposition attachment)
|
||||
w.Header().Del("Content-Disposition")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", file.Name))
|
||||
|
||||
// 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)
|
||||
|
||||
Reference in New Issue
Block a user