feat(center): POST /auth/resolve-names

Resolve display names/emails to userIds within a guild node's active
members (api-key auth). Used by guild for <@user.name:NAME> translation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-15 15:47:01 +01:00
parent bc0d1ba8bf
commit 3da51a60bc
2 changed files with 35 additions and 0 deletions

View File

@@ -240,6 +240,33 @@ export class AuthService {
.filter((x) => !!x.email);
}
// Resolve display names (or emails) to userIds, scoped to a guild node's
// active members. Called by guild nodes (api-key auth). Unresolved names
// are omitted from the result.
async resolveNames(
guildNodeId: string,
names: string[],
): Promise<{ resolved: Record<string, string> }> {
const wanted = [...new Set(names.map((n) => String(n ?? '').trim()).filter(Boolean))];
if (!guildNodeId || !wanted.length) return { resolved: {} };
const members = await this.guildUserRepo.find({ where: { guildNodeId, status: 'active' } });
const userIds = [...new Set(members.map((m) => m.userId))];
if (!userIds.length) return { resolved: {} };
const users = await this.userRepo
.createQueryBuilder('u')
.where('u.id IN (:...userIds)', { userIds })
.getMany();
const resolved: Record<string, string> = {};
for (const name of wanted) {
const hit = users.find((u) => u.name === name) ?? users.find((u) => u.email === name);
if (hit) resolved[name] = hit.id;
}
return { resolved };
}
verifyCenterAccessToken(accessToken: string): jwt.JwtPayload {
try {
const payload = jwt.verify(accessToken, process.env.FABRIC_BACKEND_CENTER_JWT_ACCESS_SECRET as string) as jwt.JwtPayload;