Cap simultaneous API queries to 2 and prevent multiple requests trying to refresh the API ticket at the same time
This commit is contained in:
parent
f862d53243
commit
168d659785
|
@ -124,16 +124,14 @@ import {InlineDisplayMode} from '../interfaces';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
core.connection.onEvent('closed', (isReconnect) => {
|
core.connection.onEvent('closed', (isReconnect) => {
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'connection.closed',
|
||||||
'connection.closed',
|
{
|
||||||
{
|
character: core.characters.ownCharacter?.name,
|
||||||
character: core.characters.ownCharacter?.name,
|
error: this.error,
|
||||||
error: this.error,
|
isReconnect
|
||||||
isReconnect
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isReconnect) (<Modal>this.$refs['reconnecting']).show(true);
|
if(isReconnect) (<Modal>this.$refs['reconnecting']).show(true);
|
||||||
if(this.connected) core.notifications.playSound('logout');
|
if(this.connected) core.notifications.playSound('logout');
|
||||||
|
@ -148,14 +146,12 @@ import {InlineDisplayMode} from '../interfaces';
|
||||||
core.connection.onEvent('connecting', async() => {
|
core.connection.onEvent('connecting', async() => {
|
||||||
this.connecting = true;
|
this.connecting = true;
|
||||||
|
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'connection.connecting',
|
||||||
'connection.connecting',
|
{
|
||||||
{
|
character: core.characters.ownCharacter?.name
|
||||||
character: core.characters.ownCharacter?.name
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
profileApiInit({
|
profileApiInit({
|
||||||
defaultCharacter: this.defaultCharacter, animateEicons: core.state.settings.animatedEicons, fuzzyDates: true,
|
defaultCharacter: this.defaultCharacter, animateEicons: core.state.settings.animatedEicons, fuzzyDates: true,
|
||||||
|
@ -164,14 +160,12 @@ import {InlineDisplayMode} from '../interfaces';
|
||||||
if(core.state.settings.notifications) await core.notifications.requestPermission();
|
if(core.state.settings.notifications) await core.notifications.requestPermission();
|
||||||
});
|
});
|
||||||
core.connection.onEvent('connected', () => {
|
core.connection.onEvent('connected', () => {
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'connection.connected',
|
||||||
'connection.connected',
|
{
|
||||||
{
|
character: core.characters.ownCharacter?.name
|
||||||
character: core.characters.ownCharacter?.name
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
(<Modal>this.$refs['reconnecting']).hide();
|
(<Modal>this.$refs['reconnecting']).hide();
|
||||||
this.error = '';
|
this.error = '';
|
||||||
|
@ -184,15 +178,13 @@ import {InlineDisplayMode} from '../interfaces';
|
||||||
document.title = (hasNew ? '💬 ' : '') + l(core.connection.isOpen ? 'title.connected' : 'title', core.connection.character);
|
document.title = (hasNew ? '💬 ' : '') + l(core.connection.isOpen ? 'title.connected' : 'title', core.connection.character);
|
||||||
});
|
});
|
||||||
core.connection.onError((e) => {
|
core.connection.onError((e) => {
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'connection.error',
|
||||||
'connection.error',
|
{
|
||||||
{
|
error: errorToString(e),
|
||||||
error: errorToString(e),
|
character: core.characters.ownCharacter?.name
|
||||||
character: core.characters.ownCharacter?.name
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if((<Error & {request?: object}>e).request !== undefined) {//catch axios network errors
|
if((<Error & {request?: object}>e).request !== undefined) {//catch axios network errors
|
||||||
this.error = l('login.connectError', errorToString(e));
|
this.error = l('login.connectError', errorToString(e));
|
||||||
|
|
|
@ -3,9 +3,15 @@ import { ipcRenderer, IpcRendererEvent } from 'electron';
|
||||||
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
||||||
import core from '../core';
|
import core from '../core';
|
||||||
|
|
||||||
|
interface PendingAd {
|
||||||
|
resolve(): void,
|
||||||
|
reject(err: Error): void,
|
||||||
|
from: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export class AdCoordinatorGuest {
|
export class AdCoordinatorGuest {
|
||||||
protected pendingAds: Record<string, any> = {};
|
protected pendingAds: Record<string, PendingAd> = {};
|
||||||
protected adCounter = 0;
|
protected adCounter = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -26,7 +32,7 @@ export class AdCoordinatorGuest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
requestTurnToPostAd(): Promise<void> {
|
async requestTurnToPostAd(): Promise<void> {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
(resolve, reject) => {
|
(resolve, reject) => {
|
||||||
const adId = `${Math.round(Math.random() * 1000000)}-${this.adCounter++}-${Date.now()}`;
|
const adId = `${Math.round(Math.random() * 1000000)}-${this.adCounter++}-${Date.now()}`;
|
||||||
|
@ -42,7 +48,7 @@ export class AdCoordinatorGuest {
|
||||||
|
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
_.each(this.pendingAds, (pa) => (pa.reject()));
|
_.each(this.pendingAds, (pa) => (pa.reject(new Error('Pending ad cleared'))));
|
||||||
|
|
||||||
console.debug('adid.clear', _.keys(this.pendingAds), core.characters.ownCharacter?.name);
|
console.debug('adid.clear', _.keys(this.pendingAds), core.characters.ownCharacter?.name);
|
||||||
|
|
||||||
|
|
|
@ -69,19 +69,17 @@ export class AdManager {
|
||||||
|
|
||||||
const delayTime = Date.now();
|
const delayTime = Date.now();
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'adManager.sendAdToChannel',
|
||||||
'adManager.sendAdToChannel',
|
{
|
||||||
{
|
character: core.characters.ownCharacter?.name,
|
||||||
character: core.characters.ownCharacter?.name,
|
channel: conv.channel.name,
|
||||||
channel: conv.channel.name,
|
throatDelta: throatTime - initTime,
|
||||||
throatDelta: throatTime - initTime,
|
delayDelta: delayTime - throatTime,
|
||||||
delayDelta: delayTime - throatTime,
|
totalWait: delayTime - initTime,
|
||||||
totalWait: delayTime - initTime,
|
msg
|
||||||
msg
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await conv.sendAd(msg);
|
await conv.sendAd(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,19 +415,17 @@ class ChannelConversation extends Conversation implements Interfaces.ChannelConv
|
||||||
core.connection.send('LRP', {channel: this.channel.id, message: text});
|
core.connection.send('LRP', {channel: this.channel.id, message: text});
|
||||||
core.cache.markLastPostTime();
|
core.cache.markLastPostTime();
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
log.debug(
|
'conversation.sendAd',
|
||||||
'conversation.sendAd',
|
{
|
||||||
{
|
character: core.characters.ownCharacter?.name,
|
||||||
character: core.characters.ownCharacter?.name,
|
channel: this.channel.name,
|
||||||
channel: this.channel.name,
|
throatDelta: throatTime - initTime,
|
||||||
throatDelta: throatTime - initTime,
|
delayDelta: delayTime - throatTime,
|
||||||
delayDelta: delayTime - throatTime,
|
totalWait: delayTime - initTime,
|
||||||
totalWait: delayTime - initTime,
|
text
|
||||||
text
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.addMessage(
|
await this.addMessage(
|
||||||
createMessage(MessageType.Ad, core.characters.ownCharacter, text, new Date())
|
createMessage(MessageType.Ad, core.characters.ownCharacter, text, new Date())
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {Connection as Interfaces, WebSocketConnection} from './interfaces';
|
||||||
import ReadyState = WebSocketConnection.ReadyState;
|
import ReadyState = WebSocketConnection.ReadyState;
|
||||||
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
||||||
import core from '../chat/core';
|
import core from '../chat/core';
|
||||||
|
import throat from 'throat';
|
||||||
|
|
||||||
const fatalErrors = [2, 3, 4, 9, 30, 31, 33, 39, 40, 62, -4];
|
const fatalErrors = [2, 3, 4, 9, 30, 31, 33, 39, 40, 62, -4];
|
||||||
const dieErrors = [9, 30, 31, 39, 40];
|
const dieErrors = [9, 30, 31, 39, 40];
|
||||||
|
@ -11,12 +12,12 @@ const dieErrors = [9, 30, 31, 39, 40];
|
||||||
let lastFetch = Date.now();
|
let lastFetch = Date.now();
|
||||||
let lastApiTicketFetch = Date.now();
|
let lastApiTicketFetch = Date.now();
|
||||||
|
|
||||||
|
const queryApiThroat = throat(2);
|
||||||
|
const queryTicketThroat = throat(1);
|
||||||
|
|
||||||
|
|
||||||
async function queryApi(this: void, endpoint: string, data: object): Promise<AxiosResponse> {
|
async function queryApi(this: void, endpoint: string, data: object): Promise<AxiosResponse> {
|
||||||
if (false) {
|
lastFetch = Date.now();
|
||||||
console.log(`https://www.f-list.net/json/api/${endpoint}, gap: ${Date.now() - lastFetch}ms`);
|
|
||||||
lastFetch = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Axios.post(`https://www.f-list.net/json/api/${endpoint}`, qs.stringify(data));
|
return Axios.post(`https://www.f-list.net/json/api/${endpoint}`, qs.stringify(data));
|
||||||
}
|
}
|
||||||
|
@ -115,13 +116,59 @@ export default class Connection implements Interfaces.Connection {
|
||||||
if(!keepState) this.character = '';
|
if(!keepState) this.character = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get isOpen(): boolean {
|
get isOpen(): boolean {
|
||||||
return this.socket !== undefined && this.socket.readyState === ReadyState.OPEN;
|
return this.socket !== undefined && this.socket.readyState === ReadyState.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async queryApi<T = object>(endpoint: string, data?: {account?: string, ticket?: string}): Promise<T> {
|
async queryApi<T = object>(endpoint: string, data?: {account?: string, ticket?: string}): Promise<T> {
|
||||||
|
return queryApiThroat(async() => this.queryApiExec<T>(endpoint, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async refreshTicket(oldTicket: string): Promise<string> {
|
||||||
|
if (this.ticket !== oldTicket) {
|
||||||
|
log.debug(
|
||||||
|
'api.ticket.renew.resolve.cache',
|
||||||
|
{
|
||||||
|
character: core.characters.ownCharacter?.name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.ticketProvider) {
|
||||||
|
throw new Error('No credentials set!');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ticket = await queryTicketThroat(async() => this.ticketProvider!());
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
'api.ticket.renew.resolve.refresh',
|
||||||
|
{
|
||||||
|
character: core.characters.ownCharacter?.name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected async queryApiExec<T = object>(endpoint: string, data?: {account?: string, ticket?: string}): Promise<T> {
|
||||||
if(!this.ticketProvider) throw new Error('No credentials set!');
|
if(!this.ticketProvider) throw new Error('No credentials set!');
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
'api.query.start',
|
||||||
|
{
|
||||||
|
endpoint,
|
||||||
|
character: core.characters.ownCharacter?.name,
|
||||||
|
deltaToLastApiCall: Date.now() - lastFetch,
|
||||||
|
deltaToLastApiTicket: Date.now() - lastApiTicketFetch
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if(data === undefined) data = {};
|
if(data === undefined) data = {};
|
||||||
|
|
||||||
data.account = this.account;
|
data.account = this.account;
|
||||||
|
@ -140,13 +187,13 @@ export default class Connection implements Interfaces.Connection {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
data.ticket = this.ticket = await this.ticketProvider();
|
data.ticket = await this.refreshTicket(data.ticket);
|
||||||
res = <T & {error: string}>(await queryApi(endpoint, data)).data;
|
res = <T & {error: string}>(await queryApi(endpoint, data)).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res.error !== '') {
|
if(res.error !== '') {
|
||||||
log.debug(
|
log.debug(
|
||||||
'error.api.query',
|
'api.query.error',
|
||||||
{
|
{
|
||||||
error: res.error,
|
error: res.error,
|
||||||
endpoint,
|
endpoint,
|
||||||
|
@ -160,6 +207,17 @@ export default class Connection implements Interfaces.Connection {
|
||||||
(<Error & {request: true}>error).request = true;
|
(<Error & {request: true}>error).request = true;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
'api.query.success',
|
||||||
|
{
|
||||||
|
endpoint,
|
||||||
|
character: core.characters.ownCharacter?.name,
|
||||||
|
deltaToLastApiCall: Date.now() - lastFetch,
|
||||||
|
deltaToLastApiTicket: Date.now() - lastApiTicketFetch
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,43 +290,48 @@ export default class Connection implements Interfaces.Connection {
|
||||||
|
|
||||||
private async getTicket(password: string): Promise<string> {
|
private async getTicket(password: string): Promise<string> {
|
||||||
console.log('Acquiring new API ticket');
|
console.log('Acquiring new API ticket');
|
||||||
|
const oldLastApiTicketFetch = lastApiTicketFetch;
|
||||||
|
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
log.debug(
|
||||||
console.log(`https://www.f-list.net/json/getApiTicket.php, gap: ${Date.now() - lastApiTicketFetch}ms`);
|
'api.getTicket.start',
|
||||||
|
{
|
||||||
|
character: core.characters.ownCharacter?.name,
|
||||||
|
deltaToLastApiCall: Date.now() - lastFetch,
|
||||||
|
deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
log.debug(
|
lastApiTicketFetch = Date.now();
|
||||||
'api.getTicket',
|
|
||||||
{
|
|
||||||
character: core.characters.ownCharacter?.name,
|
|
||||||
deltaToLastApiCall: Date.now() - lastFetch,
|
|
||||||
deltaToLastApiTicket: Date.now() - lastApiTicketFetch
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
lastApiTicketFetch = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = <{ticket?: string, error: string}>(await Axios.post('https://www.f-list.net/json/getApiTicket.php', qs.stringify(
|
const data = <{ticket?: string, error: string}>(await Axios.post('https://www.f-list.net/json/getApiTicket.php', qs.stringify(
|
||||||
{account: this.account, password, no_friends: true, no_bookmarks: true, no_characters: true}))).data;
|
{account: this.account, password, no_friends: true, no_bookmarks: true, no_characters: true}))).data;
|
||||||
|
|
||||||
if(data.ticket !== undefined) return data.ticket;
|
if(data.ticket !== undefined) {
|
||||||
|
log.debug(
|
||||||
if(process.env.NODE_ENV !== 'production') {
|
'api.getTicket.success',
|
||||||
console.error('API Ticket Error', data.error);
|
|
||||||
|
|
||||||
log.error(
|
|
||||||
'error.api.getTicket',
|
|
||||||
{
|
{
|
||||||
character: core.characters.ownCharacter.name,
|
character: core.characters.ownCharacter?.name,
|
||||||
error: data.error,
|
|
||||||
deltaToLastApiCall: Date.now() - lastFetch,
|
deltaToLastApiCall: Date.now() - lastFetch,
|
||||||
deltaToLastApiTicket: Date.now() - lastApiTicketFetch
|
deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
lastApiTicketFetch = Date.now();
|
return data.ticket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.error('API Ticket Error', data.error);
|
||||||
|
|
||||||
|
log.error(
|
||||||
|
'error.api.getTicket',
|
||||||
|
{
|
||||||
|
character: core.characters.ownCharacter.name,
|
||||||
|
error: data.error,
|
||||||
|
deltaToLastApiCall: Date.now() - lastFetch,
|
||||||
|
deltaToLastApiTicket: Date.now() - oldLastApiTicketFetch
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
throw new Error(data.error);
|
throw new Error(data.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,10 +295,14 @@
|
||||||
|
|
||||||
|
|
||||||
async updateMeta(name: string): Promise<void> {
|
async updateMeta(name: string): Promise<void> {
|
||||||
await this.updateImages();
|
await Promise.all(
|
||||||
await this.updateGuestbook();
|
[
|
||||||
await this.updateFriends();
|
this.updateImages(),
|
||||||
await this.updateGroups();
|
this.updateGuestbook(),
|
||||||
|
this.updateFriends(),
|
||||||
|
this.updateGroups()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
await core.cache.profileCache.registerMeta(
|
await core.cache.profileCache.registerMeta(
|
||||||
name,
|
name,
|
||||||
|
|
Loading…
Reference in New Issue