mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-24 13:55:04 +02:00
579 lines
20 KiB
TypeScript
579 lines
20 KiB
TypeScript
import * as moment from 'moment';
|
|
import { Socket, SocketServer, Method, ClientExtensions } from 'ag-sockets';
|
|
import { AccountCounters, Subscription } from '../common/interfaces';
|
|
import { HOUR } from '../common/constants';
|
|
import { fromNow, removeItem, formatDuration } from '../common/utils';
|
|
import { hasRole } from '../common/accountUtils';
|
|
import {
|
|
Settings, UpdateOrigin, AccountUpdate, OriginInfo, AccountOrigins, IAdminServerActions, FindPonyQuery,
|
|
AuthUpdate, PonyCreator, ServerConfig, GameServerSettings, AccountState, AuthDetails, MergeAccountData,
|
|
FindAccountQuery, AdminCache, ClearOrignsOptions, ModelTypes, Stats
|
|
} from '../common/adminInterfaces';
|
|
import { ClientAdminActions, ClientUpdate } from '../client/clientAdminActions';
|
|
import { TokenData } from './serverInterfaces';
|
|
import { toAccountData, toPonyObjectAdmin } from './serverUtils';
|
|
import {
|
|
IAccount, Account, ICharacter, Character, Auth, checkIfAdmin, ID, findCharacterById, updateAuth, queryAuths,
|
|
nullToUndefined, findAccount, findFriendIds
|
|
} from './db';
|
|
import {
|
|
updateAccountSafe, setRole, addEmail, removeEmail, removeIgnore, updateAccountCounter, timeoutAccount,
|
|
addIgnores, setAccountState, findAccounts, getAccountsByEmails, getAccountsByOrigin,
|
|
removeAccount, setAccountAlert
|
|
} from './api/admin-accounts';
|
|
import {
|
|
getAdminState, getChat, kickFromAllServers, notifyUpdate, clearSessions, actionForAllServers, updateOrigin,
|
|
getChatForAccounts, shutdownServers, resetUpdating, getUserCounts, getAccountDetails,
|
|
EndPoints, getOtherStats, updateGameServerSettings, updateServerSettings, forAllGameServers,
|
|
} from './api/admin';
|
|
import {
|
|
findPonies, removeCharactersAboveLimit, createCharacter, removeCharacter, assignCharacter, removeAllCharacters
|
|
} from './api/ponies';
|
|
import { accountStatus, accountAround, getServer, getLoginServer, RemovedDocument, accountHidden } from './internal';
|
|
import { create } from './reporter';
|
|
import { system } from './logger';
|
|
import { AdminService } from './services/adminService';
|
|
import { getOriginStats, clearOrigins, removeAllOrigins, removeOrigins, addOrigin, clearOriginsForAccounts } from './api/origins';
|
|
import { getDuplicateEntries, getAllDuplicatesQuickInfo, getAllDuplicatesWithInfo } from './api/duplicates';
|
|
import { splitAccounts } from './api/merge';
|
|
import { removeAuth, assignAuth } from './api/admin-auths';
|
|
import { removeFriend, addFriend } from './accountUtils';
|
|
|
|
@Socket({
|
|
id: 'admin',
|
|
path: '/ws-admin',
|
|
connectionTokens: true,
|
|
tokenLifetime: 12 * HOUR,
|
|
perMessageDeflate: false,
|
|
})
|
|
export class AdminServerActions implements IAdminServerActions, SocketServer {
|
|
private account: IAccount;
|
|
private cache: AdminCache = {};
|
|
private subscriptions = new Map<string, Subscription>();
|
|
constructor(
|
|
private client: ClientAdminActions & ClientExtensions,
|
|
private server: ServerConfig,
|
|
private settings: Settings,
|
|
private adminService: AdminService,
|
|
private endPoints: EndPoints,
|
|
private removedDocument: RemovedDocument,
|
|
) {
|
|
this.account = (client.tokenData as TokenData).account;
|
|
this.subscriptions.set('account:deleted', this.adminService.accountDeleted.subscribe(account => {
|
|
if (this.cache.findAccounts) {
|
|
removeItem(this.cache.findAccounts.result, account);
|
|
}
|
|
}));
|
|
}
|
|
disconnected() {
|
|
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
|
clearTimeout(this.updatesTimeout);
|
|
}
|
|
// other
|
|
@Method({ promise: true })
|
|
async getSignedAccount() {
|
|
return toAccountData(this.account);
|
|
}
|
|
@Method({ promise: true })
|
|
async getCounts() {
|
|
const characters = await Promise.resolve(Character.estimatedDocumentCount() as any);
|
|
|
|
return {
|
|
characters,
|
|
accounts: this.adminService.accounts.items.length,
|
|
auths: this.adminService.auths.items.length,
|
|
origins: this.adminService.origins.items.length,
|
|
};
|
|
}
|
|
@Method({ promise: true })
|
|
async getOtherStats() {
|
|
return await getOtherStats(this.adminService);
|
|
}
|
|
// subscribing
|
|
private updates: ClientUpdate[] = [];
|
|
private updatesTimeout: any;
|
|
private pushUpdate(type: ModelTypes, id: string, update: any) {
|
|
const index = this.updates.findIndex(u => u.type === type && u.id === id);
|
|
|
|
if (index !== -1) {
|
|
this.updates[index].update = update;
|
|
} else {
|
|
this.updates.push({ type, id, update });
|
|
}
|
|
|
|
if (!this.updatesTimeout) {
|
|
this.updatesTimeout = setTimeout(() => {
|
|
this.client.updates(this.updates);
|
|
this.updates = [];
|
|
this.updatesTimeout = 0;
|
|
}, 50);
|
|
}
|
|
}
|
|
@Method()
|
|
subscribe(type: ModelTypes, id: string) {
|
|
const key = `${type}:${id}`;
|
|
|
|
if (this.subscriptions.has(key))
|
|
return;
|
|
|
|
if (type === 'ponies') {
|
|
if (!this.adminService.ponies.get(id)) {
|
|
this.adminService.ponies.fetch({ _id: id });
|
|
}
|
|
}
|
|
|
|
let subscription: Subscription | undefined;
|
|
|
|
if (type === 'accountAuths') {
|
|
subscription = this.adminService.subscribeToAccountAuths(id, update => this.pushUpdate(type, id, update));
|
|
} else if (type === 'accountOrigins') {
|
|
subscription = this.adminService.subscribeToAccountOrigins(id, update => this.pushUpdate(type, id, update));
|
|
} else if (type === 'accountPonies') {
|
|
subscription = this.adminService.subscribeToAccountPonies(id, update => this.pushUpdate(type, id, update));
|
|
} else if (type in this.adminService) {
|
|
subscription = this.adminService[type].subscribe(id, (id, update) => this.pushUpdate(type, id, update));
|
|
} else {
|
|
throw new Error(`Invalid model type (${type})`);
|
|
}
|
|
|
|
if (subscription) {
|
|
this.subscriptions.set(key, subscription);
|
|
}
|
|
}
|
|
@Method()
|
|
unsubscribe(type: ModelTypes, id: string) {
|
|
const key = `${type}:${id}`;
|
|
const subscription = this.subscriptions.get(key);
|
|
|
|
if (subscription) {
|
|
subscription.unsubscribe();
|
|
this.subscriptions.delete(key);
|
|
|
|
if (type === 'ponies') {
|
|
this.adminService.cleanupPony(id);
|
|
} else if (type === 'accountPonies') {
|
|
this.adminService.cleanupPoniesList(id);
|
|
}
|
|
}
|
|
}
|
|
// state
|
|
@Method({ promise: true })
|
|
async clearSessions(accountId: string) {
|
|
await clearSessions(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getState() {
|
|
return getAdminState();
|
|
}
|
|
@Method({ promise: true })
|
|
async updateSettings(update: Partial<Settings>) {
|
|
await updateServerSettings(this.settings, update);
|
|
}
|
|
@Method({ promise: true })
|
|
async updateGameServerSettings(serverId: string, update: Partial<GameServerSettings>) {
|
|
await updateGameServerSettings(this.settings, serverId, update);
|
|
}
|
|
@Method({ promise: true })
|
|
async fetchServerStats(serverId: string) {
|
|
const server = getServer(serverId);
|
|
return await server.api.stats();
|
|
}
|
|
@Method({ promise: true })
|
|
async fetchServerStatsTable(serverId: string, stats: Stats) {
|
|
const server = getServer(serverId);
|
|
return await server.api.statsTable(stats);
|
|
}
|
|
@Method({ promise: true })
|
|
async report(accountId: string) {
|
|
create(this.server, accountId).info(`Reported by ${this.account.name}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async notifyUpdate(server: string) {
|
|
await notifyUpdate(server);
|
|
}
|
|
@Method({ promise: true })
|
|
async shutdownServers(server: string) {
|
|
await shutdownServers(server, true);
|
|
}
|
|
@Method({ promise: true })
|
|
async resetUpdating(server: string) {
|
|
await resetUpdating(server);
|
|
}
|
|
@Method({ promise: true })
|
|
async action(action: string, accountId: string) {
|
|
await actionForAllServers(action, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async kick(accountId: string) {
|
|
await kickFromAllServers(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async kickAll(serverId: string) {
|
|
const server = getServer(serverId);
|
|
await server.api.kickAll();
|
|
}
|
|
@Method({ promise: true })
|
|
async getChat(search: string, date: string, caseInsensitive: boolean) {
|
|
return await getChat(search, date, caseInsensitive);
|
|
}
|
|
@Method({ promise: true })
|
|
async getChatForAccounts(accountIds: string[], date: string) {
|
|
return await getChatForAccounts(accountIds, date);
|
|
}
|
|
@Method({ promise: true })
|
|
async getRequestStats() {
|
|
const loginServer = getLoginServer('login');
|
|
const requests = await loginServer.api.loginServerStats();
|
|
const userCounts = await getUserCounts();
|
|
return { requests, userCounts };
|
|
}
|
|
// live (remove)
|
|
@Method({ promise: true })
|
|
async get(endPoint: keyof EndPoints, id: string) {
|
|
// console.log('get', endPoint);
|
|
// return this.adminService[endPoint].get(id);
|
|
return await this.endPoints[endPoint].get(id) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async getAll(endPoint: keyof EndPoints, timestamp?: string) {
|
|
return await this.endPoints[endPoint].getAll(timestamp) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async assignAccount(endPoint: keyof EndPoints, id: string, account: string) {
|
|
return await this.endPoints[endPoint].assignAccount(id, account) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async removeItem(endPoint: keyof EndPoints, id: string) {
|
|
return await this.endPoints[endPoint].removeItem(id) as any;
|
|
}
|
|
// events
|
|
@Method({ promise: true })
|
|
async removeEvent(id: string) {
|
|
await this.adminService.events.remove(id);
|
|
await this.endPoints.events.removedItem(id);
|
|
}
|
|
// origins
|
|
@Method({ promise: true })
|
|
async updateOrigin(origin: UpdateOrigin) {
|
|
await updateOrigin(origin);
|
|
}
|
|
@Method({ promise: true })
|
|
async getOriginStats() {
|
|
return await getOriginStats(this.adminService.accounts.items);
|
|
}
|
|
@Method({ promise: true })
|
|
async clearOrigins(count: number, andHigher: boolean, options: ClearOrignsOptions) {
|
|
if (!this.adminService.loaded) {
|
|
throw new Error('Not loaded yet');
|
|
} else {
|
|
await clearOrigins(this.adminService, count, andHigher, options);
|
|
}
|
|
}
|
|
@Method({ promise: true })
|
|
async clearOriginsForAccounts(accounts: string[], options: ClearOrignsOptions) {
|
|
if (!this.adminService.loaded) {
|
|
throw new Error('Not loaded yet');
|
|
} else {
|
|
await clearOriginsForAccounts(this.adminService, accounts, options);
|
|
}
|
|
}
|
|
// ponies
|
|
@Method({ promise: true })
|
|
async getPony(id: string) {
|
|
return await Character.findById(id).exec().then(nullToUndefined) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async getPonyInfo(id: string) {
|
|
const character = await findCharacterById(id);
|
|
return toPonyObjectAdmin(character);
|
|
}
|
|
@Method({ promise: true })
|
|
async getPoniesCreators(account: string) {
|
|
const items: ICharacter[] = await Character.find({ account }, '_id name creator').lean().exec();
|
|
return items.map(({ _id, name, creator }) => <PonyCreator>{ _id, name, creator });
|
|
}
|
|
@Method({ promise: true })
|
|
async getPoniesForAccount(account: string) {
|
|
return await Character.find({ account }).lean().exec();
|
|
}
|
|
@Method({ promise: true })
|
|
async getDetailsForAccount(accountId: string) {
|
|
return await getAccountDetails(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async findPonies(query: FindPonyQuery, page: number, _skipTotalCount: boolean) {
|
|
return await findPonies(query, page);
|
|
}
|
|
@Method({ promise: true })
|
|
async createPony(account: string, name: string, info: string) {
|
|
await createCharacter(account, name, info);
|
|
system(account, `Created character (${name}) ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async assignPony(ponyId: string, accountId: string) {
|
|
await assignCharacter(ponyId, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async removePony(id: string) {
|
|
await removeCharacter(this.adminService, id);
|
|
}
|
|
@Method({ promise: true })
|
|
async removePoniesAboveLimit(account: string) {
|
|
await removeCharactersAboveLimit(this.removedDocument, account);
|
|
system(account, `Removed ponies above limit ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeAllPonies(account: string) {
|
|
await removeAllCharacters(this.removedDocument, account);
|
|
system(account, `Removed all ponies ${this.by()}`);
|
|
}
|
|
// auths
|
|
@Method({ promise: true })
|
|
async getAuth(id: string) {
|
|
return await Auth.findById(id).exec().then(nullToUndefined) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async getAuthsForAccount(accountId: string) {
|
|
return await Auth.find({ account: accountId }).exec() as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async fetchAuthDetails(auths: string[]): Promise<AuthDetails[]> {
|
|
const items = await queryAuths({ _id: { $in: auths } }, '_id lastUsed');
|
|
|
|
return items.map(a => ({
|
|
id: a._id.toString(),
|
|
lastUsed: a.lastUsed && a.lastUsed.toISOString(),
|
|
}));
|
|
}
|
|
@Method({ promise: true })
|
|
async updateAuth(id: string, update: AuthUpdate) {
|
|
const auth = await Auth.findById(id).exec();
|
|
await throwOnAdmin(auth && auth.account);
|
|
await updateAuth(id, update);
|
|
}
|
|
@Method({ promise: true })
|
|
async assignAuth(authId: string, accountId: string) {
|
|
await assignAuth(authId, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeAuth(id: string) {
|
|
await removeAuth(this.adminService, id);
|
|
}
|
|
// accounts
|
|
@Method({ promise: true })
|
|
async getAccount(id: string) {
|
|
return await findAccount(id) as any;
|
|
}
|
|
@Method({ promise: true })
|
|
async findAccounts(query: FindAccountQuery) {
|
|
return await findAccounts(this.cache, this.adminService, query);
|
|
}
|
|
@Method({ promise: true })
|
|
async createAccount(name: string): Promise<string> {
|
|
const account = await Account.create({ name });
|
|
system(account._id.toString(), `Created account ${this.by()}`);
|
|
return account._id.toString();
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountsByEmails(emails: string[]) {
|
|
return getAccountsByEmails(this.adminService, emails);
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountsByOrigin(ip: string) {
|
|
return getAccountsByOrigin(this.adminService, ip);
|
|
}
|
|
@Method({ promise: true })
|
|
async setName(accountId: string, name: string) {
|
|
await updateAccountSafe(accountId, { name });
|
|
system(accountId, `Updated name (${name}) ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async setAge(accountId: string, age: number) {
|
|
if (age === -1) {
|
|
await Account.updateOne({ _id: accountId }, { $unset: { birthyear: 1 } }).exec();
|
|
} else {
|
|
const birthyear = (new Date()).getFullYear() - age;
|
|
await Account.updateOne({ _id: accountId }, { birthyear }).exec();
|
|
}
|
|
|
|
system(accountId, `Updated birth year (${age}) ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async setRole(accountId: string, role: string, set: boolean) {
|
|
await setRole(accountId, role, set, hasRole(this.account, 'superadmin'));
|
|
system(accountId, `${set ? 'Added' : 'Removed'} role (${role}) ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async updateAccount(accountId: string, update: AccountUpdate, message?: string) {
|
|
await updateAccountSafe(accountId, update);
|
|
|
|
if (message) {
|
|
system(accountId, `${message} ${this.by()}`);
|
|
}
|
|
}
|
|
@Method({ promise: true })
|
|
async timeoutAccount(accountId: string, timeout: number) {
|
|
const message = timeout ? `Timed out ${moment.duration(timeout).humanize()}` : 'Unmuted';
|
|
system(accountId, `${message} ${this.by()}`);
|
|
await timeoutAccount(accountId, fromNow(timeout | 0));
|
|
}
|
|
@Method({ promise: true })
|
|
async updateAccountCounter(accountId: string, name: keyof AccountCounters, value: number) {
|
|
await updateAccountCounter(accountId, name, value);
|
|
}
|
|
@Method({ promise: true })
|
|
async mergeAccounts(accountId: string, withId: string) {
|
|
const server = getLoginServer('login');
|
|
await server.api.mergeAccounts(accountId, withId, this.by(), hasRole(this.account, 'superadmin'), true);
|
|
}
|
|
@Method({ promise: true })
|
|
async unmergeAccounts(accountId: string, mergeId: string | undefined, split: MergeAccountData, keep: MergeAccountData) {
|
|
await splitAccounts(accountId, mergeId, split, keep, this.by());
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountStatus(accountId: string) {
|
|
return await accountStatus(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountAround(accountId: string) {
|
|
return await accountAround(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountHidden(accountId: string) {
|
|
return await accountHidden(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getAccountFriends(accountId: string) {
|
|
return findFriendIds(accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeAccount(accountId: string) {
|
|
await removeAccount(this.adminService, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async setAlert(accountId: string, message: string, expiresIn: number) {
|
|
await setAccountAlert(accountId, message, fromNow(expiresIn));
|
|
system(accountId, `${expiresIn ? 'Set' : 'Unset'} alert for ${formatDuration(expiresIn)} "${message}" ${this.by()}`);
|
|
}
|
|
// accounts - origins
|
|
@Method({ promise: true })
|
|
async removeAllOrigins(accountId: string) {
|
|
await removeAllOrigins(this.adminService, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeOriginsForAccount(accountId: string, ips: string[]) {
|
|
await removeOrigins(this.adminService, accountId, ips);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeOriginsForAccounts(origins: AccountOrigins[]) {
|
|
await Promise.all(origins.map(o => removeOrigins(this.adminService, o.accountId, o.ips)));
|
|
}
|
|
@Method({ promise: true })
|
|
async addOriginToAccount(accountId: string, origin: OriginInfo) {
|
|
if (origin && origin.ip && origin.country) {
|
|
await addOrigin(accountId, origin);
|
|
system(accountId, `Added origin (${JSON.stringify(origin)}) ${this.by()}`);
|
|
} else {
|
|
throw new Error('Invalid origin');
|
|
}
|
|
}
|
|
// accounts - emails
|
|
@Method({ promise: true })
|
|
async addEmail(accountId: string, email: string) {
|
|
await addEmail(accountId, email);
|
|
system(accountId, `Added email (${email}) ${this.by()}`);
|
|
}
|
|
@Method({ promise: true })
|
|
async removeEmail(accountId: string, email: string) {
|
|
await removeEmail(accountId, email);
|
|
system(accountId, `Removed email (${email}) ${this.by()}`);
|
|
}
|
|
// accounts - ignores
|
|
@Method({ promise: true })
|
|
async removeIgnore(accountId: string, ignore: string) {
|
|
await removeIgnore(accountId, ignore);
|
|
}
|
|
@Method({ promise: true })
|
|
async addIgnores(accountId: string, ignores: string[]) {
|
|
await addIgnores(accountId, ignores);
|
|
}
|
|
@Method({ promise: true })
|
|
async setAccountState(accountId: string, state: AccountState) {
|
|
await setAccountState(accountId, state);
|
|
}
|
|
@Method({ promise: true })
|
|
async getIgnoresAndIgnoredBy(accountId: string): Promise<{ ignores: string[]; ignoredBy: string[]; }> {
|
|
const [ignores, ignoredBy] = await Promise.all([
|
|
Account
|
|
.find({ ignores: { $in: [accountId] } }, '_id')
|
|
.lean()
|
|
.exec()
|
|
.then((accounts: IAccount[]) => accounts.map(a => a._id.toString())),
|
|
Account
|
|
.findOne({ _id: accountId }, 'ignores')
|
|
.lean()
|
|
.exec()
|
|
.then((account: IAccount | null) => account && account.ignores || []),
|
|
]);
|
|
|
|
return { ignores, ignoredBy };
|
|
}
|
|
// accounts - friends
|
|
@Method({ promise: true })
|
|
async removeFriend(accountId: string, friendId: string) {
|
|
await removeFriend(accountId, friendId);
|
|
}
|
|
@Method({ promise: true })
|
|
async addFriend(accountId: string, friendId: string) {
|
|
await addFriend(accountId, friendId);
|
|
}
|
|
// accounts - duplicates
|
|
@Method({ promise: true })
|
|
async getAllDuplicatesQuickInfo(accountId: string) {
|
|
return await getAllDuplicatesQuickInfo(this.adminService, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getAllDuplicates(accountId: string) {
|
|
return await getAllDuplicatesWithInfo(this.adminService, accountId);
|
|
}
|
|
@Method({ promise: true })
|
|
async getDuplicateEntries(force: boolean) {
|
|
return await getDuplicateEntries(this.adminService.accounts.items, force);
|
|
}
|
|
// other
|
|
@Method({ promise: true })
|
|
async getTimings(serverId: string) {
|
|
const server = getServer(serverId);
|
|
return server.api.getTimings();
|
|
}
|
|
@Method({ promise: true })
|
|
async setTimingEnabled(serverId: string, isEnabled: boolean) {
|
|
const server = getServer(serverId);
|
|
server.api.setTimingEnabled(isEnabled);
|
|
}
|
|
@Method({ promise: true })
|
|
async getWorldPerfStats(serverId: string) {
|
|
const server = getServer(serverId);
|
|
return server.api.getWorldPerfStats();
|
|
}
|
|
@Method({ promise: true })
|
|
async teleportTo(accountId: string) {
|
|
const adminAccountId = this.account._id.toString();
|
|
await forAllGameServers(server => server.api.teleportTo(adminAccountId, accountId));
|
|
}
|
|
// utils
|
|
private by() {
|
|
return `by ${this.account.name} [${this.account._id}]`;
|
|
}
|
|
}
|
|
|
|
async function throwOnAdmin(account: ID | null | undefined) {
|
|
if (account) {
|
|
const isAdmin = await checkIfAdmin(account);
|
|
|
|
if (isAdmin) {
|
|
throw new Error('Cannot change for admin user');
|
|
}
|
|
}
|
|
}
|