Overhaul settings and implement user preferences. Also implements dark theme toggle as part of the user settings.
This commit is contained in:
parent
b84f0907a8
commit
727746030c
17 changed files with 760 additions and 378 deletions
|
@ -1,5 +1,6 @@
|
|||
import { Router } from 'express';
|
||||
import prisma from '../database.js';
|
||||
import { handlePrismaError } from '../database.js';
|
||||
import PDFDocument from 'pdfkit';
|
||||
import { join } from 'path';
|
||||
import { generateTodaysSummary } from '../services/mantisSummarizer.js';
|
||||
|
@ -9,18 +10,6 @@ const router = Router();
|
|||
|
||||
const __dirname = new URL('.', import.meta.url).pathname.replace(/\/$/, '');
|
||||
|
||||
// Helper function for consistent error handling
|
||||
const handlePrismaError = (res, err, context) =>
|
||||
{
|
||||
console.error(`Error ${context}:`, err.message);
|
||||
// Basic error handling, can be expanded (e.g., check for Prisma-specific error codes)
|
||||
if (err.code === 'P2025')
|
||||
{ // Prisma code for record not found
|
||||
return res.status(404).json({ error: `${context}: Record not found` });
|
||||
}
|
||||
res.status(500).json({ error: `Failed to ${context}: ${err.message}` });
|
||||
};
|
||||
|
||||
// --- Forms API --- //
|
||||
|
||||
// GET /api/forms - List all forms
|
||||
|
@ -647,59 +636,4 @@ router.post('/mantis-summaries/generate', async(req, res) =>
|
|||
}
|
||||
});
|
||||
|
||||
// --- Settings API --- //
|
||||
|
||||
// GET /api/settings/:key - Get a specific setting value
|
||||
router.get('/settings/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
try
|
||||
{
|
||||
const setting = await prisma.setting.findUnique({
|
||||
where: { key: key },
|
||||
select: { value: true }
|
||||
});
|
||||
|
||||
if (setting !== null)
|
||||
{
|
||||
res.json({ key, value: setting.value });
|
||||
}
|
||||
else
|
||||
{
|
||||
res.json({ key, value: '' }); // Return empty value if not found
|
||||
}
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `fetch setting '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/settings/:key - Update or create a specific setting
|
||||
router.put('/settings/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
const { value } = req.body;
|
||||
|
||||
if (typeof value === 'undefined')
|
||||
{
|
||||
return res.status(400).json({ error: 'Setting value is required in the request body' });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const upsertedSetting = await prisma.setting.upsert({
|
||||
where: { key: key },
|
||||
update: { value: String(value) },
|
||||
create: { key: key, value: String(value) },
|
||||
select: { key: true, value: true } // Select to return the updated/created value
|
||||
});
|
||||
res.status(200).json(upsertedSetting);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `update setting '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -48,7 +48,8 @@ async function getAuthenticatorByCredentialID(credentialID)
|
|||
// Generate Registration Options
|
||||
router.post('/generate-registration-options', async(req, res) =>
|
||||
{
|
||||
const { username } = req.body;
|
||||
// Destructure username, email, and fullName from the request body
|
||||
const { username, email, fullName } = req.body;
|
||||
|
||||
if (!username)
|
||||
{
|
||||
|
@ -59,13 +60,18 @@ router.post('/generate-registration-options', async(req, res) =>
|
|||
{
|
||||
let user = await getUserByUsername(username);
|
||||
|
||||
// If user doesn't exist, create one
|
||||
// If user doesn't exist, create one with the provided details
|
||||
if (!user)
|
||||
{
|
||||
const userData = { username };
|
||||
if (email) userData.email = email; // Add email if provided
|
||||
if (fullName) userData.fullName = fullName; // Add fullName if provided
|
||||
|
||||
user = await prisma.user.create({
|
||||
data: { username },
|
||||
data: userData,
|
||||
});
|
||||
}
|
||||
// ... rest of the existing logic ...
|
||||
|
||||
const userAuthenticators = await getUserAuthenticators(user.id);
|
||||
|
||||
|
@ -107,6 +113,11 @@ router.post('/generate-registration-options', async(req, res) =>
|
|||
catch (error)
|
||||
{
|
||||
console.error('Registration options error:', error);
|
||||
// Handle potential Prisma unique constraint errors (e.g., email already exists)
|
||||
if (error.code === 'P2002' && error.meta?.target?.includes('email'))
|
||||
{
|
||||
return res.status(409).json({ error: 'Email address is already in use.' });
|
||||
}
|
||||
res.status(500).json({ error: 'Failed to generate registration options' });
|
||||
}
|
||||
});
|
||||
|
@ -190,12 +201,18 @@ router.post('/verify-registration', async(req, res) =>
|
|||
}
|
||||
else
|
||||
{
|
||||
// This else block was previously misplaced before the if block
|
||||
res.status(400).json({ error: 'Registration verification failed' });
|
||||
}
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error('Registration verification error:', error);
|
||||
// Handle potential Prisma unique constraint errors (e.g., email already exists)
|
||||
if (error.code === 'P2002' && error.meta?.target?.includes('email'))
|
||||
{
|
||||
return res.status(409).json({ error: 'Email address is already in use.' });
|
||||
}
|
||||
challengeStore.delete(userId); // Clean up challenge on error
|
||||
delete req.session.userId;
|
||||
res.status(500).json({ error: 'Failed to verify registration', details: error.message });
|
||||
|
@ -437,7 +454,8 @@ router.get('/status', async(req, res) =>
|
|||
{});
|
||||
return res.status(401).json({ status: 'unauthenticated' });
|
||||
}
|
||||
return res.json({ status: 'authenticated', user: { id: user.id, username: user.username, email: user.email } });
|
||||
// Include email and fullName in the response
|
||||
return res.json({ status: 'authenticated', user: { id: user.id, username: user.username, email: user.email, fullName: user.fullName } });
|
||||
}
|
||||
res.json({ status: 'unauthenticated' });
|
||||
});
|
||||
|
|
51
src-server/routes/settings.js
Normal file
51
src-server/routes/settings.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import express from 'express';
|
||||
import { getSetting, setSetting, getUserPreference, setUserPreference } from '../utils/settings.js';
|
||||
import { handlePrismaError } from '../database.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
|
||||
// --- Settings API --- //
|
||||
|
||||
// GET /api/settings/:key - Get a specific setting value
|
||||
router.get('/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
try
|
||||
{
|
||||
const setting = await getSetting(key);
|
||||
if (!setting)
|
||||
{
|
||||
return res.status(404).json({ error: `Setting '${key}' not found` });
|
||||
}
|
||||
res.status(200).json(setting);
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `fetch setting '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/settings/:key - Update or create a specific setting
|
||||
router.put('/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
const { value } = req.body;
|
||||
|
||||
if (typeof value === 'undefined')
|
||||
{
|
||||
return res.status(400).json({ error: 'Setting value is required in the request body' });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await setSetting(key, value);
|
||||
res.status(200).json({ message: `Setting '${key}' updated successfully` });
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `update setting '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
57
src-server/routes/userPreferences.js
Normal file
57
src-server/routes/userPreferences.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import express from 'express';
|
||||
|
||||
import { getUserPreference, setUserPreference } from '../utils/settings.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// GET /api/user-preferences/:key - Get a user preference for the logged-in user
|
||||
router.get('/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
const userId = req.session?.loggedInUserId;
|
||||
if (!userId)
|
||||
{
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
try
|
||||
{
|
||||
const value = await getUserPreference(userId, key);
|
||||
if (typeof value === 'undefined' || value === null)
|
||||
{
|
||||
return res.status(404).json({ error: `Preference '${key}' not found for user` });
|
||||
}
|
||||
res.status(200).json({ key, value });
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `fetch user preference '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/user-preferences/:key - Set a user preference for the logged-in user
|
||||
router.put('/:key', async(req, res) =>
|
||||
{
|
||||
const { key } = req.params;
|
||||
const { value } = req.body;
|
||||
const userId = req.session?.loggedInUserId;
|
||||
if (!userId)
|
||||
{
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
if (typeof value === 'undefined')
|
||||
{
|
||||
return res.status(400).json({ error: 'Preference value is required in the request body' });
|
||||
}
|
||||
try
|
||||
{
|
||||
await setUserPreference(userId, key, value);
|
||||
res.status(200).json({ message: `Preference '${key}' updated for user` });
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
handlePrismaError(res, err, `update user preference '${key}'`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default router;
|
Loading…
Add table
Add a link
Reference in a new issue