deckhearth/api/user/preferences.js

202 lines
6.5 KiB
JavaScript
Raw Normal View History

const { Pool } = require('pg');
const jwt = require('jsonwebtoken');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
});
// Verify JWT token and extract user info
function verifyAuth(req) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new Error('No authorization token provided');
}
const token = authHeader.substring(7);
const jwtSecret = process.env.JWT_SECRET || 'fallback-secret-change-in-production';
try {
const decoded = jwt.verify(token, jwtSecret);
return decoded;
} catch (error) {
throw new Error('Invalid or expired token');
}
}
export default async function handler(req, res) {
const client = await pool.connect();
try {
const user = verifyAuth(req);
if (req.method === 'GET') {
// Get user preferences
const result = await client.query(`
SELECT
default_view,
items_per_page,
enable_animations,
enable_ocr,
theme,
privacy_settings,
ocr_settings,
created_at,
updated_at
FROM user_preferences
WHERE user_id = $1
`, [user.userId]);
if (result.rows.length === 0) {
// Create default preferences if none exist
const defaultPrefs = {
default_view: 'card',
items_per_page: 20,
enable_animations: true,
enable_ocr: true,
theme: 'light',
privacy_settings: { collections_public: false, decks_public: false },
ocr_settings: {
preferred_service: 'openai',
openai_api_key: '',
ollama_url: 'http://localhost:11434',
auto_add_to_collection: false,
confidence_threshold: 80
}
};
await client.query(`
INSERT INTO user_preferences (
user_id, default_view, items_per_page, enable_animations,
enable_ocr, theme, privacy_settings, ocr_settings
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`, [
user.userId,
defaultPrefs.default_view,
defaultPrefs.items_per_page,
defaultPrefs.enable_animations,
defaultPrefs.enable_ocr,
defaultPrefs.theme,
JSON.stringify(defaultPrefs.privacy_settings),
JSON.stringify(defaultPrefs.ocr_settings)
]);
return res.status(200).json({
success: true,
preferences: defaultPrefs
});
}
const preferences = result.rows[0];
res.status(200).json({
success: true,
preferences: {
defaultView: preferences.default_view,
itemsPerPage: preferences.items_per_page,
enableAnimations: preferences.enable_animations,
enableOcr: preferences.enable_ocr,
theme: preferences.theme,
privacySettings: preferences.privacy_settings,
ocrSettings: preferences.ocr_settings || {
preferred_service: 'openai',
openai_api_key: '',
ollama_url: 'http://localhost:11434',
auto_add_to_collection: false,
confidence_threshold: 80
},
createdAt: preferences.created_at,
updatedAt: preferences.updated_at
}
});
} else if (req.method === 'PUT') {
// Update user preferences
const {
defaultView,
itemsPerPage,
enableAnimations,
enableOcr,
theme,
privacySettings,
ocrSettings
} = req.body;
// Validate OCR settings if provided
if (ocrSettings) {
const allowedServices = ['openai', 'ollama'];
if (ocrSettings.preferred_service && !allowedServices.includes(ocrSettings.preferred_service)) {
return res.status(400).json({
error: 'Invalid OCR service. Must be "openai" or "ollama"'
});
}
if (ocrSettings.confidence_threshold && (ocrSettings.confidence_threshold < 0 || ocrSettings.confidence_threshold > 100)) {
return res.status(400).json({
error: 'Confidence threshold must be between 0 and 100'
});
}
}
// Update preferences (upsert)
const result = await client.query(`
INSERT INTO user_preferences (
user_id, default_view, items_per_page, enable_animations,
enable_ocr, theme, privacy_settings, ocr_settings, updated_at
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP)
ON CONFLICT (user_id)
DO UPDATE SET
default_view = COALESCE($2, user_preferences.default_view),
items_per_page = COALESCE($3, user_preferences.items_per_page),
enable_animations = COALESCE($4, user_preferences.enable_animations),
enable_ocr = COALESCE($5, user_preferences.enable_ocr),
theme = COALESCE($6, user_preferences.theme),
privacy_settings = COALESCE($7, user_preferences.privacy_settings),
ocr_settings = COALESCE($8, user_preferences.ocr_settings),
updated_at = CURRENT_TIMESTAMP
RETURNING *
`, [
user.userId,
defaultView,
itemsPerPage,
enableAnimations,
enableOcr,
theme,
privacySettings ? JSON.stringify(privacySettings) : null,
ocrSettings ? JSON.stringify(ocrSettings) : null
]);
const preferences = result.rows[0];
res.status(200).json({
success: true,
message: 'Preferences updated successfully',
preferences: {
defaultView: preferences.default_view,
itemsPerPage: preferences.items_per_page,
enableAnimations: preferences.enable_animations,
enableOcr: preferences.enable_ocr,
theme: preferences.theme,
privacySettings: preferences.privacy_settings,
ocrSettings: preferences.ocr_settings,
updatedAt: preferences.updated_at
}
});
} else {
res.status(405).json({ error: 'Method not allowed' });
}
} catch (error) {
console.error('User preferences error:', error);
if (error.message.includes('authorization') || error.message.includes('token')) {
res.status(401).json({ error: 'Unauthorized' });
} else {
res.status(500).json({
error: 'Failed to manage preferences',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
} finally {
client.release();
}
}