feat: Group chat and push notifications
- Add group chat tables (chat_groups, chat_group_members, group_messages) - Add push subscriptions table for web push notifications - Add group service, controller, routes - Add push service, controller, routes - Integrate push notifications with todos, messages, group messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
149
src/controllers/group.controller.js
Normal file
149
src/controllers/group.controller.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import * as groupService from '../services/group.service.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
export const createGroup = async (req, res, next) => {
|
||||
try {
|
||||
const { name, memberIds } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
const group = await groupService.createGroup(name, userId, memberIds);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: group,
|
||||
message: 'Skupina bola vytvorená',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Create group error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserGroups = async (req, res, next) => {
|
||||
try {
|
||||
const groups = await groupService.getUserGroups(req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: groups,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Get groups error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getGroupDetails = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const group = await groupService.getGroupDetails(groupId, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: group,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Get group details error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getGroupMessages = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const messages = await groupService.getGroupMessages(groupId, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: messages,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Get group messages error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const sendGroupMessage = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { content } = req.body;
|
||||
|
||||
const message = await groupService.sendGroupMessage(groupId, req.user.id, content);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: message,
|
||||
message: 'Správa odoslaná',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Send group message error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const addGroupMember = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { userId } = req.body;
|
||||
|
||||
await groupService.addGroupMember(groupId, userId, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Člen bol pridaný',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Add member error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const removeGroupMember = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId, userId } = req.params;
|
||||
|
||||
await groupService.removeGroupMember(groupId, userId, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Člen bol odstránený',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Remove member error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateGroupName = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { name } = req.body;
|
||||
|
||||
const group = await groupService.updateGroupName(groupId, name, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: group,
|
||||
message: 'Názov skupiny bol aktualizovaný',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Update group name error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteGroup = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
|
||||
await groupService.deleteGroup(groupId, req.user.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Skupina bola odstránená',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Delete group error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
117
src/controllers/push.controller.js
Normal file
117
src/controllers/push.controller.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import * as pushService from '../services/push.service.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
/**
|
||||
* Get VAPID public key
|
||||
*/
|
||||
export const getVapidPublicKey = (req, res) => {
|
||||
const publicKey = pushService.getVapidPublicKey();
|
||||
|
||||
if (!publicKey) {
|
||||
return res.status(503).json({
|
||||
success: false,
|
||||
message: 'Push notifikácie nie sú nakonfigurované',
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: { publicKey },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribe to push notifications
|
||||
*/
|
||||
export const subscribe = async (req, res, next) => {
|
||||
try {
|
||||
const { subscription } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
if (!subscription || !subscription.endpoint || !subscription.keys) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Neplatná subscription',
|
||||
});
|
||||
}
|
||||
|
||||
await pushService.saveSubscription(userId, subscription);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Push notifikácie aktivované',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Subscribe error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Unsubscribe from push notifications
|
||||
*/
|
||||
export const unsubscribe = async (req, res, next) => {
|
||||
try {
|
||||
const { endpoint } = req.body;
|
||||
const userId = req.user.id;
|
||||
|
||||
if (endpoint) {
|
||||
await pushService.removeSubscription(userId, endpoint);
|
||||
} else {
|
||||
await pushService.removeAllSubscriptions(userId);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Push notifikácie deaktivované',
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Unsubscribe error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check subscription status
|
||||
*/
|
||||
export const getStatus = async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
const hasSubscription = await pushService.hasActiveSubscription(userId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
enabled: hasSubscription,
|
||||
supported: !!pushService.getVapidPublicKey(),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Get status error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test push notification (for debugging)
|
||||
*/
|
||||
export const testPush = async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.user.id;
|
||||
|
||||
const result = await pushService.sendPushNotification(userId, {
|
||||
title: 'Test notifikácie',
|
||||
body: 'Toto je testovacia push notifikácia z CRM',
|
||||
icon: '/icon-192.png',
|
||||
data: { url: '/' },
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Test push error', error);
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
@@ -148,7 +148,7 @@ export const updateTodo = async (req, res, next) => {
|
||||
// Get old todo for audit
|
||||
const oldTodo = await todoService.getTodoById(todoId);
|
||||
|
||||
const todo = await todoService.updateTodo(todoId, data);
|
||||
const todo = await todoService.updateTodo(todoId, data, userId);
|
||||
|
||||
// Log audit event
|
||||
await logTodoUpdated(
|
||||
|
||||
Reference in New Issue
Block a user