diff --git a/ipfs-pinning-service.yaml b/ipfs-pinning-service.yaml index 9127b1c..7b3c393 100644 --- a/ipfs-pinning-service.yaml +++ b/ipfs-pinning-service.yaml @@ -476,6 +476,7 @@ components: requestid: description: Globally unique identifier of the pin request; can be used to check the status of ongoing pinning, or pin removal type: string + format: uuid example: "UniqueIdOfPinRequest" status: $ref: '#/components/schemas/Status' diff --git a/lib/IpfsUpload/Controller/Pins.pm b/lib/IpfsUpload/Controller/Pins.pm index e76f84d..f6ebdc1 100644 --- a/lib/IpfsUpload/Controller/Pins.pm +++ b/lib/IpfsUpload/Controller/Pins.pm @@ -81,6 +81,152 @@ sub get($c) { }); } +sub replace($c) { + $c->openapi->valid_input or return; + my $uid = $c->stash('uid'); + my $id = $c->param('requestid'); + + my $body = $c->req->json; + + my $cid = $body->{cid}; + my $name = $body->{name}; + my $origins = $body->{origins}; + # No support for meta. + + return $c->pins->get({ + id => $id, + })->then(sub ($pin) { + if (!defined $pin) { + return $c->render(status => 404, openapi => { + error => { + reason => "NOT_FOUND", + details => "The specified resource was not found", + }, + }); + } + if ($pin->{uid} ne $uid) { + return $c->render(status => 401, openapi => { + error => { + reason => "UNAUTHORIZED", + details => "You cannot replace that pin.", + }, + }); + } + + # Begin add process + my @requests; + + push @requests, Mojo::Promise->resolve(1); + + # Firstly try to direct to possible origins. + if (defined $origins) { + for my $origin (@$origins) { + my $url = Mojo::URL->new($c->config->{ipfs}->{gatewayWriteUrl}); + $url->path("api/v0/swarm/peering/add"); + $url->query({ + arg => $origin, + }); + + push @requests, $c->ua->post_p($url); + } + } + + Mojo::Promise->all_settled(@requests)->then(sub(@results) { + # I had to look at the sourcecode for this. + # my $peer_failed = 0; + # for my $res (@results) { + # if ($res->{status} eq 'fulfilled') { + # my $tx = $res->{value}; + # + # # Handle first initial empty promise + # if ($tx == 1) { + # next; + # } + # + # if (!$tx->result->is_success) { + # say "Failure to add IPFS peer."; + # $peer_failed = 1; + # last; + # } + # } else { + # say "Failure to connect!"; + # die $res->{reason}; + # } + # } + + my $url = Mojo::URL->new($c->config->{ipfs}->{gatewayWriteUrl}); + $url->path("api/v0/pin/add"); + $url->query({ + arg => $cid, + recursive => "true", + # It seems progress is a stream. Not sure how to handle that? + # Let's just assume it works immediately. + # TODO. + progress => "false", + }); + + return $c->ua->post_p($url); + })->then(sub($tx) { + my $res = $tx->result; + + if ($res->is_success) { + # Now we do DB stuff. + return $c->pins->update({ + cid => $cid, + name => $name, + }, { + id => $id, + }); + } else { + # TODO: read error and use appropriate response + say $res->body; + die "Failed to pin."; + } + })->then(sub($res) { + if (!defined $res) { + die "DB Failure"; + } + + # Now do deletion of previous CID. + return $c->pins->cid_count($pin->{cid}); + })->then(sub($count) { + # Zero this time, since we already replaced the DB entry. + say "Count: $count"; + if ($count == 0) { + my $url = Mojo::URL->new($c->config->{ipfs}->{gatewayWriteUrl}); + $url->path("api/v0/pin/rm"); + $url->query({ + arg => $pin->{cid}, + recursive => "true", + }); + + return $c->ua->post_p($url); + } + + return 1; + })->then(sub($tx) { + if ($tx != 1) { + my $res = $tx->result; + if (!$res->is_success) { + die "Could not delete pin!"; + } + } + + return $c->render(openapi => { + requestid => $id, + # TODO + status => "pinned", + created => IpfsUpload::Util::date_format($pin->{created_at}), + pin => { + cid => $cid, + name => $name, + }, + delegates => $c->config->{ipfs}->{delegates}, + }, status => 202); + }); + }); +} + sub delete($c) { $c->openapi->valid_input or return; my $uid = $c->stash('uid'); diff --git a/lib/IpfsUpload/Model/Pins.pm b/lib/IpfsUpload/Model/Pins.pm index 1f82729..758ab2a 100644 --- a/lib/IpfsUpload/Model/Pins.pm +++ b/lib/IpfsUpload/Model/Pins.pm @@ -23,6 +23,12 @@ sub get($self, $where) { }); } +sub update($self, $update, $where) { + return $self->pg->db->update_p('pins', $update, $where, { returning => ['cid', 'id']})->then(sub ($res) { + return $res->hash; + }); +} + sub cid_count($self, $cid) { return $self->pg->db->select_p('pins', 'cid', {cid => $cid})->then(sub ($res) { return $res->rows;