Software: Apache. PHP/8.1.30 uname -a: Linux server1.tuhinhossain.com 5.15.0-163-generic #173-Ubuntu SMP Tue Oct 14 17:51:00 UTC uid=1002(picotech) gid=1003(picotech) groups=1003(picotech),0(root) Safe-mode: OFF (not secure) /home/picotech/domains/note.picotech.app/public_html/src/controllers/ drwxr-xr-x | |
| Viewing file: Select action/file-type: const { Meeting, Note, Recording, Transcript, Summary, User } = require('../models');
const { AppError, catchAsync } = require('../middleware/errorHandler');
const { Op } = require('sequelize');
// Create new meeting (auto-triggered for live note sessions)
const createMeeting = catchAsync(async (req, res) => {
const {
title,
description,
start_time,
end_time,
attendees,
location,
meeting_url,
tags,
labels,
calendar_source = 'manual',
external_id,
} = req.body;
const userId = req.user.id;
// Auto-generate title if not provided (for live note sessions)
const meetingTitle = title || `Live Note Session - ${new Date().toLocaleDateString()}`;
const meeting = await Meeting.create({
title: meetingTitle,
description,
start_time: start_time || new Date(),
end_time,
user_id: userId,
attendees: attendees || [],
location,
meeting_url,
tags: tags || [],
labels: labels || [],
calendar_source,
external_id,
status: 'active',
});
// Include user information in response
const meetingWithUser = await Meeting.findByPk(meeting.id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name', 'email'],
},
],
});
// Update the Redux state with the new meeting
// This will be handled by the React Native app when it starts a live note session
res.status(201).json({
success: true,
message: 'Meeting created successfully',
data: { meeting: meetingWithUser },
});
});
// Auto-create meeting when a user starts a live note session
const startLiveNoteSession = catchAsync(async (req, res) => {
const userId = req.user.id;
// Auto-generate title for live note session
const meetingTitle = `Live Note Session - ${new Date().toLocaleDateString()}`;
const meeting = await Meeting.create({
title: meetingTitle,
description: 'Live note session started',
start_time: new Date(),
end_time: null,
user_id: userId,
attendees: [],
location: null,
meeting_url: null,
tags: [],
calendar_source: 'manual',
external_id: null,
status: 'active',
});
res.status(201).json({
success: true,
message: 'Live note session started successfully',
data: { meeting },
});
});
// Get all meetings for the authenticated user
const getMeetings = catchAsync(async (req, res) => {
const userId = req.user.id;
const {
page = 1,
limit = 20,
status,
start_date,
end_date,
search,
sort_by = 'start_time',
sort_order = 'DESC',
} = req.query;
const offset = (page - 1) * limit;
const whereClause = { user_id: userId };
// Add filters
if (status) {
whereClause.status = status;
}
if (start_date && end_date) {
whereClause.start_time = {
[Op.between]: [new Date(start_date), new Date(end_date)],
};
} else if (start_date) {
whereClause.start_time = {
[Op.gte]: new Date(start_date),
};
} else if (end_date) {
whereClause.start_time = {
[Op.lte]: new Date(end_date),
};
}
if (search) {
whereClause[Op.or] = [
{ title: { [Op.like]: `%${search}%` } },
{ description: { [Op.like]: `%${search}%` } },
];
}
const { count, rows: meetings } = await Meeting.findAndCountAll({
where: whereClause,
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name', 'email'],
},
{
model: Note,
as: 'notes',
attributes: ['id', 'title', 'status', 'created_at'],
},
{
model: Recording,
as: 'recordings',
attributes: ['id', 'file_name', 'duration', 'status'],
},
{
model: Summary,
as: 'summary',
attributes: ['id', 'overview', 'status', 'created_at'],
},
],
order: [[sort_by, sort_order.toUpperCase()]],
limit: parseInt(limit),
offset: parseInt(offset),
});
const totalPages = Math.ceil(count / limit);
res.json({
success: true,
data: {
meetings,
pagination: {
current_page: parseInt(page),
total_pages: totalPages,
total_items: count,
items_per_page: parseInt(limit),
has_next: page < totalPages,
has_prev: page > 1,
},
},
});
});
// Get single meeting by ID
const getMeeting = catchAsync(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const meeting = await Meeting.findOne({
where: { id, user_id: userId },
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name', 'email'],
},
{
model: Note,
as: 'notes',
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name'],
},
],
order: [['created_at', 'ASC']],
},
{
model: Recording,
as: 'recordings',
include: [
{
model: Transcript,
as: 'transcript',
attributes: ['id', 'content', 'confidence', 'status'],
},
],
order: [['timestamp', 'ASC']],
},
{
model: Summary,
as: 'summary',
},
],
});
if (!meeting) {
throw new AppError('Meeting not found', 404, 'MEETING_NOT_FOUND');
}
res.json({
success: true,
data: { meeting },
});
});
// Update meeting
const updateMeeting = catchAsync(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const {
title,
description,
start_time,
end_time,
status,
attendees,
location,
meeting_url,
tags,
labels,
} = req.body;
const meeting = await Meeting.findOne({
where: { id, user_id: userId },
});
if (!meeting) {
throw new AppError('Meeting not found', 404, 'MEETING_NOT_FOUND');
}
// Update fields
if (title !== undefined) meeting.title = title;
if (description !== undefined) meeting.description = description;
if (start_time !== undefined) meeting.start_time = new Date(start_time);
if (end_time !== undefined) meeting.end_time = end_time ? new Date(end_time) : null;
if (status !== undefined) meeting.status = status;
if (attendees !== undefined) meeting.attendees = attendees;
if (location !== undefined) meeting.location = location;
if (meeting_url !== undefined) meeting.meeting_url = meeting_url;
if (tags !== undefined) meeting.tags = tags;
if (labels !== undefined) meeting.labels = labels;
// Auto-set end_time when completing meeting
if (status === 'completed' && !meeting.end_time) {
meeting.end_time = new Date();
}
await meeting.save();
// Fetch updated meeting with associations
const updatedMeeting = await Meeting.findByPk(meeting.id, {
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name', 'email'],
},
],
});
res.json({
success: true,
message: 'Meeting updated successfully',
data: { meeting: updatedMeeting },
});
});
// Delete meeting
const deleteMeeting = catchAsync(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const meeting = await Meeting.findOne({
where: { id, user_id: userId },
});
if (!meeting) {
throw new AppError('Meeting not found', 404, 'MEETING_NOT_FOUND');
}
await meeting.destroy();
res.json({
success: true,
message: 'Meeting deleted successfully',
});
});
// Complete meeting (end live session)
const completeMeeting = catchAsync(async (req, res) => {
const { id } = req.params;
const userId = req.user.id;
const meeting = await Meeting.findOne({
where: { id, user_id: userId },
});
if (!meeting) {
throw new AppError('Meeting not found', 404, 'MEETING_NOT_FOUND');
}
if (meeting.status === 'completed') {
throw new AppError('Meeting is already completed', 400, 'MEETING_ALREADY_COMPLETED');
}
await meeting.complete();
res.json({
success: true,
message: 'Meeting completed successfully',
data: { meeting },
});
});
// Get active meetings for user
const getActiveMeetings = catchAsync(async (req, res) => {
const userId = req.user.id;
const meetings = await Meeting.findActiveByUser(userId);
res.json({
success: true,
data: { meetings },
});
});
// Get meeting statistics
const getMeetingStats = catchAsync(async (req, res) => {
const userId = req.user.id;
const { start_date, end_date } = req.query;
const whereClause = { user_id: userId };
if (start_date && end_date) {
whereClause.start_time = {
[Op.between]: [new Date(start_date), new Date(end_date)],
};
}
const [
totalMeetings,
activeMeetings,
completedMeetings,
totalNotes,
totalRecordings,
totalSummaries,
] = await Promise.all([
Meeting.count({ where: whereClause }),
Meeting.count({ where: { ...whereClause, status: 'active' } }),
Meeting.count({ where: { ...whereClause, status: 'completed' } }),
Note.count({
include: [{ model: Meeting, where: whereClause, attributes: [] }],
}),
Recording.count({
include: [{ model: Meeting, where: whereClause, attributes: [] }],
}),
Summary.count({
include: [{ model: Meeting, where: whereClause, attributes: [] }],
}),
]);
// Calculate average meeting duration for completed meetings
const completedMeetingsWithDuration = await Meeting.findAll({
where: {
...whereClause,
status: 'completed',
end_time: { [Op.not]: null },
},
attributes: ['start_time', 'end_time'],
});
let averageDuration = 0;
if (completedMeetingsWithDuration.length > 0) {
const totalDuration = completedMeetingsWithDuration.reduce((sum, meeting) => {
const duration = new Date(meeting.end_time) - new Date(meeting.start_time);
return sum + duration;
}, 0);
averageDuration = Math.floor(totalDuration / completedMeetingsWithDuration.length / (1000 * 60)); // in minutes
}
res.json({
success: true,
data: {
stats: {
total_meetings: totalMeetings,
active_meetings: activeMeetings,
completed_meetings: completedMeetings,
total_notes: totalNotes,
total_recordings: totalRecordings,
total_summaries: totalSummaries,
average_duration_minutes: averageDuration,
},
},
});
});
// Search meetings
const searchMeetings = catchAsync(async (req, res) => {
const userId = req.user.id;
const { q: query, page = 1, limit = 20 } = req.query;
if (!query) {
throw new AppError('Search query is required', 400, 'MISSING_SEARCH_QUERY');
}
const offset = (page - 1) * limit;
const { count, rows: meetings } = await Meeting.findAndCountAll({
where: {
user_id: userId,
[Op.or]: [
{ title: { [Op.like]: `%${query}%` } },
{ description: { [Op.like]: `%${query}%` } },
],
},
include: [
{
model: User,
as: 'user',
attributes: ['id', 'name', 'email'],
},
],
order: [['start_time', 'DESC']],
limit: parseInt(limit),
offset: parseInt(offset),
});
const totalPages = Math.ceil(count / limit);
res.json({
success: true,
data: {
meetings,
pagination: {
current_page: parseInt(page),
total_pages: totalPages,
total_items: count,
items_per_page: parseInt(limit),
},
},
});
});
// Sync calendar meetings
const syncCalendarMeetings = catchAsync(async (req, res) => {
const userId = req.user.id;
const { calendarEvents } = req.body;
if (!Array.isArray(calendarEvents)) {
throw new AppError('Calendar events must be an array', 400, 'INVALID_CALENDAR_EVENTS');
}
const syncedMeetings = [];
const skippedMeetings = [];
for (const event of calendarEvents) {
try {
// Check if meeting already exists with this external_id
const existingMeeting = await Meeting.findOne({
where: {
user_id: userId,
external_id: event.id,
calendar_source: event.calendarSource || 'google',
},
});
if (existingMeeting) {
// Skip existing meeting - do not update
skippedMeetings.push({
eventId: event.id,
title: event.title,
reason: 'Already synced',
});
} else {
// Create new meeting from calendar event
const newMeeting = await Meeting.create({
title: event.title,
description: event.description || '',
start_time: new Date(event.startDate),
end_time: event.endDate ? new Date(event.endDate) : null,
user_id: userId,
attendees: event.attendees || [],
location: event.location || null,
meeting_url: event.meetingUrl || null,
tags: event.tags || [],
labels: event.labels || [],
calendar_source: event.calendarSource || 'google',
external_id: event.id,
status: 'active',
});
syncedMeetings.push(newMeeting);
}
} catch (error) {
console.error('Error syncing calendar event:', event.id, error);
skippedMeetings.push({
eventId: event.id,
title: event.title,
error: error.message,
});
}
}
res.json({
success: true,
message: `Synced ${syncedMeetings.length} calendar meetings`,
data: {
synced_count: syncedMeetings.length,
skipped_count: skippedMeetings.length,
synced_meetings: syncedMeetings,
skipped_meetings: skippedMeetings,
},
});
});
// Get calendar meetings for sync
const getCalendarMeetings = catchAsync(async (req, res) => {
const userId = req.user.id;
const { calendar_source, start_date, end_date } = req.query;
const whereClause = {
user_id: userId,
calendar_source: { [Op.ne]: 'manual' }, // Only calendar-sourced meetings
};
if (calendar_source) {
whereClause.calendar_source = calendar_source;
}
if (start_date && end_date) {
whereClause.start_time = {
[Op.between]: [new Date(start_date), new Date(end_date)],
};
}
const meetings = await Meeting.findAll({
where: whereClause,
order: [['start_time', 'ASC']],
});
res.json({
success: true,
data: { meetings },
});
});
module.exports = {
createMeeting,
getMeetings,
getMeeting,
updateMeeting,
deleteMeeting,
completeMeeting,
getActiveMeetings,
getMeetingStats,
searchMeetings,
syncCalendarMeetings,
getCalendarMeetings,
}; |
:: Command execute :: | |
--[ c99shell v. 2.5 [PHP 8 Update] [24.05.2025] | Generation time: 0.0047 ]-- |