3
0
corteza/compose/rest/attachment.go
2020-01-28 10:13:17 +01:00

199 lines
5.1 KiB
Go

package rest
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/compose/service"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/rh"
"github.com/pkg/errors"
)
var _ = errors.Wrap
type (
attachmentPayload struct {
*types.Attachment
}
attachmentSetPayload struct {
Filter types.AttachmentFilter `json:"filter"`
Set []*attachmentPayload `json:"set"`
}
Attachment struct {
attachment service.AttachmentService
}
)
func (Attachment) New() *Attachment {
return &Attachment{
attachment: service.DefaultAttachment,
}
}
// Attachments returns list of all files attached to records
func (ctrl Attachment) List(ctx context.Context, r *request.AttachmentList) (interface{}, error) {
if !auth.GetIdentityFromContext(ctx).Valid() {
return nil, errors.New("Unauthorized")
}
f := types.AttachmentFilter{
NamespaceID: r.NamespaceID,
Kind: r.Kind,
ModuleID: r.ModuleID,
RecordID: r.RecordID,
FieldName: r.FieldName,
PageFilter: rh.Paging(r.Page, r.PerPage),
}
set, filter, err := ctrl.attachment.With(ctx).Find(f)
return ctrl.makeFilterPayload(ctx, set, filter, err)
}
func (ctrl Attachment) Read(ctx context.Context, r *request.AttachmentRead) (interface{}, error) {
if !auth.GetIdentityFromContext(ctx).Valid() {
return nil, errors.New("Unauthorized")
}
a, err := ctrl.attachment.With(ctx).FindByID(r.NamespaceID, r.AttachmentID)
return makeAttachmentPayload(ctx, a, err)
}
func (ctrl Attachment) Delete(ctx context.Context, r *request.AttachmentDelete) (interface{}, error) {
if !auth.GetIdentityFromContext(ctx).Valid() {
return nil, errors.New("Unauthorized")
}
_, err := ctrl.attachment.With(ctx).FindByID(r.NamespaceID, r.AttachmentID)
if err != nil {
return nil, err
}
return resputil.OK(), ctrl.attachment.With(ctx).DeleteByID(r.NamespaceID, r.AttachmentID)
}
func (ctrl Attachment) Original(ctx context.Context, r *request.AttachmentOriginal) (interface{}, error) {
if err := ctrl.isAccessible(r.NamespaceID, r.AttachmentID, r.UserID, r.Sign); err != nil {
return nil, err
}
return ctrl.serve(ctx, r.NamespaceID, r.AttachmentID, false, r.Download)
}
func (ctrl Attachment) Preview(ctx context.Context, r *request.AttachmentPreview) (interface{}, error) {
if err := ctrl.isAccessible(r.NamespaceID, r.AttachmentID, r.UserID, r.Sign); err != nil {
return nil, err
}
return ctrl.serve(ctx, r.NamespaceID, r.AttachmentID, true, false)
}
func (ctrl Attachment) isAccessible(namespaceID, attachmentID, userID uint64, signature string) error {
if signature == "" {
return errors.New("Unauthorized")
}
if userID == 0 {
return errors.New("missing or invalid user ID")
}
if attachmentID == 0 {
return errors.New("missing or invalid attachment ID")
}
if !auth.DefaultSigner.Verify(signature, userID, namespaceID, attachmentID) {
return errors.New("missing or invalid signature")
}
return nil
}
func (ctrl Attachment) serve(ctx context.Context, namespaceID, attachmentID uint64, preview, download bool) (interface{}, error) {
return func(w http.ResponseWriter, req *http.Request) {
att, err := ctrl.attachment.With(ctx).FindByID(namespaceID, attachmentID)
if err != nil {
// Simplify error handling for now
w.WriteHeader(http.StatusNotFound)
return
}
var fh io.ReadSeeker
if preview {
fh, err = ctrl.attachment.OpenPreview(att)
} else {
fh, err = ctrl.attachment.OpenOriginal(att)
}
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
name := url.QueryEscape(att.Name)
if download {
w.Header().Add("Content-Disposition", "attachment; filename="+name)
} else {
w.Header().Add("Content-Disposition", "inline; filename="+name)
}
http.ServeContent(w, req, name, att.CreatedAt, fh)
}, nil
}
func (ctrl Attachment) makeFilterPayload(ctx context.Context, aa types.AttachmentSet, f types.AttachmentFilter, err error) (*attachmentSetPayload, error) {
if err != nil {
return nil, err
}
asp := &attachmentSetPayload{Filter: f, Set: make([]*attachmentPayload, len(aa))}
for i := range aa {
asp.Set[i], _ = makeAttachmentPayload(ctx, aa[i], nil)
}
return asp, nil
}
func makeAttachmentPayload(ctx context.Context, a *types.Attachment, err error) (*attachmentPayload, error) {
if err != nil || a == nil {
return nil, err
}
var (
userID = auth.GetIdentityFromContext(ctx).Identity()
signParams = fmt.Sprintf("?sign=%s&userID=%d", auth.DefaultSigner.Sign(userID, a.NamespaceID, a.ID), userID)
preview string
baseURL = fmt.Sprintf("/namespace/%d/attachment/%s/%d/", a.NamespaceID, a.Kind, a.ID)
)
if a.Meta.Preview != nil {
var ext = a.Meta.Preview.Extension
if ext == "" {
ext = "jpg"
}
preview = baseURL + fmt.Sprintf("preview.%s", ext)
}
ap := &attachmentPayload{a}
ap.Url = baseURL + fmt.Sprintf("original/%s", url.PathEscape(a.Name)) + signParams
ap.PreviewUrl = preview + signParams
return ap, nil
}