fix(vpn): восстановление VPN-модуля после затирания chat/push fix'ом

- Добавлена ветка bot.type === 'vpn' в messenger.messages.routes.ts
- Добавлен полный модуль server/vpn/ (routes, service, db, bot, config)
- Подключены VPN-маршруты в server/routes/index.ts
- Добавлена раздача /apps статики и COPY в Dockerfile
- apps/ пока содержит .gitkeep и README; APK/IPA нужно добавить отдельно
This commit is contained in:
2026-07-17 09:32:43 +03:00
parent 0a5f4516ef
commit 586a591773
12 changed files with 985 additions and 58 deletions

View File

@@ -11,6 +11,7 @@ import { formatUserName } from "../utils/formatUserName";
import { notificationService } from "../services/notification.service";
import { handleAiBotDirectMessage } from "../services/ai-bot.service";
import { checkBotAccess } from "../services/ai-bot.service";
import { handleVpnBotDirectMessage } from "../vpn/vpn-bot.service";
export function registerMessengerMessageRoutes(router: Router): void {
// GET /api/messenger/conversations/:id/messages
@@ -249,71 +250,111 @@ export function registerMessengerMessageRoutes(router: Router): void {
.where(eq(bots.id, botId));
if (!bot || !bot.isActive) return;
if (bot.type !== 'ai_assistant') return;
const typedBot = bot as Bot;
if (!checkBotAccess(typedBot, req.user!)) {
const [denyMsg] = await db
.insert(conversationMessages)
.values({
if (bot.type === 'ai_assistant') {
const typedBot = bot as Bot;
if (!checkBotAccess(typedBot, req.user!)) {
const [denyMsg] = await db
.insert(conversationMessages)
.values({
conversationId: convId,
authorId: null,
botId: bot.id,
message: `⛔ У вас нет доступа к боту ${bot.name}`,
replyToId: null,
mentionedUserIds: null,
attachments: null,
})
.returning();
const enrichedDeny = await enrichMessage(denyMsg.id);
for (const { userId: memberId, orgId: memberOrgId } of members) {
eventBus.publishEvent({ type: "conv_message_created", data: { conversationId: convId, message: enrichedDeny }, organizationId: memberOrgId, userId: memberId });
}
notificationService.notifyMessengerMessage({
conversationId: convId,
authorId: null,
botId: bot.id,
message: `⛔ У вас нет доступа к боту ${bot.name}`,
replyToId: null,
mentionedUserIds: null,
attachments: null,
})
.returning();
const enrichedDeny = await enrichMessage(denyMsg.id);
for (const { userId: memberId, orgId: memberOrgId } of members) {
eventBus.publishEvent({ type: "conv_message_created", data: { conversationId: convId, message: enrichedDeny }, organizationId: memberOrgId, userId: memberId });
authorId: -1,
authorName: bot.name,
chatName: bot.name,
chatType: 'bot_direct',
messageText: `⛔ У вас нет доступа к боту ${bot.name}`,
mentionedUserIds: [],
memberOrgMap,
}).catch(err => console.error('[Bot Deny Notify] Error:', err));
return;
}
notificationService.notifyMessengerMessage({
await handleAiBotDirectMessage({
bot: typedBot,
conversationId: convId,
authorId: -1,
authorName: bot.name,
chatName: bot.name,
chatType: 'bot_direct',
messageText: `⛔ У вас нет доступа к боту ${bot.name}`,
mentionedUserIds: [],
memberOrgMap,
}).catch(err => console.error('[Bot Deny Notify] Error:', err));
return;
}
message: message.trim(),
user: req.user!,
organizationId: orgId,
});
await handleAiBotDirectMessage({
bot: typedBot,
conversationId: convId,
message: message.trim(),
user: req.user!,
organizationId: orgId,
});
const latestBotMsg = await db
.select({ id: conversationMessages.id })
.from(conversationMessages)
.where(and(
eq(conversationMessages.conversationId, convId),
eq(conversationMessages.botId, botId),
))
.orderBy(desc(conversationMessages.id))
.limit(1);
const latestBotMsg = await db
.select({ id: conversationMessages.id })
.from(conversationMessages)
.where(and(
eq(conversationMessages.conversationId, convId),
eq(conversationMessages.botId, botId),
))
.orderBy(desc(conversationMessages.id))
.limit(1);
if (latestBotMsg[0]) {
const enrichedBot = await enrichMessage(latestBotMsg[0].id);
for (const { userId: memberId, orgId: memberOrgId } of members) {
eventBus.publishEvent({ type: "conv_message_created", data: { conversationId: convId, message: enrichedBot }, organizationId: memberOrgId, userId: memberId });
if (latestBotMsg[0]) {
const enrichedBot = await enrichMessage(latestBotMsg[0].id);
for (const { userId: memberId, orgId: memberOrgId } of members) {
eventBus.publishEvent({ type: "conv_message_created", data: { conversationId: convId, message: enrichedBot }, organizationId: memberOrgId, userId: memberId });
}
notificationService.notifyMessengerMessage({
conversationId: convId,
authorId: -1,
authorName: bot.name,
chatName: bot.name,
chatType: 'bot_direct',
messageText: (enrichedBot as any).message ?? '',
mentionedUserIds: [],
memberOrgMap,
}).catch(err => console.error('[Bot Reply Notify] Error:', err));
}
notificationService.notifyMessengerMessage({
} else if (bot.type === 'vpn') {
const typedBot = bot as Bot;
if (!checkBotAccess(typedBot, req.user!)) {
const [denyMsg] = await db
.insert(conversationMessages)
.values({
conversationId: convId,
authorId: null,
botId: bot.id,
message: `⛔ У вас нет доступа к боту ${bot.name}`,
replyToId: null,
mentionedUserIds: null,
attachments: null,
})
.returning();
const enrichedDeny = await enrichMessage(denyMsg.id);
for (const { userId: memberId, orgId: memberOrgId } of members) {
eventBus.publishEvent({ type: "conv_message_created", data: { conversationId: convId, message: enrichedDeny }, organizationId: memberOrgId, userId: memberId });
}
notificationService.notifyMessengerMessage({
conversationId: convId,
authorId: -1,
authorName: bot.name,
chatName: bot.name,
chatType: 'bot_direct',
messageText: `⛔ У вас нет доступа к боту ${bot.name}`,
mentionedUserIds: [],
memberOrgMap,
}).catch(err => console.error('[VPN Bot Deny Notify] Error:', err));
return;
}
await handleVpnBotDirectMessage({
bot: typedBot,
conversationId: convId,
authorId: -1,
authorName: bot.name,
chatName: bot.name,
chatType: 'bot_direct',
messageText: (enrichedBot as any).message ?? '',
mentionedUserIds: [],
memberOrgMap,
}).catch(err => console.error('[Bot Reply Notify] Error:', err));
message: message.trim(),
user: req.user!,
organizationId: orgId,
});
}
});
} finally {