* #10 add localization * #10 revert locale from dev page * #10 add more examples * #10 localize auth panel * #10 localize some comment message * #10 localize some comment form message * #10 translate vote messages * #10 translate comment message * #10 use messages value for translate reference * #10 fix compile issue * #10 use messages value for translate reference * #10 use messages value for translate reference * #10 use messages value for translate reference * #10 use messages value for translate reference * #10 use messages value for translate reference * #10 translate comment message * #10 translate comment form * #10 translate comment form * #10 translate root component * #10 translate user info * #10 translate settings * #10 translate settings * #10 translate toolbar * #10 translate errors messages * #10 sort dict * #10 fix after rebase * #10 localize anonymousLoginForm * #10 localize emailLoginForm * #10 update size-limit * #10 localize subscribe by rss * #10 localize subscribe by email * #10 add de locale * #10 increase limit size * #10 auto generate loadLocale * #10 add some documentation * #10 fix error messages * fix typo * don't bundle en locale * fix message id * change size limits * Update extract message pattern Co-Authored-By: Pavel Mineev <pavel@mineev.me> Co-authored-by: Pavel Mineev <pavel@mineev.me>
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import { BASE_URL, API_BASE } from './constants';
|
|
import { siteId } from './settings';
|
|
import { StaticStore } from './static_store';
|
|
import { getCookie } from './cookies';
|
|
import { httpErrorMap, isFailedFetch, httpMessages } from '@app/utils/errorUtils';
|
|
|
|
export type FetcherMethod = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'head';
|
|
const methods: FetcherMethod[] = ['get', 'post', 'put', 'patch', 'delete', 'head'];
|
|
|
|
interface FetcherInitBase {
|
|
url: string;
|
|
overriddenApiBase?: string;
|
|
withCredentials?: boolean;
|
|
/** whether log error message to console */
|
|
logError?: boolean;
|
|
}
|
|
|
|
interface FetcherInitJSON extends FetcherInitBase {
|
|
contentType?: 'application/json';
|
|
body?: string | object | Blob | ArrayBuffer;
|
|
}
|
|
|
|
interface FetcherInitMultipart extends FetcherInitBase {
|
|
contentType: 'multipart/form-data';
|
|
body: FormData;
|
|
}
|
|
|
|
type FetcherInit = string | FetcherInitJSON | FetcherInitMultipart;
|
|
|
|
type FetcherObject = { [K in FetcherMethod]: <T = unknown>(data: FetcherInit) => Promise<T> };
|
|
|
|
const fetcher = methods.reduce<Partial<FetcherObject>>((acc, method) => {
|
|
acc[method] = <T = unknown>(data: FetcherInit): Promise<T> => {
|
|
const {
|
|
url,
|
|
body = undefined,
|
|
withCredentials = false,
|
|
overriddenApiBase = API_BASE,
|
|
contentType = 'application/json',
|
|
logError = true,
|
|
} = typeof data === 'string' ? { url: data } : data;
|
|
const basename = `${BASE_URL}${overriddenApiBase}`;
|
|
|
|
const headers = new Headers({
|
|
Accept: 'application/json',
|
|
'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') || '',
|
|
});
|
|
|
|
if (contentType !== 'multipart/form-data') {
|
|
headers.append('Content-Type', contentType);
|
|
}
|
|
|
|
let rurl = `${basename}${url}`;
|
|
|
|
const parameters: RequestInit = {
|
|
method,
|
|
headers,
|
|
mode: 'cors',
|
|
credentials: withCredentials ? 'include' : 'omit',
|
|
};
|
|
|
|
if (body) {
|
|
if (contentType === 'multipart/form-data') {
|
|
parameters.body = body as FormData;
|
|
} else if (typeof body === 'object' && !(body instanceof Blob) && !(body instanceof ArrayBuffer)) {
|
|
parameters.body = JSON.stringify(body);
|
|
} else {
|
|
parameters.body = body;
|
|
}
|
|
}
|
|
|
|
if (siteId && method !== 'post' && !rurl.includes('?site=') && !rurl.includes('&site=')) {
|
|
rurl += (rurl.includes('?') ? '&' : '?') + `site=${siteId}`;
|
|
}
|
|
|
|
return fetch(rurl, parameters)
|
|
.then(res => {
|
|
const date = (res.headers.has('date') && res.headers.get('date')) || '';
|
|
const timestamp = isNaN(Date.parse(date)) ? 0 : Date.parse(date);
|
|
const timeDiff = (new Date().getTime() - timestamp) / 1000;
|
|
StaticStore.serverClientTimeDiff = timeDiff;
|
|
|
|
if (res.status >= 400) {
|
|
if (httpErrorMap.has(res.status)) {
|
|
const descriptor = httpErrorMap.get(res.status) || httpMessages.unexpectedError;
|
|
throw {
|
|
code: res.status,
|
|
error: descriptor.defaultMessage,
|
|
};
|
|
}
|
|
return res.text().then(text => {
|
|
let err;
|
|
try {
|
|
err = JSON.parse(text);
|
|
} catch (e) {
|
|
if (logError) {
|
|
// eslint-disable-next-line no-console
|
|
console.error(err);
|
|
}
|
|
throw {
|
|
code: 0,
|
|
error: httpMessages.unexpectedError.defaultMessage,
|
|
};
|
|
}
|
|
throw err;
|
|
});
|
|
}
|
|
|
|
if (res.headers.has('Content-Type') && res.headers.get('Content-Type')!.indexOf('application/json') === 0) {
|
|
return res.json();
|
|
}
|
|
|
|
return res.text();
|
|
})
|
|
.catch(e => {
|
|
if (isFailedFetch(e)) {
|
|
throw {
|
|
code: -2,
|
|
error: e.message,
|
|
};
|
|
}
|
|
throw e;
|
|
});
|
|
};
|
|
return acc;
|
|
}, {}) as FetcherObject;
|
|
|
|
export default fetcher;
|