Initial commit - Church Music Database

This commit is contained in:
2026-01-27 18:04:50 -06:00
commit d367261867
336 changed files with 103545 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
const express = require("express");
const router = express.Router();
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const { query } = require("../db");
const JWT_SECRET = process.env.JWT_SECRET || "your-super-secret-jwt-key";
// Login
router.post("/login", async (req, res) => {
try {
const { username, password } = req.body;
if (!username || !password) {
return res.status(400).json({
success: false,
message: "Username and password are required",
});
}
// Find user in database (case-insensitive)
const result = await query(
"SELECT * FROM users WHERE LOWER(username) = LOWER($1)",
[username],
);
if (result.rows.length === 0) {
return res
.status(401)
.json({ success: false, message: "Invalid credentials" });
}
const user = result.rows[0];
// Check password
const validPassword = await bcrypt.compare(password, user.password_hash);
if (!validPassword) {
return res
.status(401)
.json({ success: false, message: "Invalid credentials" });
}
// Generate JWT token
const token = jwt.sign(
{
id: user.id,
username: user.username,
role: user.role || "user",
},
JWT_SECRET,
{ expiresIn: "7d" },
);
res.json({
success: true,
token,
user: {
id: user.id,
username: user.username,
name: user.display_name || user.username,
role: user.role || "user",
},
});
} catch (err) {
console.error("Login error:", err);
res.status(500).json({ success: false, message: "Login failed" });
}
});
// Verify token
router.get("/verify", async (req, res) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res
.status(401)
.json({ success: false, message: "No token provided" });
}
const token = authHeader.split(" ")[1];
const decoded = jwt.verify(token, JWT_SECRET);
// Get fresh user data
const result = await query("SELECT * FROM users WHERE id = $1", [
decoded.id,
]);
if (result.rows.length === 0) {
return res
.status(401)
.json({ success: false, message: "User not found" });
}
const user = result.rows[0];
res.json({
success: true,
user: {
id: user.id,
username: user.username,
name: user.display_name || user.username,
role: user.role || "user",
},
});
} catch (err) {
console.error("Token verification error:", err);
res.status(401).json({ success: false, message: "Invalid token" });
}
});
// Logout (client-side token deletion, but we can track here if needed)
router.post("/logout", (req, res) => {
res.json({ success: true, message: "Logged out" });
});
// Get current user
router.get("/me", async (req, res) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res
.status(401)
.json({ success: false, message: "Not authenticated" });
}
const token = authHeader.split(" ")[1];
const decoded = jwt.verify(token, JWT_SECRET);
const result = await query("SELECT * FROM users WHERE id = $1", [
decoded.id,
]);
if (result.rows.length === 0) {
return res
.status(401)
.json({ success: false, message: "User not found" });
}
const user = result.rows[0];
res.json({
success: true,
user: {
id: user.id,
username: user.username,
name: user.display_name || user.username,
role: user.role || "user",
biometric_enabled: user.biometric_enabled || false,
},
});
} catch (err) {
console.error("Get user error:", err);
res.status(401).json({ success: false, message: "Invalid token" });
}
});
// Biometric registration - store public key
router.post("/biometric-register", async (req, res) => {
try {
const { username, credentialId, publicKey } = req.body;
if (!username || !credentialId || !publicKey) {
return res.status(400).json({
success: false,
message: "Username, credential ID, and public key required",
});
}
// Update user with biometric credential
const result = await query(
`UPDATE users
SET biometric_credential_id = $1,
biometric_public_key = $2,
biometric_enabled = true
WHERE username = $3
RETURNING id, username`,
[credentialId, publicKey, username.toLowerCase()],
);
if (result.rows.length === 0) {
return res
.status(404)
.json({ success: false, message: "User not found" });
}
res.json({
success: true,
message: "Biometric authentication registered successfully",
});
} catch (err) {
console.error("Biometric registration error:", err);
res.status(500).json({ success: false, message: "Registration failed" });
}
});
// Biometric login - verify assertion
router.post("/biometric-login", async (req, res) => {
try {
const { username, assertion } = req.body;
if (!username || !assertion) {
return res.status(400).json({
success: false,
message: "Username and assertion required",
});
}
// Find user with biometric enabled
const result = await query(
`SELECT * FROM users
WHERE username = $1 AND biometric_enabled = true`,
[username.toLowerCase()],
);
if (result.rows.length === 0) {
return res.status(401).json({
success: false,
message: "Biometric authentication not enabled",
});
}
const user = result.rows[0];
// In a production environment, verify the assertion signature here
// For now, we'll trust the client-side verification
// TODO: Implement server-side WebAuthn assertion verification
// Generate JWT token
const token = jwt.sign(
{
id: user.id,
username: user.username,
role: user.role || "user",
},
JWT_SECRET,
{ expiresIn: "7d" },
);
res.json({
success: true,
token,
user: {
id: user.id,
username: user.username,
name: user.display_name || user.username,
role: user.role || "user",
},
});
} catch (err) {
console.error("Biometric login error:", err);
res.status(500).json({ success: false, message: "Biometric login failed" });
}
});
module.exports = router;