summaryrefslogtreecommitdiff
path: root/srv/src/http/static/api.js
blob: 55c9ecd6e81468a640daf99a66f42a967551f08f (plain)
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
import * as utils from "/static/utils.js";

const doFetch = async (req) => {
  let res, jsonRes;
  try {
    res = await fetch(req);
    jsonRes = await res.json();

  } catch (e) {

    if (e instanceof SyntaxError)
      e = new Error(`status ${res.status}, empty (or invalid) response body`);

    console.error(`api call ${req.method} ${req.url}: unexpected error:`, e);
    throw e;
  }

  if (jsonRes.error) {
    console.error(
      `api call ${req.method} ${req.url}: application error:`,
      res.status,
      jsonRes.error,
    );

    throw jsonRes.error;
  }

  return jsonRes;
}

// may throw
const solvePow = async () => {

  const res = await call('/api/pow/challenge');

  const worker = new Worker('/static/solvePow.js');

  const p = new Promise((resolve, reject) => {
    worker.postMessage({seedHex: res.seed, target: res.target});
    worker.onmessage = resolve;
  });

  const powSol = (await p).data;
  worker.terminate();

  return {seed: res.seed, solution: powSol};
}

const call = async (route, opts = {}) => {
  const {
    method = 'POST',
    body = {},
    requiresPow = false,
  } = opts;

  const reqOpts = {
    method,
  };

  if (requiresPow) {
    const {seed, solution} = await solvePow();
    body.powSeed = seed;
    body.powSolution = solution;
  }

  if (Object.keys(body).length > 0) {
    const form = new FormData();
    for (const key in body) form.append(key, body[key]);

    reqOpts.body = form;
  }

  const req = new Request(route, reqOpts);
  return doFetch(req);
}

const ws = async (route, opts = {}) => {
  const {
    requiresPow = false,
    params = {},
  } = opts;

  const docURL = new URL(document.URL);
  const protocol = docURL.protocol == "http:" ? "ws:" : "wss:";

  const fullParams = new URLSearchParams(params);

  if (requiresPow) {
    const {seed, solution} = await solvePow();
    fullParams.set("powSeed", seed);
    fullParams.set("powSolution", solution);
  }

  const rawConn = new WebSocket(`${protocol}//${docURL.host}${route}?${fullParams.toString()}`);

  const conn = {
    next: () => new Promise((resolve, reject) => {
      rawConn.onmessage = (m) => {
        const mj = JSON.parse(m.data);
        resolve(mj);
      };
      rawConn.onerror = reject;
      rawConn.onclose = reject;
    }),

    close: rawConn.close,
  };

  return new Promise((resolve, reject) => {
    rawConn.onopen = () => resolve(conn);
    rawConn.onerror = reject;
  });
}

export {
  call,
  ws
}