-
Notifications
You must be signed in to change notification settings - Fork 0
/
publication.go
138 lines (114 loc) · 3.67 KB
/
publication.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package doc
import (
"context"
"errors"
"fmt"
"sync"
"github.com/bahner/go-ma/api"
"github.com/bahner/go-ma/did"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/path"
"github.com/ipfs/go-cid"
caopts "github.com/ipfs/kubo/core/coreiface/options"
log "github.com/sirupsen/logrus"
)
var ErrAlreadyPublished = errors.New("Document is already published")
type PublishOptions struct {
Ctx context.Context
Pin bool
Force bool
AllowBigBlock bool
}
func DefaultPublishOptions() *PublishOptions {
return &PublishOptions{
Ctx: context.Background(),
Pin: true,
Force: true,
AllowBigBlock: false,
}
}
// Publish publishes the document to IPFS and returns the CID
// If the opts is nil, the default options are used.
// NB! Publication is more than simply adding the document to IPFS.
// It's about publishing the document to IPNS and possibly pinning it.
func (d *Document) Publish(o ...*PublishOptions) (ipns.Name, error) {
alreadyPublishedString := "'to' cid was already recursively pinned"
opts := DefaultPublishOptions()
// Iterate through all options provided
for _, opt := range o {
if opt == nil {
continue // Skip any nil options
}
if opt.Force {
opts.Force = opt.Force
}
if opt.Pin {
opts.Pin = opt.Pin
}
if opt.AllowBigBlock {
opts.AllowBigBlock = opt.AllowBigBlock
}
}
_did, err := did.New(d.ID)
if err != nil {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
// Make sure a key is available for the document
ik, err := did.GetOrCreate(_did.Fragment)
if err != nil {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
data, err := d.MarshalToCBOR()
if err != nil {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
// Actually add the document to IPFS and possibly pin it and allow bib blocks.
c, err := api.IPFSDagPutCBOR(data, opts.Pin, opts.AllowBigBlock)
if err != nil {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
p := path.FromCid(c)
// Get the IPFS API
a := api.GetIPFSAPI()
// If an existing document is already published and Pin is set we need to update the existing pin f asked to force.
e, err := d.CID()
if err == nil && c != e && opts.Pin && opts.Force {
err := a.Pin().Update(opts.Ctx, path.FromCid(e), p)
if err != nil {
if err.Error() == alreadyPublishedString {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", ErrAlreadyPublished)
}
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
} else {
log.Debugf("DocPublish: Document %s is not yet published.", d.ID)
}
log.Debugf("DocPublish: Announcing publication of document %s to IPNS. Please wait ...", c.String())
n, err := a.Name().Publish(opts.Ctx, p, caopts.Name.Key(ik.Fragment))
if err != nil {
return ipns.Name{}, fmt.Errorf("DocPublish: %w", err)
}
log.Debugf("DocPublish: Successfully announced publication of document %s to %s", c.String(), n.AsPath().String())
return n, nil
}
// The Goroutine version of Publish must get a cancel function as argument.
// This is to force the caller to use a context with a cancel function.
// Obviously this should probably be a timeout context.
// Other than that it is the same as Publish.
func (d *Document) PublishGoroutine(wg *sync.WaitGroup, cancel context.CancelFunc, opts *PublishOptions) {
defer cancel()
defer wg.Done()
d.Publish(opts)
}
func (d *Document) CID() (cid.Cid, error) {
maybeDoc, c, err := Fetch(d.ID, false) // Don't accept cached document
if err != nil {
log.Debugf("DocumentPublishGoroutine: %v", err)
return cid.Cid{}, err
}
if maybeDoc == nil {
log.Debugf("DocumentPublishGoroutine: Document is nil")
return cid.Cid{}, fmt.Errorf("Document is nil")
}
return c, nil
}