buttercheezii c539dba6a8 Fix insane bug where some profiles cause client hang
Some code was converting the customs object to an array with millions of elements, which caused iteration and ipc transport (json serialize) to hang the client for many minutes at a time.

This is probably because incorrect lodash functions were used on the customs. Lodash would convert it to array and then it would get stored and turn up as array every time, corrupting storage and ensuring client denial of service.

This fixes that bug by avoiding using incorrect lodash functions that convert things to arrays with millions of elements.

It also retroactively fixes profiles that were stored with array customs, so the client won't crash.
2021-12-06 05:00:44 -08:00

76 lines
2.3 KiB
TypeScript

import {Character as ComplexCharacter, CharacterGroup, Guestbook} from '../../site/character_page/interfaces';
import { PermanentIndexedStore, ProfileRecord } from './types';
import { CharacterImage, SimpleCharacter } from '../../interfaces';
import { WorkerClient } from './worker/client';
export class WorkerStore implements PermanentIndexedStore {
// @ts-ignore
private _isVue = true;
protected readonly workerClient: WorkerClient;
constructor(jsEndpointFile: string) {
this.workerClient = new WorkerClient(jsEndpointFile);
}
static async open(jsEndpointFile: string, dbName?: string): Promise<WorkerStore> {
const store = new WorkerStore(jsEndpointFile);
await store.workerClient.request('init', { dbName });
return store;
}
async getProfile(name: string): Promise<ProfileRecord | undefined> {
const record: ProfileRecord | undefined = await this.workerClient.request('get', { name });
// fix custom kinks to prevent hangs
if (record && Array.isArray(record.profileData.character.customs)) {
// fix customs because it will crash the client
const customsObject: ProfileRecord['profileData']['character']['customs'] = {};
for (const [key, value] of Object.entries(record.profileData.character.customs)) {
if (value !== undefined) customsObject[key] = value;
}
record.profileData.character.customs = customsObject;
}
return record;
}
async storeProfile(character: ComplexCharacter): Promise<void> {
return this.workerClient.request('store', { character });
}
async updateProfileMeta(
name: string,
images: CharacterImage[] | null,
guestbook: Guestbook | null,
friends: SimpleCharacter[] | null,
groups: CharacterGroup[] | null
): Promise<void> {
return this.workerClient.request('update-meta', { name, images, guestbook, friends, groups });
}
async start(): Promise<void> {
return this.workerClient.request('start');
}
async stop(): Promise<void> {
return this.workerClient.request('stop');
}
async flushProfiles(daysToExpire: number): Promise<void> {
return this.workerClient.request('flush', { daysToExpire });
}
}