* Add next suggestions and remove console logs * Add pomodoro timer * Add pomodoro switch in settings * Fix pomodoro setting * Add timezones to settings * Fix an issue with password reset * Cleanup * Sort tags alphabetically * Clean up today's view * Add an indicator for repeatedly added to today * Refactor tags * Add due date today item * Move recurrence to the subtitle area * Fix today layout * Add a badge to Inbox items * Move inbox badge to sidebar * Add quotes and progress bar * Add translations for quotes * Fix test issues * Add helper script for docker local * Set up overdue tasks * Add linux/arm/v7 build to deploy script * Add linux/arm/v7 build to deploy script pt2 * Fix an issue with helmet and SSL * Add volume db persistence * Fix cog icon issues
334 lines
No EOL
11 KiB
JavaScript
334 lines
No EOL
11 KiB
JavaScript
const express = require('express');
|
|
const { User } = require('../models');
|
|
const taskSummaryService = require('../services/taskSummaryService');
|
|
const router = express.Router();
|
|
|
|
const VALID_FREQUENCIES = ['daily', 'weekdays', 'weekly', '1h', '2h', '4h', '8h', '12h'];
|
|
|
|
// GET /api/profile
|
|
router.get('/profile', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId, {
|
|
attributes: [
|
|
'id', 'email', 'appearance', 'language', 'timezone',
|
|
'avatar_image', 'telegram_bot_token', 'telegram_chat_id',
|
|
'task_summary_enabled', 'task_summary_frequency', 'task_intelligence_enabled',
|
|
'auto_suggest_next_actions_enabled', 'pomodoro_enabled', 'today_settings'
|
|
]
|
|
});
|
|
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'Profile not found.' });
|
|
}
|
|
|
|
// Parse today_settings if it's a string
|
|
if (user.today_settings && typeof user.today_settings === 'string') {
|
|
try {
|
|
user.today_settings = JSON.parse(user.today_settings);
|
|
} catch (error) {
|
|
console.error('Error parsing today_settings:', error);
|
|
user.today_settings = null;
|
|
}
|
|
}
|
|
|
|
res.json(user);
|
|
} catch (error) {
|
|
console.error('Error fetching profile:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// PATCH /api/profile
|
|
router.patch('/profile', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'Profile not found.' });
|
|
}
|
|
|
|
const { appearance, language, timezone, avatar_image, telegram_bot_token, task_intelligence_enabled, task_summary_enabled, task_summary_frequency, auto_suggest_next_actions_enabled, pomodoro_enabled, currentPassword, newPassword } = req.body;
|
|
|
|
const allowedUpdates = {};
|
|
if (appearance !== undefined) allowedUpdates.appearance = appearance;
|
|
if (language !== undefined) allowedUpdates.language = language;
|
|
if (timezone !== undefined) allowedUpdates.timezone = timezone;
|
|
if (avatar_image !== undefined) allowedUpdates.avatar_image = avatar_image;
|
|
if (telegram_bot_token !== undefined) allowedUpdates.telegram_bot_token = telegram_bot_token;
|
|
if (task_intelligence_enabled !== undefined) allowedUpdates.task_intelligence_enabled = task_intelligence_enabled;
|
|
if (task_summary_enabled !== undefined) allowedUpdates.task_summary_enabled = task_summary_enabled;
|
|
if (task_summary_frequency !== undefined) allowedUpdates.task_summary_frequency = task_summary_frequency;
|
|
if (auto_suggest_next_actions_enabled !== undefined) allowedUpdates.auto_suggest_next_actions_enabled = auto_suggest_next_actions_enabled;
|
|
if (pomodoro_enabled !== undefined) allowedUpdates.pomodoro_enabled = pomodoro_enabled;
|
|
|
|
// Handle password change if provided
|
|
if (currentPassword && newPassword) {
|
|
if (newPassword.length < 6) {
|
|
return res.status(400).json({
|
|
field: 'newPassword',
|
|
error: 'Password must be at least 6 characters'
|
|
});
|
|
}
|
|
|
|
// Verify current password
|
|
const isValidPassword = await User.checkPassword(currentPassword, user.password_digest);
|
|
if (!isValidPassword) {
|
|
return res.status(400).json({
|
|
field: 'currentPassword',
|
|
error: 'Current password is incorrect'
|
|
});
|
|
}
|
|
|
|
// Hash and include new password in updates
|
|
const hashedNewPassword = await User.hashPassword(newPassword);
|
|
allowedUpdates.password_digest = hashedNewPassword;
|
|
}
|
|
|
|
await user.update(allowedUpdates);
|
|
|
|
// Return updated user with limited fields
|
|
const updatedUser = await User.findByPk(user.id, {
|
|
attributes: ['id', 'email', 'appearance', 'language', 'timezone', 'avatar_image', 'telegram_bot_token', 'telegram_chat_id', 'task_intelligence_enabled', 'task_summary_enabled', 'task_summary_frequency', 'auto_suggest_next_actions_enabled', 'pomodoro_enabled']
|
|
});
|
|
|
|
res.json(updatedUser);
|
|
} catch (error) {
|
|
console.error('Error updating profile:', error);
|
|
res.status(400).json({
|
|
error: 'Failed to update profile.',
|
|
details: error.errors ? error.errors.map(e => e.message) : [error.message]
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/profile/change-password
|
|
router.post('/profile/change-password', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const { currentPassword, newPassword } = req.body;
|
|
|
|
if (!currentPassword || !newPassword) {
|
|
return res.status(400).json({ error: 'Current password and new password are required' });
|
|
}
|
|
|
|
if (newPassword.length < 6) {
|
|
return res.status(400).json({
|
|
field: 'newPassword',
|
|
error: 'Password must be at least 6 characters'
|
|
});
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found' });
|
|
}
|
|
|
|
// Verify current password
|
|
const isValidPassword = await User.checkPassword(currentPassword, user.password_digest);
|
|
if (!isValidPassword) {
|
|
return res.status(400).json({
|
|
field: 'currentPassword',
|
|
error: 'Current password is incorrect'
|
|
});
|
|
}
|
|
|
|
// Hash and update new password
|
|
const hashedNewPassword = await User.hashPassword(newPassword);
|
|
await user.update({ password_digest: hashedNewPassword });
|
|
|
|
res.json({ message: 'Password changed successfully' });
|
|
} catch (error) {
|
|
console.error('Error changing password:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// POST /api/profile/task-summary/toggle
|
|
router.post('/profile/task-summary/toggle', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found.' });
|
|
}
|
|
|
|
const enabled = !user.task_summary_enabled;
|
|
|
|
await user.update({ task_summary_enabled: enabled });
|
|
|
|
// Note: Telegram integration would need to be implemented separately
|
|
const message = enabled
|
|
? 'Task summary notifications have been enabled.'
|
|
: 'Task summary notifications have been disabled.';
|
|
|
|
res.json({
|
|
success: true,
|
|
enabled: enabled,
|
|
message: message
|
|
});
|
|
} catch (error) {
|
|
console.error('Error toggling task summary:', error);
|
|
res.status(400).json({
|
|
error: 'Failed to update task summary settings.',
|
|
details: error.errors ? error.errors.map(e => e.message) : [error.message]
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/profile/task-summary/frequency
|
|
router.post('/profile/task-summary/frequency', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const { frequency } = req.body;
|
|
|
|
if (!frequency) {
|
|
return res.status(400).json({ error: 'Frequency is required.' });
|
|
}
|
|
|
|
if (!VALID_FREQUENCIES.includes(frequency)) {
|
|
return res.status(400).json({ error: 'Invalid frequency value.' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found.' });
|
|
}
|
|
|
|
await user.update({ task_summary_frequency: frequency });
|
|
|
|
res.json({
|
|
success: true,
|
|
frequency: frequency,
|
|
message: `Task summary frequency has been set to ${frequency}.`
|
|
});
|
|
} catch (error) {
|
|
console.error('Error updating task summary frequency:', error);
|
|
res.status(400).json({
|
|
error: 'Failed to update task summary frequency.',
|
|
details: error.errors ? error.errors.map(e => e.message) : [error.message]
|
|
});
|
|
}
|
|
});
|
|
|
|
// POST /api/profile/task-summary/send-now
|
|
router.post('/profile/task-summary/send-now', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found.' });
|
|
}
|
|
|
|
if (!user.telegram_bot_token || !user.telegram_chat_id) {
|
|
return res.status(400).json({ error: 'Telegram bot is not properly configured.' });
|
|
}
|
|
|
|
// Send the task summary
|
|
const success = await taskSummaryService.sendSummaryToUser(user.id);
|
|
|
|
if (success) {
|
|
res.json({
|
|
success: true,
|
|
message: 'Task summary was sent to your Telegram.'
|
|
});
|
|
} else {
|
|
res.status(400).json({ error: 'Failed to send message to Telegram.' });
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending task summary:', error);
|
|
res.status(400).json({
|
|
error: 'Error sending message to Telegram.',
|
|
details: error.message
|
|
});
|
|
}
|
|
});
|
|
|
|
// GET /api/profile/task-summary/status
|
|
router.get('/profile/task-summary/status', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found.' });
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
enabled: user.task_summary_enabled,
|
|
frequency: user.task_summary_frequency,
|
|
last_run: user.task_summary_last_run,
|
|
next_run: user.task_summary_next_run
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching task summary status:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
// PUT /api/profile/today-settings
|
|
router.put('/profile/today-settings', async (req, res) => {
|
|
try {
|
|
if (!req.session || !req.session.userId) {
|
|
return res.status(401).json({ error: 'Authentication required' });
|
|
}
|
|
|
|
const user = await User.findByPk(req.session.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found.' });
|
|
}
|
|
|
|
const {
|
|
showMetrics,
|
|
showProductivity,
|
|
showIntelligence,
|
|
showDueToday,
|
|
showCompleted,
|
|
showProgressBar,
|
|
showDailyQuote
|
|
} = req.body;
|
|
|
|
const todaySettings = {
|
|
showMetrics: showMetrics !== undefined ? showMetrics : user.today_settings?.showMetrics || false,
|
|
showProductivity: showProductivity !== undefined ? showProductivity : user.today_settings?.showProductivity || false,
|
|
showIntelligence: showIntelligence !== undefined ? showIntelligence : user.today_settings?.showIntelligence || false,
|
|
showDueToday: showDueToday !== undefined ? showDueToday : user.today_settings?.showDueToday || true,
|
|
showCompleted: showCompleted !== undefined ? showCompleted : user.today_settings?.showCompleted || true,
|
|
showProgressBar: true, // Always enabled - ignore any attempts to disable it
|
|
showDailyQuote: showDailyQuote !== undefined ? showDailyQuote : user.today_settings?.showDailyQuote || true
|
|
};
|
|
|
|
await user.update({ today_settings: todaySettings });
|
|
|
|
res.json({
|
|
success: true,
|
|
today_settings: todaySettings
|
|
});
|
|
} catch (error) {
|
|
console.error('Error updating today settings:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
});
|
|
|
|
module.exports = router; |