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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 | (ns ird.ipfs
"IPFS pinning, direct uploads, IPNS names and customer gateway domains
(which can serve your pins directly at /ipfs/<cid>).
Prepaid hourly billing per pinned MiB plus gateway egress; deletes are
always free. The standard IPFS Pinning Service API lives in ird.pins.
For uploading local content use `add-file`, which drives the whole
grant→upload→pin flow."
(:require
[ird.gate :as gate]
[ird.transport :as transport]))
(defn list-pins
"GET /ipfs/pins — List your pins.
Optional QUERY keys include :status, :q, :limit, :offset.
Returns the parsed response map."
([client] (list-pins client nil))
([client query]
(transport/request client :get "/ipfs/pins" {:query query})))
(defn list-all-pins
"GET /ipfs/pins — Every pin, following offset pagination.
Optional QUERY keys include :status, :q and a page :limit (default 100).
Returns a vector of pin maps."
([client] (list-all-pins client nil))
([client query]
(transport/paginate-offset client :get "/ipfs/pins" :pins query)))
(defn pin
"POST /ipfs/pins — Pin by CID (202; billing starts once pinned).
BODY keys include :cid, :name, :origins, :meta.
Returns the parsed response map."
[client body]
(transport/request client :post "/ipfs/pins" {:body body}))
(defn get-pin
"GET /ipfs/pins/{id} — Pin detail.
Returns the parsed response map."
[client id]
(transport/request client :get (str "/ipfs/pins/" id)))
(defn unpin
"DELETE /ipfs/pins/{id} — Unpin (202; free).
Returns the parsed response map."
[client id]
(transport/request client :delete (str "/ipfs/pins/" id)))
(defn start-upload
"POST /ipfs/uploads — Start a direct upload. Issues a short-lived
single-use grant {:upload {:url :token ...}}; POST the raw bytes to that
URL (see ird.transport/upload-bytes) then poll `get-upload`.
Optional BODY keys include :name, :format (\"file\" or \"car\"), :meta.
Returns the parsed response map."
([client] (start-upload client nil))
([client body]
(transport/request client :post "/ipfs/uploads" (when body {:body body}))))
(defn get-upload
"GET /ipfs/uploads/{id} — Upload status (issued/completed/failed/expired).
Returns the parsed response map."
[client id]
(transport/request client :get (str "/ipfs/uploads/" id)))
(defn list-names
"GET /ipfs/names — List your IPNS names.
Returns the parsed response map."
[client]
(transport/request client :get "/ipfs/names"))
(defn create-name
"POST /ipfs/names — Create an IPNS name.
Optional BODY keys include :label, :serve.
Returns the parsed response map."
([client] (create-name client nil))
([client body]
(transport/request client :post "/ipfs/names" (when body {:body body}))))
(defn get-name
"GET /ipfs/names/{id} — IPNS name detail.
Returns the parsed response map."
[client id]
(transport/request client :get (str "/ipfs/names/" id)))
(defn delete-name
"DELETE /ipfs/names/{id} — Delete an IPNS name (202).
Returns the parsed response map."
[client id]
(transport/request client :delete (str "/ipfs/names/" id)))
(defn publish-target
"PUT /ipfs/names/{id}/target — Publish a new target for an IPNS name.
BODY is a plain map with :cid.
Returns the parsed response map."
[client id body]
(transport/request client :put (str "/ipfs/names/" id "/target")
{:body body}))
(defn list-domains
"GET /ipfs/domains — List your gateway domains.
Returns the parsed response map."
[client]
(transport/request client :get "/ipfs/domains"))
(defn attach-domain
"POST /ipfs/domains — Attach your own domain as an IPFS gateway.
BODY keys include :domain and optional :ipns_name_id (link the domain to
one of your IPNS names) and :pin_ids (pins to serve at /ipfs/<cid> right
away — manage them later with `update-domain-pins`). Subject to the
client's domain gate on :domain.
Returns the parsed response map; its :dns_target is the gateway IPv4 the
domain's A record must point at on manually mapped rows."
[client body]
(gate/check! client "POST /ipfs/domains" {:domain (:domain body)})
(transport/request client :post "/ipfs/domains" {:body body}))
(defn get-domain
"GET /ipfs/domains/{id} — Gateway domain detail.
Returns the parsed response map."
[client id]
(transport/request client :get (str "/ipfs/domains/" id)))
(defn list-domain-pins
"GET /ipfs/domains/{id}/pins — The pins a gateway domain serves at
/ipfs/<cid>, oldest attachment first.
Optional QUERY keys are :limit (1–100, default 100) and :offset.
Returns the parsed response map {:pins :total :limit :offset}."
([client id] (list-domain-pins client id nil))
([client id query]
(transport/request client :get (str "/ipfs/domains/" id "/pins")
{:query query})))
(defn list-all-domain-pins
"GET /ipfs/domains/{id}/pins — Every pin a gateway domain serves,
following offset pagination (attachments are unbounded).
Returns a vector of pin maps."
([client id] (list-all-domain-pins client id nil))
([client id query]
(transport/paginate-offset client :get (str "/ipfs/domains/" id "/pins")
:pins query)))
(defn update-domain-pins
"POST /ipfs/domains/{id}/pins — Attach or detach the pins a gateway domain
serves. BODY keys are :add and :remove, both vectors of pin ids; the delta
is idempotent, so re-adding an attached pin or removing an absent one is a
no-op. Subject to the client's domain gate on the gateway's domain
(resolved via `get-domain` when a gate is set).
Returns the parsed response map with the domain's new :pin_count."
[client id body]
(when (:gate client)
(gate/check! client "POST /ipfs/domains/{id}/pins"
{:domain (get-in (get-domain client id) [:domain :domain])}))
(transport/request client :post (str "/ipfs/domains/" id "/pins")
{:body body}))
(defn detach-domain
"DELETE /ipfs/domains/{id} — Detach a gateway domain (202). Subject to
the client's domain gate on the gateway's domain (resolved via
`get-domain` when a gate is set).
Returns the parsed response map."
[client id]
(when (:gate client)
(gate/check! client "DELETE /ipfs/domains/{id}"
{:domain (get-in (get-domain client id) [:domain :domain])}))
(transport/request client :delete (str "/ipfs/domains/" id)))
(defn usage
"GET /ipfs/usage — Storage usage and cost.
Returns the parsed response map."
[client]
(transport/request client :get "/ipfs/usage"))
(def upload-terminal-status #{"completed" "failed" "expired"})
(defn add-file
"Convenience: upload local content to IPFS end to end — start an upload
grant, POST the bytes to the grant URL, then poll the upload until it
completes and return the final upload map (with :cid and :pin_id).
SOURCE is a file path, File, InputStream, or byte array.
OPTS:
:name display name for the pin
:format \"file\" (default) or \"car\" for a client-packed CAR
archive
:meta string→string metadata map
:interval-ms initial poll interval (default 1000, backing off to an 8s
cap — see ird.transport/poll-until)
:timeout-ms overall deadline (default 300000)
Throws ex-info :ird/upload-failed when the upload fails or expires, or
:ird/timeout when the deadline passes first."
([client source] (add-file client source nil))
([client source {:keys [name format meta interval-ms timeout-ms]}]
(let [start-body (cond-> {}
name (assoc :name name)
format (assoc :format format)
meta (assoc :meta meta))
{:keys [upload]} (start-upload client (not-empty start-body))
_ (transport/upload-bytes (:url upload) (:token upload) source
(when (= "car" format) {:format "car"}))
final (:upload (transport/poll-until
#(get-upload client (:id upload))
(fn [resp]
(contains? upload-terminal-status
(get-in resp [:upload :status])))
{:interval-ms interval-ms
:timeout-ms (or timeout-ms 300000)
:message (str "Timed out waiting for IPFS upload "
(:id upload))}))]
(if (= "completed" (:status final))
final
(throw (ex-info (str "IPFS upload " (:id final) " " (:status final))
{:type :ird/upload-failed :upload final}))))))
|