| 
 | 1 | +const { resolve, parse } = require('url');  | 
 | 2 | +const { join } = require('path');  | 
 | 3 | + | 
 | 4 | +const http = require('http');  | 
 | 5 | +const https = require('https');  | 
 | 6 | + | 
 | 7 | +const { unique } = require('./filter');  | 
 | 8 | + | 
 | 9 | +class ApiError extends Error {  | 
 | 10 | +  constructor(message) {  | 
 | 11 | +    super(message);  | 
 | 12 | + | 
 | 13 | +    this.jti = '';  | 
 | 14 | +    this.url = '';  | 
 | 15 | +    this.tags = [];  | 
 | 16 | +    this.code = 500;  | 
 | 17 | + | 
 | 18 | +    this.name = this.constructor.name;  | 
 | 19 | +    if (typeof Error.captureStackTrace === 'function') {  | 
 | 20 | +      Error.captureStackTrace(this, this.constructor);  | 
 | 21 | +    } else {  | 
 | 22 | +      this.stack = new Error(message).stack;  | 
 | 23 | +    }  | 
 | 24 | +    this.trySetTokenId();  | 
 | 25 | +  }  | 
 | 26 | + | 
 | 27 | +  trySetTokenId() {  | 
 | 28 | +    const [, b64] = (getToken() || '').split('.');  | 
 | 29 | +    if (b64) {  | 
 | 30 | +      try {  | 
 | 31 | +        const payload = Buffer.from(b64, 'base64').toString();  | 
 | 32 | +        this.jti = JSON.parse(payload).jti || '';  | 
 | 33 | +      } catch (err) {}  | 
 | 34 | +    }  | 
 | 35 | +  }  | 
 | 36 | + | 
 | 37 | +  withTags(tags = []) {  | 
 | 38 | +    this.tags = unique([...this.tags, ...(tags || [])]);  | 
 | 39 | +    return this;  | 
 | 40 | +  }  | 
 | 41 | + | 
 | 42 | +  withCode(code) {  | 
 | 43 | +    this.code = +code;  | 
 | 44 | +    return this;  | 
 | 45 | +  }  | 
 | 46 | + | 
 | 47 | +  withUrl(url) {  | 
 | 48 | +    this.url = url;  | 
 | 49 | +    return this;  | 
 | 50 | +  }  | 
 | 51 | +}  | 
 | 52 | + | 
 | 53 | +class NginxError extends ApiError {  | 
 | 54 | +  constructor() {  | 
 | 55 | +    super(...arguments);  | 
 | 56 | +    this.title = '';  | 
 | 57 | +  }  | 
 | 58 | + | 
 | 59 | +  withTitle(title = '') {  | 
 | 60 | +    this.title = title;  | 
 | 61 | +    return this;  | 
 | 62 | +  }  | 
 | 63 | + | 
 | 64 | +  static fromHtml(resp = '', statusCode = 500) {  | 
 | 65 | +    let [, title = 'Unknown nginx errror'] = /<title>(.*?)<\/title>/gi.exec(resp) || [];  | 
 | 66 | +    title = title.replace(statusCode.toString(), '').trim();  | 
 | 67 | +    return new NginxError(http.STATUS_CODES[statusCode])  | 
 | 68 | +      .withCode(statusCode)  | 
 | 69 | +      .withTitle(title);  | 
 | 70 | +  }  | 
 | 71 | +}  | 
 | 72 | + | 
 | 73 | +function req(url, opts = {}, data = null) {  | 
 | 74 | +  const { protocol, hostname, port, path } = parse(url);  | 
 | 75 | +  console.log({ protocol, hostname, port, path })  | 
 | 76 | + | 
 | 77 | +  const proto = protocol === 'https:' ? https : http;  | 
 | 78 | + | 
 | 79 | +  opts = {  | 
 | 80 | +    method: 'GET',  | 
 | 81 | +    host: hostname,  | 
 | 82 | +    port: +port,  | 
 | 83 | +    path,  | 
 | 84 | +    ...opts  | 
 | 85 | +  };  | 
 | 86 | + | 
 | 87 | +  if (!port) {  | 
 | 88 | +    opts.port = protocol === 'https:' ? 443 : 80;  | 
 | 89 | +  }  | 
 | 90 | + | 
 | 91 | +  return new Promise((resolve, reject) => {  | 
 | 92 | +    const req = proto.request(opts, (res) => {  | 
 | 93 | +      let resp = '';  | 
 | 94 | +      res.on('data', (chunk) => (resp += chunk.toString()));  | 
 | 95 | +      res.on('end', () => {  | 
 | 96 | +        try {  | 
 | 97 | +          /*  | 
 | 98 | +           * most nginx upstream errors should be handled by ingress default-backend  | 
 | 99 | +           * but who knows ...  | 
 | 100 | +           */  | 
 | 101 | +          if (resp.startsWith('<html>') && resp.includes('nginx')) {  | 
 | 102 | +            return reject(NginxError.fromHtml(resp, res.statusCode).withUrl(url));  | 
 | 103 | +          }  | 
 | 104 | +          const json = JSON.parse(resp);  | 
 | 105 | +          if (res.statusCode >= 400) {  | 
 | 106 | +            return reject(  | 
 | 107 | +              new ApiError(json.message)  | 
 | 108 | +                .withCode(res.statusCode)  | 
 | 109 | +                .withTags(json.tags)  | 
 | 110 | +                .withUrl(url)  | 
 | 111 | +            );  | 
 | 112 | +          }  | 
 | 113 | +          resolve(json);  | 
 | 114 | +        } catch (err) {  | 
 | 115 | +          console.log(resp);  | 
 | 116 | +          reject(err);  | 
 | 117 | +        }  | 
 | 118 | +      });  | 
 | 119 | +    });  | 
 | 120 | + | 
 | 121 | +    req.on('error', (err) => reject(err));  | 
 | 122 | + | 
 | 123 | +    if (data) {  | 
 | 124 | +      let send = data;  | 
 | 125 | +      if (typeof data === 'object') {  | 
 | 126 | +        send = JSON.stringify(data);  | 
 | 127 | +      }  | 
 | 128 | +      req.write(send);  | 
 | 129 | +    }  | 
 | 130 | +    req.end();  | 
 | 131 | +  });  | 
 | 132 | +}  | 
 | 133 | + | 
 | 134 | +class ApiClient {  | 
 | 135 | +  constructor(opts) {  | 
 | 136 | +    const { url, token } = opts || {};  | 
 | 137 | + | 
 | 138 | +    this._opts = opts;  | 
 | 139 | +    this.url = url;  | 
 | 140 | +    this.token = token;  | 
 | 141 | +  }  | 
 | 142 | + | 
 | 143 | +  getDefaultHeaders() {  | 
 | 144 | +    const defaults = {  | 
 | 145 | +      accept: 'application/json',  | 
 | 146 | +      'content-type': 'application/json',  | 
 | 147 | +      'user-agent': `npm:@rightech/utils 1.1`  | 
 | 148 | +    };  | 
 | 149 | +    if (this.token) {  | 
 | 150 | +      defaults.authorization = `Bearer ${this.token}`;  | 
 | 151 | +    }  | 
 | 152 | +    return defaults;  | 
 | 153 | +  }  | 
 | 154 | + | 
 | 155 | +  get(path, query = {}) {  | 
 | 156 | +    const url = resolve(this.url, path);  | 
 | 157 | +    const headers = this.getDefaultHeaders();  | 
 | 158 | +    return req(url, { method: 'GET', headers });  | 
 | 159 | +  }  | 
 | 160 | + | 
 | 161 | +  post(path, data = {}) {  | 
 | 162 | +    const url = resolve(this.url, path);  | 
 | 163 | +    const headers = this.getDefaultHeaders();  | 
 | 164 | +    return req(url, { method: 'POST', headers }, data);  | 
 | 165 | +  }  | 
 | 166 | + | 
 | 167 | +  patch(path, data = {}) {  | 
 | 168 | +    const url = resolve(this.url, path);  | 
 | 169 | +    const headers = this.getDefaultHeaders();  | 
 | 170 | +    return req(url, { method: 'PATCH', headers }, data);  | 
 | 171 | +  }  | 
 | 172 | + | 
 | 173 | +  delete(path) {  | 
 | 174 | +    const url = resolve(this.url, path);  | 
 | 175 | +    const headers = this.getDefaultHeaders();  | 
 | 176 | +    return req(url, { method: 'DELETE', headers });  | 
 | 177 | +  }  | 
 | 178 | + | 
 | 179 | +  with(opts = {}) {  | 
 | 180 | +    return new ApiClient({ ...(this._opts || {}), ...opts });  | 
 | 181 | +  }  | 
 | 182 | +}  | 
 | 183 | + | 
 | 184 | +module.exports = new ApiClient({  | 
 | 185 | +  url: process.env['RIC_URL'] || 'https://dev.rightech.io/',  | 
 | 186 | +  token: process.env['RIC_TOKEN']  | 
 | 187 | +});  | 
0 commit comments