pixelplanet/src/store/actions/fetch.js

381 lines
7.9 KiB
JavaScript

/*
* Collect api fetch commands for actions here
* (chunk and tiles requests in ui/ChunkLoader*.js)
*
*/
import { t } from 'ttag';
import { dateToString } from '../../core/utils';
export const shardHost = (function getShardHost() {
if (!window.ssv
|| !window.ssv.shard
|| window.location.host === 'fuckyouarkeros.fun'
) {
return '';
}
const hostParts = window.location.host.split('.');
if (hostParts.length > 2) {
hostParts.shift();
}
return `${window.ssv.shard}.${hostParts.join('.')}`;
}());
export const shardOrigin = shardHost
&& `${window.location.protocol}//${shardHost}`;
/*
* Adds customizable timeout to fetch
* defaults to 8s
*/
async function fetchWithTimeout(url, options = {}) {
const { timeout = 30000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
...options,
signal: controller.signal,
});
clearTimeout(id);
return response;
}
/*
* Parse response from API
* @param response
* @return Object of response
*/
async function parseAPIresponse(response) {
const { status: code } = response;
if (code === 429) {
let error = t`You made too many requests`;
const retryAfter = response.headers.get('Retry-After');
if (!Number.isNaN(Number(retryAfter))) {
const ti = Math.floor(retryAfter / 60);
error += `, ${t`try again after ${ti}min`}`;
}
return {
errors: [error],
};
}
try {
return await response.json();
} catch (e) {
return {
errors: [t`Connection error ${code} :(`],
};
}
}
/*
* Make API POST Request
* @param url URL of post api endpoint
* @param body Body of request
* @return Object with response or error Array
*/
async function makeAPIPOSTRequest(
url,
body,
credentials = true,
addShard = true,
) {
if (addShard) {
url = `${shardOrigin}${url}`;
}
try {
const response = await fetchWithTimeout(url, {
method: 'POST',
credentials: (credentials) ? 'include' : 'omit',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
return parseAPIresponse(response);
} catch (e) {
return {
errors: [t`Could not connect to server, please try again later :(`],
};
}
}
/*
* Make API GET Request
* @param url URL of get api endpoint
* @return Object with response or error Array
*/
async function makeAPIGETRequest(
url,
credentials = true,
addShard = true,
) {
if (addShard) {
url = `${shardOrigin}${url}`;
}
try {
const response = await fetchWithTimeout(url, {
credentials: (credentials) ? 'include' : 'omit',
});
return parseAPIresponse(response);
} catch (e) {
return {
errors: [t`Could not connect to server, please try again later :(`],
};
}
}
/*
* block / unblock user
* @param userId id of user to block
* @param block true if block, false if unblock
* @return error string or null if successful
*/
export async function requestBlock(userId, block) {
const res = await makeAPIPOSTRequest(
'/api/block',
{ userId, block },
);
if (res.errors) {
return res.errors[0];
}
if (res.status === 'ok') {
return null;
}
return t`Unknown Error`;
}
/*
* set / unset profile as private
* @param priv
* @return error string or null if successful
*/
export async function requestPrivatize(priv) {
const res = await makeAPIPOSTRequest(
'/api/privatize',
{ priv },
);
if (res.errors) {
return res.errors[0];
}
if (res.status === 'ok') {
return null;
}
return t`Unknown Error`;
}
/*
* start new DM channel with user
* @param query Object with either userId or userName: string
* @return channel Array on success, error string if not
*/
export async function requestStartDm(query) {
const res = await makeAPIPOSTRequest(
'/api/startdm',
query,
);
if (res.errors) {
return res.errors[0];
}
if (res.channel) {
return res.channel;
}
return t`Unknown Error`;
}
/*
* set receiving of all DMs on/off
* @param block true if blocking all dms, false if unblocking
* @return error string or null if successful
*/
export async function requestBlockDm(block) {
const res = await makeAPIPOSTRequest(
'/api/blockdm',
{ block },
);
if (res.errors) {
return res.errors[0];
}
if (res.status === 'ok') {
return null;
}
return t`Unknown Error`;
}
/*
* leaving Chat Channel (i.e. DM channel)
* @param channelId integer id of channel
* @return error string or null if successful
*/
export async function requestLeaveChan(channelId) {
const res = await makeAPIPOSTRequest(
'/api/leavechan',
{ channelId },
);
if (res.errors) {
return res.errors[0];
}
if (res.status === 'ok') {
return null;
}
return t`Unknown Error`;
}
export async function requestSolveCaptcha(text, captchaid) {
const res = await makeAPIPOSTRequest(
'/api/captcha',
{ text, id: captchaid },
);
if (!res.errors && !res.success) {
return {
errors: [t`Server answered with gibberish :(`],
};
}
return res;
}
export async function requestHistoricalTimes(day, canvasId) {
try {
const date = dateToString(day);
// Not going over shard url
const url = `/history?day=${date}&id=${canvasId}`;
const response = await fetchWithTimeout(url, {
credentials: 'omit',
timeout: 45000,
});
if (response.status !== 200) {
return [];
}
const times = await response.json();
const parsedTimes = times
.map((a) => `${a.substring(0, 2)}:${a.substring(2)}`);
return [
'00:00',
...parsedTimes,
];
} catch {
return [];
}
}
export async function requestChatMessages(cid) {
const response = await fetch(
`${shardOrigin}/api/chathistory?cid=${cid}&limit=50`,
{ credentials: 'include' },
);
// timeout in order to not spam api requests and get rate limited
if (response.ok) {
const { history } = await response.json();
return history;
}
return null;
}
export function requestPasswordChange(newPassword, password) {
return makeAPIPOSTRequest(
'/api/auth/change_passwd',
{ password, newPassword },
);
}
export async function requestResendVerify() {
return makeAPIGETRequest(
'/api/auth/resend_verify',
);
}
export async function requestLogOut() {
const ret = makeAPIGETRequest(
'/api/auth/logout',
);
return !ret.errors;
}
export function requestNameChange(name) {
return makeAPIPOSTRequest(
'/api/auth/change_name',
{ name },
);
}
export function requestMailChange(email, password) {
return makeAPIPOSTRequest(
'/api/auth/change_mail',
{ email, password },
);
}
export function requestLogin(nameoremail, password) {
return makeAPIPOSTRequest(
'/api/auth/local',
{ nameoremail, password },
);
}
export function requestRegistration(name, email, password, captcha, captchaid) {
return makeAPIPOSTRequest(
'/api/auth/register',
{
name, email, password, captcha, captchaid,
},
);
}
export function requestNewPassword(email) {
return makeAPIPOSTRequest(
'/api/auth/restore_password',
{ email },
);
}
export function requestDeleteAccount(password) {
return makeAPIPOSTRequest(
'/api/auth/delete_account',
{ password },
);
}
export function requestRankings() {
return makeAPIGETRequest(
'/ranking',
false,
);
}
export function requestBanInfo() {
return makeAPIGETRequest(
'/api/baninfo',
);
}
export async function requestMe() {
if (window.me) {
// api/me gets pre-fetched by embedded script in html
const response = await window.me;
delete window.me;
return parseAPIresponse(response);
}
return makeAPIGETRequest(
'/api/me',
);
}
export function requestIID() {
return makeAPIGETRequest(
'/api/getiid',
);
}
export function requestBanMe(code) {
return makeAPIPOSTRequest(
'/api/banme',
{ code },
);
}