"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; var InternalRealtimeController_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.InternalRealtimeController = void 0; const common_1 = require("@nestjs/common"); const prisma_service_1 = require("../prisma/prisma.service"); const chat_service_1 = require("./chat.service"); const internal_token_guard_1 = require("../auth/internal-token.guard"); let InternalRealtimeController = InternalRealtimeController_1 = class InternalRealtimeController { prisma; chat; logger = new common_1.Logger(InternalRealtimeController_1.name); constructor(prisma, chat) { this.prisma = prisma; this.chat = chat; } async getState(conversationId, workspaceId) { return this.statePayload(conversationId, workspaceId); } async postState(conversationId, workspaceId) { return this.statePayload(conversationId, workspaceId); } async postEvent(conversationId, body) { const event = body.event; const evType = String(event?.["eventType"] ?? event?.["type"] ?? "message"); if (evType === "typing") return { ok: true, skipped: "typing" }; if (evType === "read") return { ok: true, skipped: "read" }; if (evType === "presence") return { ok: true, skipped: "presence" }; const seq = Number(event?.["seq"] ?? 0); const exists = await this.prisma.conversationMessage.findUnique({ where: { conversationId_seq: { conversationId, seq } }, select: { id: true }, }); if (exists) return { ok: true, skipped: "duplicate" }; const created = await this.prisma.conversationMessage.create({ data: { conversationId, userId: event?.["userId"] ?? null, body: event?.["body"] ?? null, eventType: evType, metadata: (event?.["meta"] ?? event?.["metadata"] ?? {}), seq, }, }); await this.prisma.conversation.update({ where: { id: conversationId }, data: { lastMessageAt: created.createdAt }, }).catch(() => undefined); return { ok: true, id: created.id }; } async recent(conversationId, limitParam) { const limit = this.parseLimit(limitParam); const messages = await this.fetchRecent(conversationId, limit); return { messages, nextCursor: null }; } async recentPost(conversationId, body) { const limit = this.parseLimit(body?.limit === undefined ? undefined : String(body.limit)); const messages = await this.fetchRecent(conversationId, limit); return { messages, nextCursor: null }; } async getMembers(conversationId) { const members = (await this.membersPayload(conversationId)); return { members }; } async addMember(conversationId, body) { if (!body?.userId) throw new common_1.BadRequestException("userId required"); const role = body.role ?? "member"; return this.chat.ensureMember(conversationId, body.userId, role); } async userConversations(userId) { const memberships = await this.prisma.conversationMember.findMany({ where: { userId }, select: { conversation: { select: { id: true, title: true, lastMessageAt: true }, }, }, orderBy: { joinedAt: "desc" }, take: 100, }); return { conversations: memberships.map((m) => m.conversation), }; } async getUser(userId) { const user = await this.prisma.user.findUnique({ where: { id: userId }, select: { id: true, name: true, email: true, image: true }, }); if (!user) return { user: null }; return { user }; } async statePayload(conversationId, workspaceId) { if (!workspaceId) { throw new common_1.BadRequestException("workspaceId is required"); } const conv = await this.prisma.conversation.findUnique({ where: { id: conversationId }, include: { members: true, messages: { orderBy: { seq: "desc" }, take: 200, }, }, }); if (!conv || conv.workspaceId !== workspaceId) { throw new common_1.UnauthorizedException("conversation unavailable"); } const recent = conv.messages .slice() .reverse() .map((m) => ({ id: m.id, userId: m.userId, conversationId: m.conversationId, body: m.body, eventType: m.eventType, metadata: m.metadata, seq: m.seq, at: m.createdAt.toISOString(), })); const members = {}; for (const m of conv.members) { members[m.userId] = { role: m.role, lastReadSeq: m.lastReadSeq, }; } return { members, recent, sequence: conv.messages[0]?.seq ?? 0 }; } async fetchRecent(conversationId, limit) { const rows = await this.prisma.conversationMessage.findMany({ where: { conversationId }, orderBy: { seq: "desc" }, take: limit, }); return rows .slice() .reverse() .map((m) => ({ id: m.id, userId: m.userId, conversationId: m.conversationId, body: m.body, eventType: m.eventType, metadata: m.metadata, seq: m.seq, at: m.createdAt.toISOString(), })); } async membersPayload(conversationId) { const conv = await this.prisma.conversation.findUnique({ where: { id: conversationId }, select: { members: true }, }); const members = {}; for (const m of conv?.members ?? []) { members[m.userId] = { role: m.role, lastReadSeq: m.lastReadSeq }; } return members; } parseLimit(raw) { if (raw === undefined || raw === "") return 50; const n = Number(raw); if (!Number.isFinite(n)) { throw new common_1.BadRequestException("limit must be a number"); } return Math.min(Math.max(Math.trunc(n), 1), 200); } }; exports.InternalRealtimeController = InternalRealtimeController; __decorate([ (0, common_1.Get)("conversations/:conversationId/state"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Query)("workspaceId")), __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "getState", null); __decorate([ (0, common_1.Post)("conversations/:conversationId/state"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Query)("workspaceId")), __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "postState", null); __decorate([ (0, common_1.Post)("conversations/:conversationId/events"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Body)()), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "postEvent", null); __decorate([ (0, common_1.Get)("conversations/:conversationId/recent"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Query)("limit")), __metadata("design:type", Function), __metadata("design:paramtypes", [String, String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "recent", null); __decorate([ (0, common_1.Post)("conversations/:conversationId/recent"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Body)()), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "recentPost", null); __decorate([ (0, common_1.Get)("conversations/:conversationId/members"), __param(0, (0, common_1.Param)("conversationId")), __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "getMembers", null); __decorate([ (0, common_1.Post)("conversations/:conversationId/members"), __param(0, (0, common_1.Param)("conversationId")), __param(1, (0, common_1.Body)()), __metadata("design:type", Function), __metadata("design:paramtypes", [String, Object]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "addMember", null); __decorate([ (0, common_1.Get)("users/:userId/conversations"), __param(0, (0, common_1.Param)("userId")), __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "userConversations", null); __decorate([ (0, common_1.Get)("users/:userId"), __param(0, (0, common_1.Param)("userId")), __metadata("design:type", Function), __metadata("design:paramtypes", [String]), __metadata("design:returntype", Promise) ], InternalRealtimeController.prototype, "getUser", null); exports.InternalRealtimeController = InternalRealtimeController = InternalRealtimeController_1 = __decorate([ (0, common_1.Controller)("internal/realtime"), (0, common_1.UseGuards)(internal_token_guard_1.InternalTokenGuard), __metadata("design:paramtypes", [prisma_service_1.PrismaService, chat_service_1.ChatService]) ], InternalRealtimeController); //# sourceMappingURL=internal-realtime.controller.js.map