Add OpenID Connect authentication and SQLite database with user preferences and favorites system
This commit is contained in:
200
backend/src/database.js
Normal file
200
backend/src/database.js
Normal file
@@ -0,0 +1,200 @@
|
||||
const sqlite3 = require('sqlite3').verbose();
|
||||
const path = require('path');
|
||||
|
||||
// Database file path
|
||||
const DB_PATH = path.join(__dirname, '../data/bible.db');
|
||||
|
||||
// Initialize database
|
||||
const db = new sqlite3.Database(DB_PATH, (err) => {
|
||||
if (err) {
|
||||
console.error('Error opening database:', err.message);
|
||||
} else {
|
||||
console.log('Connected to SQLite database');
|
||||
initializeTables();
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize database tables
|
||||
function initializeTables() {
|
||||
// Users table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
openid_sub TEXT UNIQUE NOT NULL,
|
||||
email TEXT,
|
||||
name TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// User preferences table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS user_preferences (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
font_size TEXT DEFAULT 'medium' CHECK(font_size IN ('small', 'medium', 'large')),
|
||||
dark_mode BOOLEAN DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Favorites table
|
||||
db.run(`
|
||||
CREATE TABLE IF NOT EXISTS favorites (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
book TEXT NOT NULL,
|
||||
chapter TEXT NOT NULL,
|
||||
verse_start INTEGER,
|
||||
verse_end INTEGER,
|
||||
note TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
UNIQUE(user_id, book, chapter, verse_start, verse_end)
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('Database tables initialized');
|
||||
}
|
||||
|
||||
// User operations
|
||||
const userOps = {
|
||||
// Find or create user by OpenID Connect subject
|
||||
findOrCreateUser: (profile, callback) => {
|
||||
const { sub, email, name } = profile;
|
||||
|
||||
db.get(
|
||||
'SELECT * FROM users WHERE openid_sub = ?',
|
||||
[sub],
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (user) {
|
||||
// Update user info
|
||||
db.run(
|
||||
'UPDATE users SET email = ?, name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
|
||||
[email, name, user.id],
|
||||
(err) => {
|
||||
if (err) return callback(err);
|
||||
callback(null, user);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Create new user
|
||||
db.run(
|
||||
'INSERT INTO users (openid_sub, email, name) VALUES (?, ?, ?)',
|
||||
[sub, email, name],
|
||||
function(err) {
|
||||
if (err) return callback(err);
|
||||
|
||||
const newUser = {
|
||||
id: this.lastID,
|
||||
openid_sub: sub,
|
||||
email,
|
||||
name
|
||||
};
|
||||
|
||||
// Create default preferences
|
||||
db.run(
|
||||
'INSERT INTO user_preferences (user_id) VALUES (?)',
|
||||
[newUser.id],
|
||||
(err) => {
|
||||
if (err) console.error('Error creating default preferences:', err);
|
||||
callback(null, newUser);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Find user by ID
|
||||
findById: (id, callback) => {
|
||||
db.get('SELECT * FROM users WHERE id = ?', [id], callback);
|
||||
}
|
||||
};
|
||||
|
||||
// Preferences operations
|
||||
const preferencesOps = {
|
||||
// Get user preferences
|
||||
getPreferences: (userId, callback) => {
|
||||
db.get(
|
||||
'SELECT * FROM user_preferences WHERE user_id = ?',
|
||||
[userId],
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
// Update user preferences
|
||||
updatePreferences: (userId, preferences, callback) => {
|
||||
const { font_size, dark_mode } = preferences;
|
||||
|
||||
db.run(
|
||||
`UPDATE user_preferences
|
||||
SET font_size = ?, dark_mode = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE user_id = ?`,
|
||||
[font_size, dark_mode ? 1 : 0, userId],
|
||||
callback
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Favorites operations
|
||||
const favoritesOps = {
|
||||
// Get user favorites
|
||||
getFavorites: (userId, callback) => {
|
||||
db.all(
|
||||
'SELECT * FROM favorites WHERE user_id = ? ORDER BY created_at DESC',
|
||||
[userId],
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
// Add favorite
|
||||
addFavorite: (userId, favorite, callback) => {
|
||||
const { book, chapter, verse_start, verse_end, note } = favorite;
|
||||
|
||||
db.run(
|
||||
`INSERT INTO favorites (user_id, book, chapter, verse_start, verse_end, note)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
[userId, book, chapter, verse_start, verse_end, note],
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
// Remove favorite
|
||||
removeFavorite: (userId, favoriteId, callback) => {
|
||||
db.run(
|
||||
'DELETE FROM favorites WHERE id = ? AND user_id = ?',
|
||||
[favoriteId, userId],
|
||||
callback
|
||||
);
|
||||
},
|
||||
|
||||
// Check if verse is favorited
|
||||
isFavorited: (userId, book, chapter, verse_start, verse_end, callback) => {
|
||||
db.get(
|
||||
'SELECT id FROM favorites WHERE user_id = ? AND book = ? AND chapter = ? AND verse_start = ? AND verse_end = ?',
|
||||
[userId, book, chapter, verse_start, verse_end],
|
||||
(err, row) => {
|
||||
if (err) return callback(err);
|
||||
callback(null, !!row);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
db,
|
||||
userOps,
|
||||
preferencesOps,
|
||||
favoritesOps
|
||||
};
|
||||
Reference in New Issue
Block a user