Add OpenID Connect authentication and SQLite database with user preferences and favorites system
This commit is contained in:
@@ -3,6 +3,8 @@ const cors = require('cors');
|
||||
const helmet = require('helmet');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { configureAuth, requireAuth, optionalAuth } = require('./auth');
|
||||
const { preferencesOps, favoritesOps } = require('./database');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
@@ -14,9 +16,15 @@ app.use(helmet({
|
||||
crossOriginEmbedderPolicy: false,
|
||||
originAgentCluster: false
|
||||
}));
|
||||
app.use(cors());
|
||||
app.use(cors({
|
||||
origin: process.env.FRONTEND_URL || true,
|
||||
credentials: true
|
||||
}));
|
||||
app.use(express.json());
|
||||
|
||||
// Configure authentication
|
||||
configureAuth(app);
|
||||
|
||||
// Serve static files from the React build
|
||||
app.use(express.static(path.join(__dirname, '../../frontend/build')));
|
||||
|
||||
@@ -120,6 +128,107 @@ app.get('/books/:book/:chapter', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// User preferences routes
|
||||
app.get('/api/preferences', requireAuth, (req, res) => {
|
||||
preferencesOps.getPreferences(req.user.id, (err, preferences) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to get preferences' });
|
||||
}
|
||||
res.json({
|
||||
font_size: preferences?.font_size || 'medium',
|
||||
dark_mode: !!preferences?.dark_mode
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.put('/api/preferences', requireAuth, (req, res) => {
|
||||
const { font_size, dark_mode } = req.body;
|
||||
|
||||
// Validate font_size
|
||||
if (font_size && !['small', 'medium', 'large'].includes(font_size)) {
|
||||
return res.status(400).json({ error: 'Invalid font_size' });
|
||||
}
|
||||
|
||||
preferencesOps.updatePreferences(req.user.id, { font_size, dark_mode }, (err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to update preferences' });
|
||||
}
|
||||
res.json({ message: 'Preferences updated successfully' });
|
||||
});
|
||||
});
|
||||
|
||||
// Favorites routes
|
||||
app.get('/api/favorites', requireAuth, (req, res) => {
|
||||
favoritesOps.getFavorites(req.user.id, (err, favorites) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to get favorites' });
|
||||
}
|
||||
res.json({ favorites });
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/api/favorites', requireAuth, (req, res) => {
|
||||
const { book, chapter, verse_start, verse_end, note } = req.body;
|
||||
|
||||
if (!book || !chapter) {
|
||||
return res.status(400).json({ error: 'Book and chapter are required' });
|
||||
}
|
||||
|
||||
const favorite = {
|
||||
book,
|
||||
chapter,
|
||||
verse_start: verse_start || null,
|
||||
verse_end: verse_end || null,
|
||||
note: note || null
|
||||
};
|
||||
|
||||
favoritesOps.addFavorite(req.user.id, favorite, function(err) {
|
||||
if (err) {
|
||||
if (err.code === 'SQLITE_CONSTRAINT') {
|
||||
return res.status(409).json({ error: 'Favorite already exists' });
|
||||
}
|
||||
return res.status(500).json({ error: 'Failed to add favorite' });
|
||||
}
|
||||
res.json({
|
||||
message: 'Favorite added successfully',
|
||||
id: this.lastID
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.delete('/api/favorites/:id', requireAuth, (req, res) => {
|
||||
const favoriteId = req.params.id;
|
||||
|
||||
favoritesOps.removeFavorite(req.user.id, favoriteId, (err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to remove favorite' });
|
||||
}
|
||||
res.json({ message: 'Favorite removed successfully' });
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/api/favorites/check', requireAuth, (req, res) => {
|
||||
const { book, chapter, verse_start, verse_end } = req.query;
|
||||
|
||||
if (!book || !chapter) {
|
||||
return res.status(400).json({ error: 'Book and chapter are required' });
|
||||
}
|
||||
|
||||
favoritesOps.isFavorited(
|
||||
req.user.id,
|
||||
book,
|
||||
chapter,
|
||||
verse_start || null,
|
||||
verse_end || null,
|
||||
(err, isFavorited) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Failed to check favorite status' });
|
||||
}
|
||||
res.json({ isFavorited });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Catch-all handler: send back React's index.html for client-side routing
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, '../../frontend/build/index.html'));
|
||||
|
||||
Reference in New Issue
Block a user