your message

This commit is contained in:
root
2026-02-06 14:53:52 +00:00
parent c809d049fe
commit 3d488f22b7
47 changed files with 3127 additions and 425 deletions

View 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;
}
}