mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 06:57:40 +08:00
259 lines
5.0 KiB
Go
259 lines
5.0 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/ipfs/boxo/path"
|
|
"github.com/ipfs/go-cid"
|
|
iface "github.com/ipfs/kubo/core/coreiface"
|
|
caopts "github.com/ipfs/kubo/core/coreiface/options"
|
|
)
|
|
|
|
type PinAPI HttpApi
|
|
|
|
type pinRefKeyObject struct {
|
|
Type string
|
|
}
|
|
|
|
type pinRefKeyList struct {
|
|
Keys map[string]pinRefKeyObject
|
|
}
|
|
|
|
type pin struct {
|
|
path path.ImmutablePath
|
|
typ string
|
|
name string
|
|
err error
|
|
}
|
|
|
|
func (p pin) Err() error {
|
|
return p.err
|
|
}
|
|
|
|
func (p pin) Path() path.ImmutablePath {
|
|
return p.path
|
|
}
|
|
|
|
func (p pin) Name() string {
|
|
return p.name
|
|
}
|
|
|
|
func (p pin) Type() string {
|
|
return p.typ
|
|
}
|
|
|
|
func (api *PinAPI) Add(ctx context.Context, p path.Path, opts ...caopts.PinAddOption) error {
|
|
options, err := caopts.PinAddOptions(opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return api.core().Request("pin/add", p.String()).
|
|
Option("recursive", options.Recursive).Exec(ctx, nil)
|
|
}
|
|
|
|
type pinLsObject struct {
|
|
Cid string
|
|
Name string
|
|
Type string
|
|
}
|
|
|
|
func (api *PinAPI) Ls(ctx context.Context, pins chan<- iface.Pin, opts ...caopts.PinLsOption) error {
|
|
defer close(pins)
|
|
|
|
options, err := caopts.PinLsOptions(opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
res, err := api.core().Request("pin/ls").
|
|
Option("type", options.Type).
|
|
Option("stream", true).
|
|
Send(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Output.Close()
|
|
|
|
dec := json.NewDecoder(res.Output)
|
|
var out pinLsObject
|
|
for {
|
|
err := dec.Decode(&out)
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
c, err := cid.Parse(out.Cid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
select {
|
|
case pins <- pin{typ: out.Type, name: out.Name, path: path.FromCid(c)}:
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
}
|
|
|
|
// IsPinned returns whether or not the given cid is pinned
|
|
// and an explanation of why its pinned.
|
|
func (api *PinAPI) IsPinned(ctx context.Context, p path.Path, opts ...caopts.PinIsPinnedOption) (string, bool, error) {
|
|
options, err := caopts.PinIsPinnedOptions(opts...)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
var out pinRefKeyList
|
|
err = api.core().Request("pin/ls").
|
|
Option("type", options.WithType).
|
|
Option("arg", p.String()).
|
|
Exec(ctx, &out)
|
|
if err != nil {
|
|
// TODO: This error-type discrimination based on sub-string matching is brittle.
|
|
// It is addressed by this open issue: https://github.com/ipfs/go-ipfs/issues/7563
|
|
if strings.Contains(err.Error(), "is not pinned") {
|
|
return "", false, nil
|
|
}
|
|
return "", false, err
|
|
}
|
|
|
|
for _, obj := range out.Keys {
|
|
return obj.Type, true, nil
|
|
}
|
|
return "", false, errors.New("http api returned no error and no results")
|
|
}
|
|
|
|
func (api *PinAPI) Rm(ctx context.Context, p path.Path, opts ...caopts.PinRmOption) error {
|
|
options, err := caopts.PinRmOptions(opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return api.core().Request("pin/rm", p.String()).
|
|
Option("recursive", options.Recursive).
|
|
Exec(ctx, nil)
|
|
}
|
|
|
|
func (api *PinAPI) Update(ctx context.Context, from path.Path, to path.Path, opts ...caopts.PinUpdateOption) error {
|
|
options, err := caopts.PinUpdateOptions(opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return api.core().Request("pin/update", from.String(), to.String()).
|
|
Option("unpin", options.Unpin).Exec(ctx, nil)
|
|
}
|
|
|
|
type pinVerifyRes struct {
|
|
ok bool
|
|
badNodes []iface.BadPinNode
|
|
err error
|
|
}
|
|
|
|
func (r pinVerifyRes) Ok() bool {
|
|
return r.ok
|
|
}
|
|
|
|
func (r pinVerifyRes) BadNodes() []iface.BadPinNode {
|
|
return r.badNodes
|
|
}
|
|
|
|
func (r pinVerifyRes) Err() error {
|
|
return r.err
|
|
}
|
|
|
|
type badNode struct {
|
|
err error
|
|
cid cid.Cid
|
|
}
|
|
|
|
func (n badNode) Path() path.ImmutablePath {
|
|
return path.FromCid(n.cid)
|
|
}
|
|
|
|
func (n badNode) Err() error {
|
|
return n.err
|
|
}
|
|
|
|
func (api *PinAPI) Verify(ctx context.Context) (<-chan iface.PinStatus, error) {
|
|
resp, err := api.core().Request("pin/verify").Option("verbose", true).Send(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.Error != nil {
|
|
return nil, resp.Error
|
|
}
|
|
res := make(chan iface.PinStatus)
|
|
|
|
go func() {
|
|
defer resp.Close()
|
|
defer close(res)
|
|
dec := json.NewDecoder(resp.Output)
|
|
for {
|
|
var out struct {
|
|
Cid string
|
|
Err string
|
|
Ok bool
|
|
|
|
BadNodes []struct {
|
|
Cid string
|
|
Err string
|
|
}
|
|
}
|
|
if err := dec.Decode(&out); err != nil {
|
|
if err == io.EOF {
|
|
return
|
|
}
|
|
select {
|
|
case res <- pinVerifyRes{err: err}:
|
|
return
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
|
|
if out.Err != "" {
|
|
select {
|
|
case res <- pinVerifyRes{err: errors.New(out.Err)}:
|
|
return
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
|
|
badNodes := make([]iface.BadPinNode, len(out.BadNodes))
|
|
for i, n := range out.BadNodes {
|
|
c, err := cid.Decode(n.Cid)
|
|
if err != nil {
|
|
badNodes[i] = badNode{cid: c, err: err}
|
|
continue
|
|
}
|
|
|
|
if n.Err != "" {
|
|
err = errors.New(n.Err)
|
|
}
|
|
badNodes[i] = badNode{cid: c, err: err}
|
|
}
|
|
|
|
select {
|
|
case res <- pinVerifyRes{ok: out.Ok, badNodes: badNodes}:
|
|
case <-ctx.Done():
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (api *PinAPI) core() *HttpApi {
|
|
return (*HttpApi)(api)
|
|
}
|