Skip to content

Instantly share code, notes, and snippets.

@jeriko
Created August 30, 2023 09:16
Show Gist options
  • Save jeriko/11d2432bde653e9549ec6dc03cd326cd to your computer and use it in GitHub Desktop.
Save jeriko/11d2432bde653e9549ec6dc03cd326cd to your computer and use it in GitHub Desktop.
recombinary web
if (typeof variableName == 'undefined') {
const process = { env: {} }
// the browser complained about "process" not existing in readable package
}
import axios from 'axios'
import qs from 'qs';
let tokenCache;
async function getToken() {
console.log('get token')
// TODO this should handle refresh tokens somehow too idk
if (process.env.RECOMBINARY_TOKEN) {
return process.env.RECOMBINARY_TOKEN
}
if (!process.env.RECOMBINARY_CLIENT_ID || !process.env.RECOMBINARY_CLIENT_SECRET) {
throw ('Please set either RECOMBINARY_TOKEN or both RECOMBINARY_CLIENT_ID and RECOMBINARY_CLIENT_SECRET. Sign up at https://platform.recombinary.com :)')
}
const url = 'https://recombinary.eu.auth0.com/oauth/token';
let currentTime = Math.floor(Date.now() / 1000)
let oneHourFromNow = currentTime + (60 * 60)
if (tokenCache && tokenCache.expires_at > oneHourFromNow) {
return tokenCache.access_token
}
const data = {
client_id: process.env.RECOMBINARY_CLIENT_ID,
client_secret: process.env.RECOMBINARY_CLIENT_SECRET,
audience: 'https://api.recombinary.com',
grant_type: 'client_credentials',
};
try {
const response = await axios.post(url, data, {
headers: { 'Content-Type': 'application/json' },
});
if (response.data && response.data.access_token) {
tokenCache = response.data
tokenCache.expires_at = currentTime + tokenCache.expires_in
return response.data.access_token;
} else {
throw new Error('No access token found in the response');
}
} catch (error) {
tokenCache = null
console.error(`Error fetching API key: ${error.message}`);
throw error;
}
}
class API {
constructor(domain, token = null) {
this.domain = domain;
this.token = token
}
auth(token) {
this.token = token
}
async getHeaders() {
let token = this.token || await getToken()
return { Authorization: `Bearer ${token}` };
}
url(path = '') {
return `https://${this.domain}/${path}`;
}
async get(path = '', queryParams = {}) {
let headers = await this.getHeaders();
let query = qs.stringify(queryParams);
let url = this.url(path) + (query ? `?${query}` : '');
let { data, status } = await axios.get(url, { headers });
if (status >= 400) { throw new Error(data); }
return data;
}
async sendRequest(method, path, payload, responseType = null) {
if (typeof path === 'object') {
payload = path;
path = '';
}
let options = { headers: await this.getHeaders() }
if (responseType) {
options['responseType'] = responseType;
}
if (responseType) options['responseType'] = responseType
return axios({
method,
url: this.url(path),
data: payload,
...options
});
}
async post(path, payload, responseType = null) {
let { data, status } = await this.sendRequest('POST', path, payload, responseType);
if (status >= 400) { throw new Error(data); } // TODO rather throw the content of the response.data?
return data;
}
async stream(path, payload) {
if (typeof path === 'object') {
payload = path;
path = '';
}
const url = this.url(path) + `?stream=true`;
console.log(url)
console.log(payload)
let options = { headers: await this.getHeaders() }
options.headers['Content-Type'] = 'application/json'
console.log(options)
// Create an EventTarget-like object to allow registration of event listeners
const eventTarget = new EventTarget();
const response = await fetch(url, {
method: 'POST',
...options,
body: JSON.stringify(payload)
});
const reader = await response.body.getReader();
const stream = new ReadableStream({
async start(controller) {
while (true) {
const { done, value } = await reader.read();
if (done) {
eventTarget.dispatchEvent(new Event('end'));
controller.close();
return;
}
eventTarget.dispatchEvent(new MessageEvent('data', { data: new TextDecoder().decode(value) }));
controller.enqueue(value);
}
}
});
return {
on: (eventName, callback) => eventTarget.addEventListener(eventName, callback),
off: (eventName, callback) => eventTarget.removeEventListener(eventName, callback)
};
}
}
function connect(domain, token) {
const api = new API(domain, token);
const callableInstance = async function(...args) {
if (args.length === 0) {
return api.get();
}
if (typeof args[0] === 'string' && args.length === 1) {
return api.get(args[0]);
}
if (typeof args[0] === 'object') {
return api.post(args[0]);
}
if (typeof args[0] === 'string' && typeof args[1] === 'object') {
return api.post(args[0], args[1]);
}
throw new Error("Invalid arguments");
}
callableInstance.get = api.get.bind(api);
callableInstance.post = api.post.bind(api);
callableInstance.stream = api.stream.bind(api);
return callableInstance;
}
const cpu = connect('cpu.recombinary.com')
const drive = connect('drive.recombinary.com')
const wallet = connect('wallet.recombinary.com')
const io = connect('io.recombinary.com')
async function encode(fileBuffer, mimeType = 'audio/mpeg') {
const bytes = new Uint8Array(fileBuffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
const base64 = window.btoa(binary);
return `data:${mimeType};base64,${base64}`;
}
export { API, connect, encode, cpu, drive, wallet, io };
const recombinary = { API, connect, encode, cpu, drive, wallet, io }
export default recombinary;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment