feat(center): guild join and guild members APIs; stop auto-joining all guilds
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Get, Headers, Post, UnauthorizedException } from '@nestjs/common';
|
import { Body, Controller, Get, Headers, Param, Post, UnauthorizedException } from '@nestjs/common';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { LoginDto } from './dto.login.dto';
|
import { LoginDto } from './dto.login.dto';
|
||||||
import { RefreshDto } from './dto.refresh.dto';
|
import { RefreshDto } from './dto.refresh.dto';
|
||||||
@@ -30,6 +30,20 @@ export class AuthController {
|
|||||||
return this.authService.listMyGuilds(token);
|
return this.authService.listMyGuilds(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('me/guilds/join')
|
||||||
|
joinGuild(@Headers('authorization') authorization: string | undefined, @Body() body: { guildNodeId?: string }) {
|
||||||
|
const token = authorization?.startsWith('Bearer ') ? authorization.slice(7) : '';
|
||||||
|
if (!token) throw new UnauthorizedException('missing bearer token');
|
||||||
|
return this.authService.joinGuild(token, String(body?.guildNodeId ?? ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('guilds/:guildNodeId/members')
|
||||||
|
guildMembers(@Headers('authorization') authorization: string | undefined, @Param('guildNodeId') guildNodeId: string) {
|
||||||
|
const token = authorization?.startsWith('Bearer ') ? authorization.slice(7) : '';
|
||||||
|
if (!token) throw new UnauthorizedException('missing bearer token');
|
||||||
|
return this.authService.listGuildMembers(token, guildNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('introspect')
|
@Post('introspect')
|
||||||
introspect(@Body() body: { token?: string; guildNodeId?: string }) {
|
introspect(@Body() body: { token?: string; guildNodeId?: string }) {
|
||||||
return this.authService.introspectGuildToken(body?.token ?? '', body?.guildNodeId ?? '');
|
return this.authService.introspectGuildToken(body?.token ?? '', body?.guildNodeId ?? '');
|
||||||
|
|||||||
@@ -50,26 +50,10 @@ export class AuthService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
private async getUserGuildsAndTokens(userId: string, email: string) {
|
private async getUserGuildsAndTokens(userId: string, email: string) {
|
||||||
let memberships = await this.guildUserRepo.find({
|
const memberships = await this.guildUserRepo.find({
|
||||||
where: { userId, status: 'active' },
|
where: { userId, status: 'active' },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!memberships.length) {
|
|
||||||
const activeNodes = await this.guildNodeRepo.find({ where: { status: 'active' } });
|
|
||||||
if (activeNodes.length) {
|
|
||||||
await this.guildUserRepo.save(
|
|
||||||
activeNodes.map((n) =>
|
|
||||||
this.guildUserRepo.create({
|
|
||||||
userId,
|
|
||||||
guildNodeId: n.nodeId,
|
|
||||||
status: 'active',
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
memberships = await this.guildUserRepo.find({ where: { userId, status: 'active' } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodeIds = memberships.map((x) => x.guildNodeId);
|
const nodeIds = memberships.map((x) => x.guildNodeId);
|
||||||
if (!nodeIds.length) {
|
if (!nodeIds.length) {
|
||||||
return { guilds: [], guildAccessTokens: [] as Array<{ guildNodeId: string; token: string; tokenType: string }> };
|
return { guilds: [], guildAccessTokens: [] as Array<{ guildNodeId: string; token: string; tokenType: string }> };
|
||||||
@@ -170,6 +154,50 @@ export class AuthService {
|
|||||||
return this.getUserGuildsAndTokens(userId, email);
|
return this.getUserGuildsAndTokens(userId, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async joinGuild(accessToken: string, guildNodeId: string) {
|
||||||
|
const payload = this.verifyCenterAccessToken(accessToken);
|
||||||
|
const userId = String(payload.sub ?? '');
|
||||||
|
if (!userId) throw new UnauthorizedException('invalid access token');
|
||||||
|
|
||||||
|
const node = await this.guildNodeRepo.findOne({ where: { nodeId: guildNodeId, status: 'active' } });
|
||||||
|
if (!node) throw new UnauthorizedException('guild node not found or inactive');
|
||||||
|
|
||||||
|
const existed = await this.guildUserRepo.findOne({ where: { userId, guildNodeId } });
|
||||||
|
if (!existed) {
|
||||||
|
await this.guildUserRepo.save(
|
||||||
|
this.guildUserRepo.create({ userId, guildNodeId, status: 'active' }),
|
||||||
|
);
|
||||||
|
} else if (existed.status !== 'active') {
|
||||||
|
existed.status = 'active';
|
||||||
|
await this.guildUserRepo.save(existed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: 'ok' as const };
|
||||||
|
}
|
||||||
|
|
||||||
|
async listGuildMembers(accessToken: string, guildNodeId: string) {
|
||||||
|
const payload = this.verifyCenterAccessToken(accessToken);
|
||||||
|
const userId = String(payload.sub ?? '');
|
||||||
|
if (!userId) throw new UnauthorizedException('invalid access token');
|
||||||
|
|
||||||
|
const selfMembership = await this.guildUserRepo.findOne({ where: { userId, guildNodeId, status: 'active' } });
|
||||||
|
if (!selfMembership) throw new UnauthorizedException('not a guild member');
|
||||||
|
|
||||||
|
const members = await this.guildUserRepo.find({ where: { guildNodeId, status: 'active' } });
|
||||||
|
const userIds = [...new Set(members.map((m) => m.userId))];
|
||||||
|
if (!userIds.length) return [];
|
||||||
|
|
||||||
|
const users = await this.userRepo
|
||||||
|
.createQueryBuilder('u')
|
||||||
|
.where('u.id IN (:...userIds)', { userIds })
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
const userMap = new Map(users.map((u) => [u.id, u]));
|
||||||
|
return members
|
||||||
|
.map((m) => ({ userId: m.userId, email: userMap.get(m.userId)?.email ?? '', status: m.status }))
|
||||||
|
.filter((x) => !!x.email);
|
||||||
|
}
|
||||||
|
|
||||||
verifyCenterAccessToken(accessToken: string): jwt.JwtPayload {
|
verifyCenterAccessToken(accessToken: string): jwt.JwtPayload {
|
||||||
try {
|
try {
|
||||||
const payload = jwt.verify(accessToken, process.env.FABRIC_BACKEND_CENTER_JWT_ACCESS_SECRET as string) as jwt.JwtPayload;
|
const payload = jwt.verify(accessToken, process.env.FABRIC_BACKEND_CENTER_JWT_ACCESS_SECRET as string) as jwt.JwtPayload;
|
||||||
|
|||||||
Reference in New Issue
Block a user