mirror of
https://github.com/kovetskiy/mark.git
synced 2025-04-24 05:42:40 +08:00
256 lines
4.8 KiB
Go
256 lines
4.8 KiB
Go
package mark
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"io"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/kovetskiy/mark/pkg/confluence"
|
|
"github.com/reconquest/karma-go"
|
|
"github.com/reconquest/pkg/log"
|
|
)
|
|
|
|
const (
|
|
AttachmentChecksumPrefix = `mark:checksum: `
|
|
)
|
|
|
|
type Attachment struct {
|
|
ID string
|
|
Name string
|
|
Filename string
|
|
Path string
|
|
Checksum string
|
|
Link string
|
|
Replace string
|
|
}
|
|
|
|
func ResolveAttachments(
|
|
api *confluence.API,
|
|
page *confluence.PageInfo,
|
|
base string,
|
|
replacements []string,
|
|
) ([]Attachment, error) {
|
|
attaches, err := prepareAttachments(base, replacements)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, attach := range attaches {
|
|
checksum, err := getChecksum(attach.Path)
|
|
if err != nil {
|
|
return nil, karma.Format(
|
|
err,
|
|
"unable to get checksum for attachment: %q", attach.Name,
|
|
)
|
|
}
|
|
|
|
attach.Checksum = checksum
|
|
}
|
|
|
|
remotes, err := api.GetAttachments(page.ID)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
existing := []Attachment{}
|
|
creating := []Attachment{}
|
|
updating := []Attachment{}
|
|
for _, attach := range attaches {
|
|
var found bool
|
|
var same bool
|
|
for _, remote := range remotes {
|
|
if remote.Filename == attach.Filename {
|
|
same = attach.Checksum == strings.TrimPrefix(
|
|
remote.Metadata.Comment,
|
|
AttachmentChecksumPrefix,
|
|
)
|
|
|
|
attach.ID = remote.ID
|
|
attach.Link = path.Join(
|
|
remote.Links.Context,
|
|
remote.Links.Download,
|
|
)
|
|
|
|
found = true
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if found {
|
|
if same {
|
|
existing = append(existing, attach)
|
|
} else {
|
|
updating = append(updating, attach)
|
|
}
|
|
} else {
|
|
creating = append(creating, attach)
|
|
}
|
|
}
|
|
|
|
for i, attach := range creating {
|
|
log.Infof(nil, "creating attachment: %q", attach.Name)
|
|
|
|
info, err := api.CreateAttachment(
|
|
page.ID,
|
|
attach.Filename,
|
|
AttachmentChecksumPrefix+attach.Checksum,
|
|
attach.Path,
|
|
)
|
|
if err != nil {
|
|
return nil, karma.Format(
|
|
err,
|
|
"unable to create attachment %q",
|
|
attach.Name,
|
|
)
|
|
}
|
|
|
|
attach.ID = info.ID
|
|
attach.Link = path.Join(
|
|
info.Links.Context,
|
|
info.Links.Download,
|
|
)
|
|
|
|
creating[i] = attach
|
|
}
|
|
|
|
for i, attach := range updating {
|
|
log.Infof(nil, "updating attachment: %q", attach.Name)
|
|
|
|
info, err := api.UpdateAttachment(
|
|
page.ID,
|
|
attach.ID,
|
|
attach.Name,
|
|
AttachmentChecksumPrefix+attach.Checksum,
|
|
attach.Path,
|
|
)
|
|
if err != nil {
|
|
return nil, karma.Format(
|
|
err,
|
|
"unable to update attachment %q",
|
|
attach.Name,
|
|
)
|
|
}
|
|
|
|
attach.Link = path.Join(
|
|
info.Links.Context,
|
|
info.Links.Download,
|
|
)
|
|
|
|
updating[i] = attach
|
|
}
|
|
|
|
attaches = []Attachment{}
|
|
attaches = append(attaches, existing...)
|
|
attaches = append(attaches, creating...)
|
|
attaches = append(attaches, updating...)
|
|
|
|
return attaches, nil
|
|
}
|
|
|
|
func prepareAttachments(base string, replacements []string) ([]Attachment, error) {
|
|
attaches := []Attachment{}
|
|
for _, name := range replacements {
|
|
attach := Attachment{
|
|
Name: name,
|
|
Filename: strings.ReplaceAll(name, "/", "_"),
|
|
Path: filepath.Join(base, name),
|
|
Replace: name,
|
|
}
|
|
|
|
attaches = append(attaches, attach)
|
|
}
|
|
|
|
return attaches, nil
|
|
}
|
|
|
|
func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte {
|
|
links := map[string]string{}
|
|
replaces := []string{}
|
|
|
|
for _, attach := range attaches {
|
|
uri, err := url.ParseRequestURI(attach.Link)
|
|
if err != nil {
|
|
links[attach.Replace] = strings.ReplaceAll("&", "&", attach.Link)
|
|
} else {
|
|
links[attach.Replace] = uri.Path +
|
|
"?" + url.QueryEscape(uri.Query().Encode())
|
|
}
|
|
|
|
replaces = append(replaces, attach.Replace)
|
|
}
|
|
|
|
// sort by length so first items will have bigger length
|
|
// it's helpful for replacing in case of following names
|
|
// attachments/a.jpg
|
|
// attachments/a.jpg.jpg
|
|
// so we replace longer and then shorter
|
|
sort.SliceStable(replaces, func(i, j int) bool {
|
|
return len(replaces[i]) > len(replaces[j])
|
|
})
|
|
|
|
for _, replace := range replaces {
|
|
to := links[replace]
|
|
|
|
found := false
|
|
if bytes.Contains(markdown, []byte("attachment://"+replace)) {
|
|
from := "attachment://" + replace
|
|
|
|
log.Debugf(nil, "replacing legacy link: %q -> %q", from, to)
|
|
|
|
markdown = bytes.ReplaceAll(
|
|
markdown,
|
|
[]byte(from),
|
|
[]byte(to),
|
|
)
|
|
|
|
found = true
|
|
}
|
|
|
|
if bytes.Contains(markdown, []byte(replace)) {
|
|
from := replace
|
|
|
|
log.Debugf(nil, "replacing link: %q -> %q", from, to)
|
|
|
|
markdown = bytes.ReplaceAll(
|
|
markdown,
|
|
[]byte(from),
|
|
[]byte(to),
|
|
)
|
|
|
|
found = true
|
|
}
|
|
|
|
if !found {
|
|
log.Warningf(nil, "unused attachment: %s", replace)
|
|
}
|
|
}
|
|
|
|
return markdown
|
|
}
|
|
|
|
func getChecksum(filename string) (string, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return "", karma.Format(
|
|
err,
|
|
"unable to open file",
|
|
)
|
|
}
|
|
defer file.Close()
|
|
|
|
hash := sha256.New()
|
|
if _, err := io.Copy(hash, file); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return hex.EncodeToString(hash.Sum(nil)), nil
|
|
}
|