your message
This commit is contained in:
113
apps/api/src/groups/groups.gateway.ts
Normal file
113
apps/api/src/groups/groups.gateway.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import {
|
||||
ConnectedSocket,
|
||||
MessageBody,
|
||||
OnGatewayConnection,
|
||||
SubscribeMessage,
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
} from '@nestjs/websockets';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { SupabaseService } from '../auth/supabase.service';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { GroupsService } from './groups.service';
|
||||
|
||||
@WebSocketGateway({
|
||||
namespace: '/ws/course-groups',
|
||||
cors: {
|
||||
origin: true,
|
||||
credentials: true,
|
||||
},
|
||||
})
|
||||
export class GroupsGateway implements OnGatewayConnection {
|
||||
@WebSocketServer()
|
||||
server: Server;
|
||||
|
||||
constructor(
|
||||
private jwtService: JwtService,
|
||||
private authService: AuthService,
|
||||
private supabaseService: SupabaseService,
|
||||
private usersService: UsersService,
|
||||
private groupsService: GroupsService
|
||||
) {}
|
||||
|
||||
async handleConnection(client: Socket): Promise<void> {
|
||||
try {
|
||||
const user = await this.resolveUser(client);
|
||||
if (!user) {
|
||||
client.disconnect();
|
||||
return;
|
||||
}
|
||||
client.data.user = user;
|
||||
} catch {
|
||||
client.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeMessage('groups:join')
|
||||
async joinGroup(
|
||||
@ConnectedSocket() client: Socket,
|
||||
@MessageBody() body: { groupId: string }
|
||||
) {
|
||||
const user = client.data.user;
|
||||
if (!user || !body?.groupId) return { ok: false };
|
||||
|
||||
const canJoin = await this.groupsService.isMember(body.groupId, user.id);
|
||||
if (!canJoin) {
|
||||
await this.groupsService.joinByInvite(body.groupId, user.id);
|
||||
}
|
||||
await client.join(this.room(body.groupId));
|
||||
const messages = await this.groupsService.getGroupMessages(body.groupId, user.id);
|
||||
return { ok: true, messages };
|
||||
}
|
||||
|
||||
@SubscribeMessage('groups:send')
|
||||
async sendMessage(
|
||||
@ConnectedSocket() client: Socket,
|
||||
@MessageBody() body: { groupId: string; content: string }
|
||||
) {
|
||||
const user = client.data.user;
|
||||
if (!user || !body?.groupId || !body?.content?.trim()) return { ok: false };
|
||||
|
||||
const message = await this.groupsService.sendMessage(body.groupId, user.id, body.content.trim());
|
||||
this.server.to(this.room(body.groupId)).emit('groups:new-message', message);
|
||||
return { ok: true, message };
|
||||
}
|
||||
|
||||
private room(groupId: string): string {
|
||||
return `group:${groupId}`;
|
||||
}
|
||||
|
||||
private async resolveUser(client: Socket) {
|
||||
const authToken =
|
||||
(client.handshake.auth?.token as string | undefined) ||
|
||||
((client.handshake.headers.authorization as string | undefined)?.replace(/^Bearer\s+/i, '')) ||
|
||||
undefined;
|
||||
if (!authToken) return null;
|
||||
|
||||
try {
|
||||
const payload = this.jwtService.verify<{ sub: string; email: string; supabaseId: string }>(authToken);
|
||||
const user = await this.authService.validateJwtPayload(payload);
|
||||
if (user) return user;
|
||||
} catch {
|
||||
// Fallback to Supabase access token
|
||||
}
|
||||
|
||||
const supabaseUser = await this.supabaseService.verifyToken(authToken);
|
||||
if (!supabaseUser) return null;
|
||||
let user = await this.usersService.findBySupabaseId(supabaseUser.id);
|
||||
if (!user) {
|
||||
user = await this.usersService.create({
|
||||
supabaseId: supabaseUser.id,
|
||||
email: supabaseUser.email!,
|
||||
name:
|
||||
supabaseUser.user_metadata?.full_name ||
|
||||
supabaseUser.user_metadata?.name ||
|
||||
null,
|
||||
avatarUrl: supabaseUser.user_metadata?.avatar_url || null,
|
||||
});
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user