mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
GO-5364 deeplink paths for single object export
This commit is contained in:
parent
18206b6ecb
commit
276efcabb9
4 changed files with 61 additions and 18 deletions
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/core/smartblock"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/database"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/gateway"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/localstore/addr"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/localstore/objectstore"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/logging"
|
||||
|
@ -82,6 +83,7 @@ type export struct {
|
|||
accountService account.Service
|
||||
notificationService notifications.Notifications
|
||||
processService process.Service
|
||||
gatewayService gateway.Gateway
|
||||
}
|
||||
|
||||
func New() Export {
|
||||
|
@ -97,6 +99,7 @@ func (e *export) Init(a *app.App) (err error) {
|
|||
e.spaceService = app.MustComponent[space.Service](a)
|
||||
e.accountService = app.MustComponent[account.Service](a)
|
||||
e.notificationService = app.MustComponent[notifications.Notifications](a)
|
||||
e.gatewayService = app.MustComponent[gateway.Gateway](a)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -115,7 +118,7 @@ func (e *export) Export(ctx context.Context, req pb.RpcObjectListExportRequest)
|
|||
if err = queue.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
exportCtx := NewExportContext(e, req)
|
||||
exportCtx := newExportContext(e, req)
|
||||
return exportCtx.exportObjects(ctx, queue)
|
||||
}
|
||||
|
||||
|
@ -129,7 +132,7 @@ func (e *export) ExportInMemory(ctx context.Context, spaceId string, objectIds [
|
|||
}
|
||||
|
||||
res = make(map[string][]byte)
|
||||
exportCtx := NewExportContext(e, req)
|
||||
exportCtx := newExportContext(e, req)
|
||||
for _, objectId := range objectIds {
|
||||
b, err := exportCtx.exportObject(ctx, objectId)
|
||||
if err != nil {
|
||||
|
@ -191,11 +194,11 @@ type exportContext struct {
|
|||
relations map[string]struct{}
|
||||
setOfList map[string]struct{}
|
||||
objectTypes map[string]struct{}
|
||||
|
||||
gatewayUrl string
|
||||
*export
|
||||
}
|
||||
|
||||
func NewExportContext(e *export, req pb.RpcObjectListExportRequest) *exportContext {
|
||||
func newExportContext(e *export, req pb.RpcObjectListExportRequest) *exportContext {
|
||||
ec := &exportContext{
|
||||
path: req.Path,
|
||||
spaceId: req.SpaceId,
|
||||
|
@ -213,8 +216,8 @@ func NewExportContext(e *export, req pb.RpcObjectListExportRequest) *exportConte
|
|||
setOfList: make(map[string]struct{}),
|
||||
objectTypes: make(map[string]struct{}),
|
||||
relations: make(map[string]struct{}),
|
||||
|
||||
export: e,
|
||||
gatewayUrl: "http://" + e.gatewayService.Addr(),
|
||||
export: e,
|
||||
}
|
||||
return ec
|
||||
}
|
||||
|
@ -256,7 +259,13 @@ func (e *exportContext) exportObject(ctx context.Context, objectId string) ([]by
|
|||
return nil, err
|
||||
}
|
||||
|
||||
inMemoryWriter := &InMemoryWriter{}
|
||||
var docNamer Namer
|
||||
if e.format == model.Export_Markdown {
|
||||
docNamer = &deepLinkNamer{gatewayUrl: e.gatewayUrl}
|
||||
} else {
|
||||
docNamer = newNamer()
|
||||
}
|
||||
inMemoryWriter := &InMemoryWriter{fn: docNamer}
|
||||
err = e.writeDoc(ctx, inMemoryWriter, objectId, e.docs.transformToDetailsMap())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -4,22 +4,28 @@ import (
|
|||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/mill"
|
||||
"github.com/anyproto/anytype-heart/util/anyerror"
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
Path() string
|
||||
Namer() *namer
|
||||
Namer() Namer
|
||||
WriteFile(filename string, r io.Reader, lastModifiedDate int64) (err error)
|
||||
Close() (err error)
|
||||
}
|
||||
|
||||
type Namer interface {
|
||||
Get(path, hash, title, ext string) (name string)
|
||||
}
|
||||
|
||||
func uniqName() string {
|
||||
return time.Now().Format("Anytype.20060102.150405.99")
|
||||
}
|
||||
|
@ -44,7 +50,7 @@ type dirWriter struct {
|
|||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (d *dirWriter) Namer() *namer {
|
||||
func (d *dirWriter) Namer() Namer {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fn == nil {
|
||||
|
@ -108,7 +114,7 @@ type zipWriter struct {
|
|||
fn *namer
|
||||
}
|
||||
|
||||
func (d *zipWriter) Namer() *namer {
|
||||
func (d *zipWriter) Namer() Namer {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fn == nil {
|
||||
|
@ -152,16 +158,11 @@ func getZipName(path string) string {
|
|||
|
||||
type InMemoryWriter struct {
|
||||
data map[string][]byte
|
||||
fn *namer
|
||||
fn Namer
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (d *InMemoryWriter) Namer() *namer {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fn == nil {
|
||||
d.fn = newNamer()
|
||||
}
|
||||
func (d *InMemoryWriter) Namer() Namer {
|
||||
return d.fn
|
||||
}
|
||||
|
||||
|
@ -192,3 +193,28 @@ func (d *InMemoryWriter) GetData(id string) []byte {
|
|||
defer d.m.Unlock()
|
||||
return d.data[id]
|
||||
}
|
||||
|
||||
// deepLinkNamer used to render a single-object export, in md format
|
||||
type deepLinkNamer struct {
|
||||
gatewayUrl string
|
||||
}
|
||||
|
||||
func (fn *deepLinkNamer) Get(path, hash, title, ext string) (name string) {
|
||||
if ext == ".md" {
|
||||
// object links via deeplink to the app
|
||||
return "anytype://object?objectId=" + hash
|
||||
}
|
||||
|
||||
// files links via gateway
|
||||
u, err := url.Parse(fn.gatewayUrl)
|
||||
if err != nil {
|
||||
return "anytype://object?objectId=" + hash
|
||||
}
|
||||
if mill.IsImageExt(ext) {
|
||||
u.Path = "image/" + hash
|
||||
} else {
|
||||
u.Path = "file/" + hash
|
||||
}
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func ConvertTextToFile(filePath string) *model.BlockContentOfFile {
|
|||
return nil
|
||||
}
|
||||
|
||||
imageFormats := []string{"jpg", "jpeg", "png", "gif", "webp"}
|
||||
imageFormats := []string{"jpg", "jpeg", "png", "gif", "webp", "heic", "heif", "bmp", "tiff", "psd", "ico"}
|
||||
videoFormats := []string{"mp4", "m4v", "mov"}
|
||||
audioFormats := []string{"mp3", "ogg", "wav", "m4a", "flac"}
|
||||
pdfFormat := "pdf"
|
||||
|
|
|
@ -42,6 +42,14 @@ const (
|
|||
TIFF Format = "tiff"
|
||||
)
|
||||
|
||||
func IsImageExt(ext string) bool {
|
||||
switch strings.ToLower(strings.TrimPrefix(ext, ".")) {
|
||||
case "jpg", "jpeg", "png", "gif", "ico", "webp", "heic", "heif", "bmp", "tiff", "psd":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsImage(mime string) bool {
|
||||
parts := strings.SplitN(mime, "/", 2)
|
||||
if len(parts) == 1 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue