fixed subscriptions re-requesting because of re-created fetch functions
This commit is contained in:
parent
1c1d3209bf
commit
d875a8f8fb
@ -323,20 +323,21 @@ export class Resource implements WithEquals<Resource> {
|
||||
this.hash.toString('hex') === other.hash.toString('hex')
|
||||
);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return 'r#' + this.id;
|
||||
}
|
||||
}
|
||||
|
||||
export class Token implements WithEquals<Token> {
|
||||
public id: string;
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
public readonly token: string,
|
||||
public member: Member | { id: string } | null,
|
||||
public readonly created: Date,
|
||||
public readonly expires: Date | null,
|
||||
public readonly source?: unknown
|
||||
) {
|
||||
this.id = token; // for comparison purposes
|
||||
}
|
||||
) {}
|
||||
|
||||
fill(members: Map<string, Member>) {
|
||||
if (this.member) {
|
||||
@ -349,6 +350,7 @@ export class Token implements WithEquals<Token> {
|
||||
|
||||
static fromDBData(dataToken: any): Token {
|
||||
return new Token(
|
||||
dataToken.id,
|
||||
dataToken.token,
|
||||
dataToken.member_id ? { id: dataToken.member_id } : null,
|
||||
new Date(dataToken.created),
|
||||
@ -359,11 +361,12 @@ export class Token implements WithEquals<Token> {
|
||||
|
||||
equals(other: Token) {
|
||||
return (
|
||||
this.id === other.id &&
|
||||
this.token === other.token &&
|
||||
this.member?.id === other.member?.id &&
|
||||
this.created === other.created &&
|
||||
this.expires === other.expires
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static sortFurthestExpiresFirst(a: Token, b: Token) {
|
||||
@ -378,6 +381,10 @@ export class Token implements WithEquals<Token> {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `t#${this.id}/${this.token}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotInitializedError extends Error {
|
||||
|
@ -72,7 +72,10 @@ const GuildInvitesDisplay: FC<GuildInvitesDisplayProps> = (props: GuildInvitesDi
|
||||
</div>
|
||||
<div className="actions">
|
||||
<Button colorType={ButtonColorType.BRAND}>{BaseElements.DOWNLOAD}</Button>
|
||||
<Button colorType={ButtonColorType.NEGATIVE}>{BaseElements.TRASHCAN}</Button>
|
||||
<Button
|
||||
colorType={ButtonColorType.NEGATIVE}
|
||||
onClick={async () => await guild.requestDoRevokeToken(token.token)}
|
||||
>{BaseElements.TRASHCAN}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,3 +1,8 @@
|
||||
import * as electronRemote from '@electron/remote';
|
||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { FC, useEffect, useMemo, useState } from "react";
|
||||
import CombinedGuild from "../../guild-combined";
|
||||
import ChoicesControl from "../components/control-choices";
|
||||
|
@ -70,23 +70,24 @@ export default class GuildSubscriptions {
|
||||
private static useGuildSubscriptionEffect<T>(
|
||||
isMountedRef: React.MutableRefObject<boolean>,
|
||||
subscriptionParams: EffectParams<T>,
|
||||
fetchFunc: (() => Promise<T>) | (() => Promise<T | null>),
|
||||
fetchDeps: DependencyList
|
||||
fetchFunc: (() => Promise<T>) | (() => Promise<T | null>)
|
||||
) {
|
||||
const { guild, onFetch, onFetchError, bindEventsFunc, unbindEventsFunc } = subscriptionParams;
|
||||
|
||||
const fetchManagerFunc = useCallback(async () => {
|
||||
if (!isMountedRef.current) return;
|
||||
try {
|
||||
const value = await fetchFunc();
|
||||
const fetchManagerFunc = useMemo(() => {
|
||||
return async () => {
|
||||
if (!isMountedRef.current) return;
|
||||
onFetch(value);
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error fetching for subscription', e);
|
||||
if (!isMountedRef.current) return;
|
||||
onFetchError(e);
|
||||
try {
|
||||
const value = await fetchFunc();
|
||||
if (!isMountedRef.current) return;
|
||||
onFetch(value);
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error fetching for subscription', e);
|
||||
if (!isMountedRef.current) return;
|
||||
onFetchError(e);
|
||||
}
|
||||
}
|
||||
}, [ ...fetchDeps, fetchFunc ]);
|
||||
}, [ fetchFunc ]);
|
||||
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
@ -111,8 +112,7 @@ export default class GuildSubscriptions {
|
||||
private static useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
|
||||
guild: CombinedGuild,
|
||||
eventMappingParams: SingleEventMappingParams<T, UE, CE>,
|
||||
fetchFunc: (() => Promise<T>) | (() => Promise<T | null>),
|
||||
fetchDeps: DependencyList
|
||||
fetchFunc: (() => Promise<T>) | (() => Promise<T | null>)
|
||||
): [value: T | null, fetchError: unknown | null, events: EventEmitter<SingleSubscriptionEvents>] {
|
||||
const { updatedEventName, updatedEventArgsMap, conflictEventName, conflictEventArgsMap } = eventMappingParams;
|
||||
|
||||
@ -173,7 +173,7 @@ export default class GuildSubscriptions {
|
||||
onFetchError,
|
||||
bindEventsFunc,
|
||||
unbindEventsFunc
|
||||
}, fetchFunc, fetchDeps);
|
||||
}, fetchFunc);
|
||||
|
||||
return [ value, fetchError, events ];
|
||||
}
|
||||
@ -187,8 +187,7 @@ export default class GuildSubscriptions {
|
||||
>(
|
||||
guild: CombinedGuild,
|
||||
eventMappingParams: MultipleEventMappingParams<T, NE, UE, RE, CE>,
|
||||
fetchFunc: (() => Promise<T[]>) | (() => Promise<T[] | null>),
|
||||
fetchDeps: DependencyList
|
||||
fetchFunc: (() => Promise<T[]>) | (() => Promise<T[] | null>)
|
||||
): [value: T[] | null, fetchError: unknown | null, events: EventEmitter<MultipleSubscriptionEvents<T>>] {
|
||||
const {
|
||||
newEventName, newEventArgsMap,
|
||||
@ -291,35 +290,43 @@ export default class GuildSubscriptions {
|
||||
onFetchError,
|
||||
bindEventsFunc,
|
||||
unbindEventsFunc
|
||||
}, fetchFunc, fetchDeps);
|
||||
}, fetchFunc);
|
||||
|
||||
return [ value, fetchError, events ];
|
||||
}
|
||||
|
||||
static useGuildMetadataSubscription(guild: CombinedGuild) {
|
||||
const fetchMetadataFunc = useCallback(async () => {
|
||||
//LOG.silly('fetching metadata for subscription');
|
||||
return await guild.fetchMetadata();
|
||||
}, [ guild ]);
|
||||
return GuildSubscriptions.useSingleGuildSubscription<GuildMetadata, 'update-metadata', 'conflict-metadata'>(guild, {
|
||||
updatedEventName: 'update-metadata',
|
||||
updatedEventArgsMap: (guildMeta: GuildMetadata) => guildMeta,
|
||||
conflictEventName: 'conflict-metadata',
|
||||
conflictEventArgsMap: (changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => newGuildMeta
|
||||
}, async () => {
|
||||
return await guild.fetchMetadata()
|
||||
}, [ guild ]);
|
||||
}, fetchMetadataFunc);
|
||||
}
|
||||
|
||||
static useResourceSubscription(guild: CombinedGuild, resourceId: string | null) {
|
||||
const fetchResourceFunc = useCallback(async () => {
|
||||
//LOG.silly('fetching resource for subscription (resourceId: ' + resourceId + ')');
|
||||
if (resourceId === null) return null;
|
||||
return await guild.fetchResource(resourceId);
|
||||
}, [ guild, resourceId ]);
|
||||
return GuildSubscriptions.useSingleGuildSubscription<Resource, 'update-resource', 'conflict-resource'>(guild, {
|
||||
updatedEventName: 'update-resource',
|
||||
updatedEventArgsMap: (resource: Resource) => resource,
|
||||
conflictEventName: 'conflict-resource',
|
||||
conflictEventArgsMap: (query: IDQuery, changesType: AutoVerifierChangesType, oldResource: Resource, newResource: Resource) => newResource
|
||||
}, async () => {
|
||||
if (resourceId === null) return null;
|
||||
return await guild.fetchResource(resourceId);
|
||||
}, [ guild, resourceId ]);
|
||||
}, fetchResourceFunc);
|
||||
}
|
||||
|
||||
static useTokensSubscription(guild: CombinedGuild) {
|
||||
const fetchTokensFunc = useCallback(async () => {
|
||||
//LOG.silly('fetching tokens for subscription');
|
||||
return await guild.fetchTokens();
|
||||
}, [ guild ]);
|
||||
return GuildSubscriptions.useMultipleGuildSubscription<Token, 'new-tokens', 'update-tokens', 'remove-tokens', 'conflict-tokens'>(guild, {
|
||||
newEventName: 'new-tokens',
|
||||
newEventArgsMap: (tokens: Token[]) => tokens,
|
||||
@ -330,8 +337,6 @@ export default class GuildSubscriptions {
|
||||
conflictEventName: 'conflict-tokens',
|
||||
conflictEventArgsMap: (changesType: AutoVerifierChangesType, changes: Changes<Token>) => changes,
|
||||
sortFunc: Token.sortFurthestExpiresFirst
|
||||
}, async () => {
|
||||
return await guild.fetchTokens();
|
||||
}, [ guild ]);
|
||||
}, fetchTokensFunc);
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +123,40 @@ export default class CombinedGuild extends EventEmitter<Connectable & Conflictab
|
||||
this.emit('update-messages', messages);
|
||||
});
|
||||
|
||||
// Resources
|
||||
this.socketGuild.on('update-resource', async (updatedResource: Resource) => {
|
||||
LOG.info(`g#${this.id} updated ${updatedResource}`);
|
||||
this.ramGuild.handleResourceChanged(updatedResource);
|
||||
this.personalDBGuild.handleResourceChanged(updatedResource);
|
||||
this.emit('update-resource', updatedResource);
|
||||
});
|
||||
|
||||
// Tokens
|
||||
this.socketGuild.on('new-tokens', async (newTokens: Token[]) => {
|
||||
for (const token of newTokens) {
|
||||
LOG.info(`g#${this.id} new token ${token}`);
|
||||
}
|
||||
this.ramGuild.handleTokensAdded(newTokens);
|
||||
this.personalDBGuild.handleTokensAdded(newTokens);
|
||||
this.emit('new-tokens', newTokens);
|
||||
});
|
||||
this.socketGuild.on('update-tokens', async (updatedTokens: Token[]) => {
|
||||
for (const token of updatedTokens) {
|
||||
LOG.info(`g#${this.id} updated ${token}`);
|
||||
}
|
||||
this.ramGuild.handleTokensChanged(updatedTokens);
|
||||
this.personalDBGuild.handleTokensChanged(updatedTokens);
|
||||
this.emit('update-tokens', updatedTokens);
|
||||
});
|
||||
this.socketGuild.on('remove-tokens', async (removedTokens: Token[]) => {
|
||||
for (const token of removedTokens) {
|
||||
LOG.info(`g#${this.id} removed ${token}`);
|
||||
}
|
||||
this.ramGuild.handleTokensDeleted(removedTokens);
|
||||
this.personalDBGuild.handleTokensDeleted(removedTokens);
|
||||
this.emit('remove-tokens', removedTokens);
|
||||
});
|
||||
|
||||
const diskSocket = new PairVerifierFetchable(this.personalDBGuild, this.socketGuild);
|
||||
const ramDiskSocket = new PairVerifierFetchable(this.ramGuild, diskSocket);
|
||||
|
||||
|
@ -62,6 +62,27 @@ export default class SocketGuild extends EventEmitter<Connectable> implements As
|
||||
const message = Message.fromDBData(dataMessage);
|
||||
this.emit('new-messages', [ message ]);
|
||||
});
|
||||
|
||||
// TODO: The server does not emit update-resource
|
||||
this.socket.on('update-resource', async (updatedDataResource: any) => {
|
||||
const updatedResource = Resource.fromDBData(updatedDataResource);
|
||||
this.emit('update-resource', updatedResource);
|
||||
});
|
||||
|
||||
// TODO: The server does not emit new-token
|
||||
this.socket.on('new-token', async (newDataToken: any) => {
|
||||
const newToken = Token.fromDBData(newDataToken);
|
||||
this.emit('new-tokens', [ newToken ]);
|
||||
});
|
||||
// TODO: The server does not emit update-token
|
||||
this.socket.on('update-token', async (updatedDataToken: any) => {
|
||||
const updatedToken = Token.fromDBData(updatedDataToken);
|
||||
this.emit('update-tokens', [ updatedToken ]);
|
||||
});
|
||||
this.socket.on('revoke-token', async (revokedDataToken: any) => {
|
||||
const revokedToken = Token.fromDBData(revokedDataToken);
|
||||
this.emit('remove-tokens', [ revokedToken ]);
|
||||
});
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
|
@ -162,6 +162,10 @@ export const GuildEventNames = [
|
||||
'new-messages',
|
||||
'update-messages',
|
||||
'remove-messages',
|
||||
'update-resource',
|
||||
'new-tokens',
|
||||
'update-tokens',
|
||||
'remove-tokens',
|
||||
'conflict-metadata',
|
||||
'conflict-channels',
|
||||
'conflict-members',
|
||||
|
@ -40,6 +40,12 @@ export default class GuildsManager extends EventEmitter<{
|
||||
'update-messages': (guild: CombinedGuild, updatedMessages: Message[]) => void;
|
||||
'remove-messages': (guild: CombinedGuild, removedMessages: Message[]) => void;
|
||||
|
||||
'update-resource': (guild: CombinedGuild, updatedResource: Resource) => void;
|
||||
|
||||
'new-tokens': (guild: CombinedGuild, newTokens: Token[]) => void;
|
||||
'update-tokens': (guild: CombinedGuild, updatedTokens: Token[]) => void;
|
||||
'remove-tokens': (guild: CombinedGuild, removedTokens: Token[]) => void;
|
||||
|
||||
'conflict-metadata': (guild: CombinedGuild, changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => void;
|
||||
'conflict-channels': (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Channel>) => void;
|
||||
'conflict-members': (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Member>) => void;
|
||||
|
@ -596,10 +596,11 @@ export default class DB {
|
||||
return result;
|
||||
}
|
||||
|
||||
static async revokeToken(token: string): Promise<void> {
|
||||
const result = await db.query('DELETE FROM "tokens" WHERE "token"=$1', [ token ]);
|
||||
if (result.rowCount != 1) {
|
||||
static async revokeToken(guildId: string, token: string): Promise<any> {
|
||||
const result = await db.query('DELETE FROM "tokens" WHERE "guild_id"=$1 AND "token"=$2 RETURNING *', [ guildId, token ]);
|
||||
if (result.rows.length != 1) {
|
||||
throw new Error('unable to remove token');
|
||||
}
|
||||
return result.rows[0];
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,10 @@ class SignatureError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function guildPrivilegeName(guildId: string, privilege: string) {
|
||||
return guildId + '&' + privilege;
|
||||
}
|
||||
|
||||
function bindEvent(
|
||||
client: socketio.Socket,
|
||||
identity: IIdentity | null,
|
||||
@ -283,9 +287,10 @@ function bindChallengeVerificationEvents(io: socketio.Server, client: socketio.S
|
||||
throw new EventError('invalid signature');
|
||||
}
|
||||
|
||||
let member: any;
|
||||
try {
|
||||
await DB.setMemberStatus(identity.guildId, identity.memberId, 'online');
|
||||
const member = await DB.getMember(identity.guildId, identity.memberId);
|
||||
member = await DB.getMember(identity.guildId, identity.memberId);
|
||||
io.to(identity.guildId).emit('update-member', member);
|
||||
} catch (e: unknown) {
|
||||
LOG.warn('unable to set status for m#' + identity.memberId, e);
|
||||
@ -293,9 +298,13 @@ function bindChallengeVerificationEvents(io: socketio.Server, client: socketio.S
|
||||
}
|
||||
|
||||
identity.verified = true;
|
||||
client.join(identity.guildId); // join the socket.io guild room
|
||||
const rooms = [ identity.guildId ].concat(
|
||||
member.privileges.split(',').map((privilege: string) => guildPrivilegeName(identity.guildId as string, privilege))
|
||||
);
|
||||
LOG.debug(`c#${client.id} joining ${rooms.join(', ')}`);
|
||||
client.join(rooms);
|
||||
|
||||
LOG.info(`c#${client.id}: verified as g#${identity.guildId} u#${identity.memberId}`);
|
||||
|
||||
respond(null, identity.memberId);
|
||||
}
|
||||
);
|
||||
@ -459,8 +468,12 @@ function bindAdminEvents(io: socketio.Server, client: socketio.Socket, identity:
|
||||
throw new EventError('token already taken');
|
||||
}
|
||||
LOG.debug(`u#${identity.memberId}: revoking t#${token}`);
|
||||
await DB.revokeToken(token);
|
||||
const revokedToken = await DB.revokeToken(identity.guildId, token);
|
||||
respond(null);
|
||||
|
||||
const targetRoom = guildPrivilegeName(identity.guildId as string, 'modify_members');
|
||||
LOG.debug('emitting revoke token to members in ' + targetRoom);
|
||||
io.in(targetRoom).emit('revoke-token', revokedToken);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user