diff --git a/cmd/server/main.go b/cmd/server/main.go index 51b0f7e..cb9fc7e 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -99,6 +99,8 @@ func main() { r.Get("/public/{token}", nodeH.PublicMeta) r.Get("/public/{token}/download", nodeH.PublicDownload) + r.Get("/public/{token}/nodes/{id}", nodeH.PublicChildMeta) + r.Get("/public/{token}/nodes/{id}/download", nodeH.PublicChildDownload) srv := &http.Server{ Addr: ":" + cfg.ServerPort, diff --git a/internal/handler/node.go b/internal/handler/node.go index e0c1d1b..2921fe8 100644 --- a/internal/handler/node.go +++ b/internal/handler/node.go @@ -8,6 +8,7 @@ import ( "io" "net/http" "path/filepath" + "strconv" "strings" "time" @@ -368,19 +369,19 @@ func (h *NodeHandler) PublicMeta(w http.ResponseWriter, r *http.Request) { return } if node.NodeType == model.NodeTypeFile { - h.renderPublicPreview(w, r, node) + h.renderPublicPreview(w, node, h.publicURL(chi.URLParam(r, "token"))+"/download") return } - response := model.PublicNodeResponse{Node: node} if node.NodeType == model.NodeTypeFolder { children, err := h.repo.ListChildrenForPublic(r.Context(), node.ID) if err != nil { writeInternalError(w, r, err, "failed to list folder") return } - response.Children = children + h.renderPublicFolder(w, r, node, children) + return } - writeJSON(w, http.StatusOK, response) + h.renderPublicUnavailable(w, node) } func (h *NodeHandler) PublicDownload(w http.ResponseWriter, r *http.Request) { @@ -391,6 +392,36 @@ func (h *NodeHandler) PublicDownload(w http.ResponseWriter, r *http.Request) { h.streamNode(w, r, node) } +func (h *NodeHandler) PublicChildMeta(w http.ResponseWriter, r *http.Request) { + node, ok := h.publicNodeByID(w, r) + if !ok { + return + } + token := chi.URLParam(r, "token") + if node.NodeType == model.NodeTypeFile { + h.renderPublicPreview(w, node, h.publicNodeURL(token, node.ID)+"/download") + return + } + if node.NodeType == model.NodeTypeFolder { + children, err := h.repo.ListChildrenForPublic(r.Context(), node.ID) + if err != nil { + writeInternalError(w, r, err, "failed to list folder") + return + } + h.renderPublicFolder(w, r, node, children) + return + } + h.renderPublicUnavailable(w, node) +} + +func (h *NodeHandler) PublicChildDownload(w http.ResponseWriter, r *http.Request) { + node, ok := h.publicNodeByID(w, r) + if !ok { + return + } + h.streamNode(w, r, node) +} + func (h *NodeHandler) requireNode(w http.ResponseWriter, r *http.Request) (*model.Node, bool) { node, err := h.repo.GetForUser(r.Context(), chi.URLParam(r, "id"), commonmw.GetUserID(r.Context()), subordinates(r)) if errors.Is(err, repository.ErrNotFound) { @@ -441,7 +472,20 @@ func (h *NodeHandler) publicNode(w http.ResponseWriter, r *http.Request) (*model return node, true } -func (h *NodeHandler) renderPublicPreview(w http.ResponseWriter, r *http.Request, node *model.Node) { +func (h *NodeHandler) publicNodeByID(w http.ResponseWriter, r *http.Request) (*model.Node, bool) { + node, err := h.repo.GetPublicDescendant(r.Context(), chi.URLParam(r, "token"), chi.URLParam(r, "id")) + if errors.Is(err, repository.ErrNotFound) { + writeError(w, http.StatusNotFound, "public file not found") + return nil, false + } + if err != nil { + writeInternalError(w, r, err, "failed to open public file") + return nil, false + } + return node, true +} + +func (h *NodeHandler) renderPublicPreview(w http.ResponseWriter, node *model.Node, downloadURL string) { title := node.Title if node.OriginalFilename != nil && *node.OriginalFilename != "" { title = *node.OriginalFilename @@ -450,7 +494,6 @@ func (h *NodeHandler) renderPublicPreview(w http.ResponseWriter, r *http.Request if node.MimeType != nil { mimeType = strings.ToLower(*node.MimeType) } - downloadURL := h.publicURL(chi.URLParam(r, "token")) + "/download" preview := `