feat: implement user session management with Redis and add game token verification
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
export interface GatewayUserFlushEvent {
|
||||
userId: string;
|
||||
flushedAt: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface GatewayFlushPublisher {
|
||||
publishUserFlush(userId: string, reason?: string): Promise<void>;
|
||||
}
|
||||
|
||||
export class RedisGatewayFlushPublisher implements GatewayFlushPublisher {
|
||||
private readonly channel: string;
|
||||
private readonly client: { publish: (channel: string, message: string) => Promise<number> };
|
||||
|
||||
constructor(client: { publish: (channel: string, message: string) => Promise<number> }, channel: string) {
|
||||
this.client = client;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
async publishUserFlush(userId: string, reason?: string): Promise<void> {
|
||||
const payload: GatewayUserFlushEvent = {
|
||||
userId,
|
||||
flushedAt: new Date().toISOString(),
|
||||
reason,
|
||||
};
|
||||
await this.client.publish(this.channel, JSON.stringify(payload));
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ export class InMemoryGatewaySessionService implements GatewaySessionService {
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
roles: user.roles,
|
||||
sanctions: user.sanctions,
|
||||
createdAt: user.createdAt,
|
||||
issuedAt: new Date().toISOString(),
|
||||
};
|
||||
this.sessions.set(sessionToken, {
|
||||
@@ -95,6 +98,9 @@ export class InMemoryGatewaySessionService implements GatewaySessionService {
|
||||
userId: session.userId,
|
||||
username: session.username,
|
||||
displayName: session.displayName,
|
||||
roles: session.roles,
|
||||
sanctions: session.sanctions,
|
||||
createdAt: session.createdAt,
|
||||
issuedAt: new Date().toISOString(),
|
||||
};
|
||||
const key = buildGameKey(profile, gameToken);
|
||||
|
||||
@@ -22,6 +22,8 @@ export const createInMemoryUserRepository = (
|
||||
id: randomUUID(),
|
||||
username: input.username,
|
||||
displayName: input.displayName ?? input.username,
|
||||
roles: ['user'],
|
||||
sanctions: {},
|
||||
passwordSalt: salt,
|
||||
passwordHash: hasher.hash(input.password, salt),
|
||||
createdAt: new Date().toISOString(),
|
||||
|
||||
@@ -62,6 +62,9 @@ export class RedisGatewaySessionService implements GatewaySessionService {
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
roles: user.roles,
|
||||
sanctions: user.sanctions,
|
||||
createdAt: user.createdAt,
|
||||
issuedAt: new Date().toISOString(),
|
||||
};
|
||||
await this.client.set(this.keys.sessionKey(sessionToken), JSON.stringify(info), {
|
||||
@@ -114,6 +117,9 @@ export class RedisGatewaySessionService implements GatewaySessionService {
|
||||
userId: session.userId,
|
||||
username: session.username,
|
||||
displayName: session.displayName,
|
||||
roles: session.roles,
|
||||
sanctions: session.sanctions,
|
||||
createdAt: session.createdAt,
|
||||
issuedAt: new Date().toISOString(),
|
||||
};
|
||||
const gameKey = this.keys.gameSessionKey(profile, gameToken);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { UserRecord } from './userRepository.js';
|
||||
import type { UserRecord, UserSanctions } from './userRepository.js';
|
||||
|
||||
export interface GatewaySessionInfo {
|
||||
sessionToken: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
roles: string[];
|
||||
sanctions: UserSanctions;
|
||||
createdAt: string;
|
||||
issuedAt: string;
|
||||
}
|
||||
|
||||
@@ -15,6 +18,9 @@ export interface GameSessionInfo {
|
||||
userId: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
roles: string[];
|
||||
sanctions: UserSanctions;
|
||||
createdAt: string;
|
||||
issuedAt: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ export interface UserRecord {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
roles: string[];
|
||||
sanctions: UserSanctions;
|
||||
passwordHash: string;
|
||||
passwordSalt: string;
|
||||
createdAt: string;
|
||||
@@ -11,13 +13,24 @@ export interface PublicUser {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
roles: string[];
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface UserSanctions {
|
||||
bannedUntil?: string;
|
||||
mutedUntil?: string;
|
||||
suspendedUntil?: string;
|
||||
warningCount?: number;
|
||||
flags?: string[];
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export const toPublicUser = (user: UserRecord): PublicUser => ({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
displayName: user.displayName,
|
||||
roles: user.roles,
|
||||
createdAt: user.createdAt,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user