prismarine-realms
Version:
Library to manage and interact with Minecraft Realms for Java/Bedrock edition
89 lines (72 loc) • 2.72 kB
JavaScript
const debug = require('debug')('prismarine-realms')
const fetch = require('node-fetch')
const constants = require('./constants')
const { formatBedrockAuth, formatJavaAuth } = require('./util')
module.exports = class Rest {
constructor (authflow, platform, options) {
this.options = options
this.platform = platform
this.host = constants[platform].host
this.userAgent = constants[platform].userAgent
if (platform === 'bedrock') {
this.getAuth = () => authflow.getXboxToken(constants.bedrock.relyingParty).then(formatBedrockAuth)
} else if (platform === 'java') {
this.getAuth = () => authflow.getMinecraftJavaToken({ fetchProfile: true }).then(formatJavaAuth)
}
this.maxRetries = options.maxRetries ?? 4
}
get (route, options) {
return this.prepareRequest({ ...options, route, method: 'get' })
}
post (route, options) {
return this.prepareRequest({ ...options, route, method: 'post' })
}
put (route, options) {
return this.prepareRequest({ ...options, route, method: 'put' })
}
delete (route, options) {
return this.prepareRequest({ ...options, route, method: 'delete' })
}
async prepareRequest (request) {
const url = `${this.host}${request.route}`
const headers = {
'Client-Version': '0.0.0',
'User-Agent': this.userAgent
}
if (!this.options.skipAuth) {
const [key, value] = await this.getAuth()
headers[key] = value
}
let body
if (request.body !== null) {
body = JSON.stringify(request.body)
headers['Content-Type'] = 'application/json'
}
const fetchOptions = {
method: request.method,
body,
headers: { ...headers, ...request.headers }
}
return this.execRequest(url, fetchOptions)
}
async execRequest (url, options, retries = 0) {
const response = await fetch(url, options)
if (response.ok) {
if (response.headers.get('Content-Type')?.startsWith('application/json')) {
return response.json()
}
return response.text()
} else {
debug('Request fail', response)
// Some endpoints on the Realms API can be very intermittent and may fail with error code 503. We retry 5xx errors a maximum of 4 times to help mitigate this
if (response.status >= 500 && response.status < 600 && retries < this.maxRetries) {
const delay = Math.pow(2, retries) * 1000
debug('retry', retries, 'in', delay, 'ms')
await new Promise(resolve => setTimeout(resolve, delay))
return this.execRequest(url, options, ++retries)
}
const body = await response.text()
throw new Error(`${response.status} ${response.statusText} ${body}`)
}
}
}