2021-03-16 16:46:23 +08:00
|
|
|
import { Notification } from 'Common/Enums';
|
2021-03-16 23:49:14 +08:00
|
|
|
import { isArray, pInt, pString } from 'Common/Utils';
|
2020-09-15 01:40:56 +08:00
|
|
|
import { serverRequest } from 'Common/Links';
|
2021-12-15 04:12:38 +08:00
|
|
|
import { getNotification } from 'Common/Translator';
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-12-15 04:12:38 +08:00
|
|
|
let iJsonErrorCount = 0;
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2022-10-10 19:52:56 +08:00
|
|
|
const getURL = (add = '') => serverRequest('Json') + pString(add),
|
2020-09-15 01:40:56 +08:00
|
|
|
|
|
|
|
checkResponseError = data => {
|
|
|
|
const err = data ? data.ErrorCode : null;
|
2021-12-15 04:12:38 +08:00
|
|
|
if (Notification.InvalidToken === err) {
|
|
|
|
alert(getNotification(err));
|
2020-09-15 01:40:56 +08:00
|
|
|
rl.logoutReload();
|
2021-12-15 04:12:38 +08:00
|
|
|
} else if ([
|
|
|
|
Notification.AuthError,
|
|
|
|
Notification.ConnectionError,
|
|
|
|
Notification.DomainNotAllowed,
|
|
|
|
Notification.AccountNotAllowed,
|
|
|
|
Notification.MailServerError,
|
|
|
|
Notification.UnknownNotification,
|
|
|
|
Notification.UnknownError
|
|
|
|
].includes(err)
|
|
|
|
) {
|
|
|
|
if (7 < ++iJsonErrorCount) {
|
2021-04-29 03:33:03 +08:00
|
|
|
rl.logoutReload();
|
2021-01-05 20:58:50 +08:00
|
|
|
}
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
2021-01-05 20:58:50 +08:00
|
|
|
},
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
oRequests = {},
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2022-08-02 04:45:17 +08:00
|
|
|
abort = (sAction, sReason, bClearOnly) => {
|
2021-01-05 20:58:50 +08:00
|
|
|
if (oRequests[sAction]) {
|
2022-11-08 17:53:14 +08:00
|
|
|
bClearOnly || oRequests[sAction].abort(sReason || 'AbortError');
|
2021-01-05 20:58:50 +08:00
|
|
|
oRequests[sAction] = null;
|
|
|
|
delete oRequests[sAction];
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-11-08 17:53:14 +08:00
|
|
|
fetchJSON = (action, sUrl, params, timeout, jsonCallback) => {
|
|
|
|
if (params) {
|
|
|
|
if (params instanceof FormData) {
|
|
|
|
params.set('Action', action);
|
|
|
|
} else {
|
|
|
|
params.Action = action;
|
|
|
|
}
|
2021-07-23 17:40:03 +08:00
|
|
|
}
|
2022-10-05 17:39:25 +08:00
|
|
|
// Don't abort, read https://github.com/the-djmaze/snappymail/issues/487
|
2022-09-02 15:57:30 +08:00
|
|
|
// abort(action);
|
2022-08-02 17:01:19 +08:00
|
|
|
const controller = new AbortController(),
|
|
|
|
signal = controller.signal;
|
|
|
|
oRequests[action] = controller;
|
|
|
|
// Currently there is no way to combine multiple signals, so AbortSignal.timeout() not possible
|
|
|
|
timeout && setTimeout(() => abort(action, 'TimeoutError'), timeout);
|
2022-11-08 17:53:14 +08:00
|
|
|
return rl.fetchJSON(sUrl, {signal: signal}, params).then(jsonCallback).catch(err => {
|
2022-08-02 17:01:19 +08:00
|
|
|
err.aborted = signal.aborted;
|
|
|
|
err.reason = signal.reason;
|
2022-08-02 04:45:17 +08:00
|
|
|
return Promise.reject(err);
|
|
|
|
});
|
2021-01-05 20:58:50 +08:00
|
|
|
};
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-08-25 18:00:50 +08:00
|
|
|
class FetchError extends Error
|
|
|
|
{
|
|
|
|
constructor(code, message) {
|
|
|
|
super(message);
|
|
|
|
this.code = code || Notification.JsonFalse;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
export class AbstractFetchRemote
|
2020-09-15 01:40:56 +08:00
|
|
|
{
|
2022-08-02 17:01:19 +08:00
|
|
|
abort(sAction) {
|
|
|
|
abort(sAction);
|
2020-09-15 01:40:56 +08:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-11-01 21:57:58 +08:00
|
|
|
/**
|
|
|
|
* Allows quicker visual responses to the user.
|
|
|
|
* Can be used to stream lines of json encoded data, but does not work on all servers.
|
|
|
|
* Apache needs 'flushpackets' like in <Proxy "fcgi://...." flushpackets=on></Proxy>
|
|
|
|
*/
|
2022-03-24 19:01:41 +08:00
|
|
|
streamPerLine(fCallback, sGetAdd, postData) {
|
|
|
|
rl.fetch(getURL(sGetAdd), {}, postData)
|
2021-11-01 21:57:58 +08:00
|
|
|
.then(response => response.body)
|
|
|
|
.then(body => {
|
|
|
|
let buffer = '';
|
2022-03-24 19:01:41 +08:00
|
|
|
const
|
|
|
|
// Firefox TextDecoderStream is not defined
|
|
|
|
// reader = body.pipeThrough(new TextDecoderStream()).getReader();
|
|
|
|
reader = body.getReader(),
|
|
|
|
re = /\r\n|\n|\r/gm,
|
|
|
|
utf8decoder = new TextDecoder(),
|
|
|
|
processText = ({ done, value }) => {
|
|
|
|
buffer += value ? utf8decoder.decode(value, {stream: true}) : '';
|
|
|
|
for (;;) {
|
|
|
|
let result = re.exec(buffer);
|
|
|
|
if (!result) {
|
|
|
|
if (done) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
reader.read().then(processText);
|
|
|
|
return;
|
2021-11-01 21:57:58 +08:00
|
|
|
}
|
2022-03-24 19:01:41 +08:00
|
|
|
fCallback(buffer.slice(0, result.index));
|
|
|
|
buffer = buffer.slice(result.index + 1);
|
|
|
|
re.lastIndex = 0;
|
2021-11-01 21:57:58 +08:00
|
|
|
}
|
2022-10-10 19:52:56 +08:00
|
|
|
// last line didn't end in a newline char
|
|
|
|
buffer.length && fCallback(buffer);
|
2022-03-24 19:01:41 +08:00
|
|
|
};
|
2021-11-01 21:57:58 +08:00
|
|
|
reader.read().then(processText);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-09-15 01:40:56 +08:00
|
|
|
/**
|
2020-09-15 17:51:07 +08:00
|
|
|
* @param {?Function} fCallback
|
|
|
|
* @param {string} sAction
|
|
|
|
* @param {Object=} oParameters
|
|
|
|
* @param {?number=} iTimeout
|
2020-09-15 01:40:56 +08:00
|
|
|
* @param {string=} sGetAdd = ''
|
|
|
|
*/
|
2022-11-08 17:53:14 +08:00
|
|
|
request(sAction, fCallback, params, iTimeout, sGetAdd) {
|
2020-09-15 01:40:56 +08:00
|
|
|
params = params || {};
|
2020-09-15 17:51:07 +08:00
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
const start = Date.now();
|
2020-09-15 17:51:07 +08:00
|
|
|
|
2022-11-08 17:53:14 +08:00
|
|
|
fetchJSON(sAction, getURL(sGetAdd),
|
|
|
|
sGetAdd ? null : (params || {}),
|
2021-01-05 20:58:50 +08:00
|
|
|
undefined === iTimeout ? 30000 : pInt(iTimeout),
|
|
|
|
data => {
|
|
|
|
let cached = false;
|
2022-09-02 17:52:07 +08:00
|
|
|
if (data?.Time) {
|
2021-04-28 21:56:31 +08:00
|
|
|
cached = pInt(data.Time) > Date.now() - start;
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
|
2021-03-16 16:46:23 +08:00
|
|
|
let iError = 0;
|
2021-01-05 20:58:50 +08:00
|
|
|
if (sAction && oRequests[sAction]) {
|
2022-08-02 17:01:19 +08:00
|
|
|
abort(sAction, 0, 1);
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
|
2021-03-16 16:46:23 +08:00
|
|
|
if (!iError && data) {
|
2021-03-18 19:33:13 +08:00
|
|
|
/*
|
|
|
|
if (sAction !== data.Action) {
|
|
|
|
console.log(sAction + ' !== ' + data.Action);
|
|
|
|
}
|
|
|
|
*/
|
2021-03-16 16:46:23 +08:00
|
|
|
if (data.Result) {
|
2021-12-15 04:12:38 +08:00
|
|
|
iJsonErrorCount = 0;
|
2021-03-16 16:46:23 +08:00
|
|
|
} else {
|
|
|
|
checkResponseError(data);
|
2021-03-18 19:33:13 +08:00
|
|
|
iError = data.ErrorCode || Notification.UnknownError
|
2021-01-05 20:58:50 +08:00
|
|
|
}
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
2021-03-16 16:46:23 +08:00
|
|
|
|
2022-09-02 18:11:47 +08:00
|
|
|
fCallback && fCallback(
|
2021-03-16 16:46:23 +08:00
|
|
|
iError,
|
|
|
|
data,
|
|
|
|
cached,
|
|
|
|
sAction,
|
|
|
|
params
|
|
|
|
);
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
2021-02-25 17:12:48 +08:00
|
|
|
)
|
|
|
|
.catch(err => {
|
2022-08-02 04:45:17 +08:00
|
|
|
console.error({fetchError:err});
|
2022-09-02 18:11:47 +08:00
|
|
|
fCallback && fCallback(
|
2022-08-02 17:01:19 +08:00
|
|
|
'TimeoutError' == err.reason ? 3 : (err.name == 'AbortError' ? 2 : 1),
|
|
|
|
err
|
|
|
|
);
|
2021-02-25 17:12:48 +08:00
|
|
|
});
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {?Function} fCallback
|
|
|
|
*/
|
|
|
|
getPublicKey(fCallback) {
|
2021-12-03 06:15:24 +08:00
|
|
|
this.request('GetPublicKey', fCallback);
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
setTrigger(trigger, value) {
|
|
|
|
if (trigger) {
|
|
|
|
value = !!value;
|
2021-03-16 23:49:14 +08:00
|
|
|
(isArray(trigger) ? trigger : [trigger]).forEach(fTrigger => {
|
2022-09-02 17:52:07 +08:00
|
|
|
fTrigger?.(value);
|
2020-09-15 01:40:56 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-08 17:53:14 +08:00
|
|
|
get(action, url) {
|
|
|
|
return fetchJSON(action, url);
|
|
|
|
}
|
|
|
|
|
2021-12-03 06:15:24 +08:00
|
|
|
post(action, fTrigger, params, timeOut) {
|
2020-09-15 01:40:56 +08:00
|
|
|
this.setTrigger(fTrigger, true);
|
2022-11-08 17:53:14 +08:00
|
|
|
return fetchJSON(action, getURL(), params || {}, pInt(timeOut, 30000),
|
2021-01-05 20:58:50 +08:00
|
|
|
data => {
|
2022-08-02 17:01:19 +08:00
|
|
|
abort(action, 0, 1);
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
if (!data) {
|
2021-08-25 18:34:19 +08:00
|
|
|
return Promise.reject(new FetchError(Notification.JsonParse));
|
2021-01-05 20:58:50 +08:00
|
|
|
}
|
2020-09-15 01:40:56 +08:00
|
|
|
/*
|
2021-01-05 20:58:50 +08:00
|
|
|
let isCached = false, type = '';
|
2022-09-02 17:52:07 +08:00
|
|
|
if (data?.Time) {
|
2021-01-05 20:58:50 +08:00
|
|
|
isCached = pInt(data.Time) > microtime() - start;
|
|
|
|
}
|
|
|
|
// backward capability
|
|
|
|
switch (true) {
|
2022-09-02 17:52:07 +08:00
|
|
|
case 'success' === textStatus && data?.Result && action === data.Action:
|
2021-03-16 16:46:23 +08:00
|
|
|
type = AbstractFetchRemote.SUCCESS;
|
2021-01-05 20:58:50 +08:00
|
|
|
break;
|
|
|
|
case 'abort' === textStatus && (!data || !data.__aborted__):
|
2021-03-16 16:46:23 +08:00
|
|
|
type = AbstractFetchRemote.ABORT;
|
2021-01-05 20:58:50 +08:00
|
|
|
break;
|
|
|
|
default:
|
2021-03-16 16:46:23 +08:00
|
|
|
type = AbstractFetchRemote.ERROR;
|
2021-01-05 20:58:50 +08:00
|
|
|
break;
|
|
|
|
}
|
2020-09-15 01:40:56 +08:00
|
|
|
*/
|
2021-01-05 20:58:50 +08:00
|
|
|
this.setTrigger(fTrigger, false);
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
if (!data.Result || action !== data.Action) {
|
|
|
|
checkResponseError(data);
|
2021-08-25 18:00:50 +08:00
|
|
|
return Promise.reject(new FetchError(
|
|
|
|
data ? data.ErrorCode : 0,
|
|
|
|
data ? (data.ErrorMessageAdditional || data.ErrorMessage) : ''
|
|
|
|
));
|
2021-01-05 20:58:50 +08:00
|
|
|
}
|
2020-09-15 01:40:56 +08:00
|
|
|
|
2021-01-05 20:58:50 +08:00
|
|
|
return data;
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
2021-01-05 20:58:50 +08:00
|
|
|
);
|
2020-09-15 01:40:56 +08:00
|
|
|
}
|
|
|
|
}
|
2021-03-16 16:46:23 +08:00
|
|
|
|
|
|
|
Object.assign(AbstractFetchRemote.prototype, {
|
|
|
|
SUCCESS : 0,
|
|
|
|
ERROR : 1,
|
|
|
|
ABORT : 2
|
|
|
|
});
|