cljs / src / ird / client.cljs
497 words
 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
(ns ird.client
  "ClojureScript (Node.js) client for the ird public API (https://api.ird.ro).

  Create a client with `make-client`, then call functions in the resource
  namespaces (`ird.domains`, `ird.vps`, `ird.jobs`, …). Every one of those
  returns a core.async channel delivering exactly one value: the parsed
  response map, or an ExceptionInfo (see ird.async and ird.errors). Request
  bodies and responses use plain maps whose keys match the documented
  snake_case field names (e.g. {:auto_renew true}).

  Authentication is a single Bearer credential: either an API key created in
  the account panel (`ird_...`) or an account JWT from the device login flow
  (ird.auth/device-login) — pass whichever as :api-key. A client without
  :api-key can call the anonymous endpoints (domain check/example, device
  login)."
  (:require [clojure.string :as str]
            [cljs.core.async :as async]
            [ird.config :as config]
            [ird.errors :as err]
            [ird.gate :as gate]
            [ird.transport :as transport]))

(defn make-client
  "Create an ird API client: a plain map. Synchronous, makes no requests.

  Options:
    :api-key    (optional) API key (`ird_...`) or account JWT, sent as a
                Bearer token. Omit for anonymous access to the few endpoints
                that allow it.
    :api-url    (optional) base URL override; defaults to https://api.ird.ro.
    :headers    (optional) extra headers merged over the computed ones on
                every request (e.g. {\"User-Agent\" \"my-app\"}).
    :timeout-ms (optional) per-request timeout, applied with
                AbortSignal.timeout; a timed-out request is delivered as an
                :ird/transport-error.
    :http-fn    (optional) transport override for tests, proxies and
                instrumentation: a fn of the request map (see
                ird.transport/build-request) returning a channel of
                {:status <int> :body <string>} or an Error. Defaults to
                ird.transport/fetch-http over the global fetch.
    :sleep-fn   (optional) fn of ms returning a channel that closes after
                ms, used by the polling helpers; defaults to
                cljs.core.async/timeout.
    :verbose    (optional) when truthy, logs method and URL of each request
                to stderr.

  Domain-gate options (:denied-domains / :allowed-domains) need a network
  round trip and are therefore only accepted by `make-gated-client`; passing
  them here throws an :ird/config-error."
  [{:keys [api-key api-url headers timeout-ms http-fn sleep-fn verbose] :as opts}]
  (when (or (contains? opts :denied-domains) (contains? opts :allowed-domains))
    (throw (err/config-error
            "Domain gate options need a request to attach: use make-gated-client (returns a channel)"
            (select-keys opts [:denied-domains :allowed-domains]))))
  {:api-key api-key
   :api-url (-> (or (not-empty (some-> api-url str/trim)) config/api-base-url)
                (str/replace #"/+$" ""))
   :headers (or headers {})
   :timeout-ms timeout-ms
   :http-fn (or http-fn transport/fetch-http)
   :sleep-fn (or sleep-fn async/timeout)
   :verbose (boolean verbose)})

(defn make-gated-client
  "Create a client with a client-side domain gate. Takes every `make-client`
  option plus:
    :denied-domains  domain names/patterns (e.g. \"example.ro\",
                     \"*.example.ro\") that this client must never mutate,
                     even though the account could.
    :allowed-domains when set, ONLY these domain names/patterns may be
                     mutated; everything else is denied (an empty collection
                     denies every domain mutation). Deny wins over allow.

  Requires :api-key: the account's registrations are fetched up front to map
  registration ids to domain names, so every domain-touching mutation is
  checked client-side before any request is made; a violation is delivered
  as an :ird/domain-denied error. See ird.gate.

  Returns a channel delivering the gated client, or an error (an
  :ird/config-error without :api-key, or whatever the registration fetch
  failed with)."
  [{:keys [denied-domains allowed-domains] :as opts}]
  (gate/attach (make-client (dissoc opts :denied-domains :allowed-domains))
               {:denied-domains denied-domains
                :allowed-domains allowed-domains}))

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/cljs.git