Add player functionality with HLS support and API integration
- Implemented a new Player class in player.js to handle audio playback, including HLS support using hls.js. - Created a shared API module in api.js for making HTTP requests with proper error handling. - Added DOM utility functions in dom.js for creating and clearing elements. - Introduced WebSocket connection handling in ws.js for real-time updates. - Developed a comprehensive CSS stylesheet for styling the application, including a high-contrast theme.
This commit is contained in:
71
server/routes/auth.js
Normal file
71
server/routes/auth.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Router } from 'express';
|
||||
import {
|
||||
verifyPassword, createSession, destroySession, setSessionCookie, clearSessionCookie,
|
||||
hashPassword, requireAdmin
|
||||
} from '../auth.js';
|
||||
import { getDb } from '../db/index.js';
|
||||
|
||||
export const router = Router();
|
||||
|
||||
router.post('/login', (req, res) => {
|
||||
const { username, password } = req.body || {};
|
||||
if (!username || !password) return res.status(400).json({ error: 'username + password required' });
|
||||
const user = getDb().prepare('SELECT * FROM users WHERE username = ?').get(username);
|
||||
if (!user || !verifyPassword(password, user.password_hash)) {
|
||||
return res.status(401).json({ error: 'invalid credentials' });
|
||||
}
|
||||
const { token, expires } = createSession(user.id);
|
||||
setSessionCookie(res, token, expires);
|
||||
res.json({ id: user.id, username: user.username, role: user.role });
|
||||
});
|
||||
|
||||
router.post('/logout', (req, res) => {
|
||||
destroySession(req.session?.token);
|
||||
clearSessionCookie(res);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.get('/me', (req, res) => {
|
||||
if (!req.user) return res.status(401).json({ error: 'not signed in' });
|
||||
res.json(req.user);
|
||||
});
|
||||
|
||||
// Admin-only user management
|
||||
router.get('/users', requireAdmin, (_req, res) => {
|
||||
const users = getDb().prepare('SELECT id, username, role, created_at FROM users ORDER BY username').all();
|
||||
res.json(users);
|
||||
});
|
||||
|
||||
router.post('/users', requireAdmin, (req, res) => {
|
||||
const { username, password, role = 'user' } = req.body || {};
|
||||
if (!username || !password) return res.status(400).json({ error: 'username + password required' });
|
||||
if (!['admin', 'user'].includes(role)) return res.status(400).json({ error: 'bad role' });
|
||||
try {
|
||||
const info = getDb().prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)')
|
||||
.run(username, hashPassword(password), role);
|
||||
getDb().prepare('INSERT INTO profiles (user_id, display_name) VALUES (?, ?)')
|
||||
.run(info.lastInsertRowid, username);
|
||||
res.status(201).json({ id: info.lastInsertRowid, username, role });
|
||||
} catch (err) {
|
||||
if (String(err).includes('UNIQUE')) return res.status(409).json({ error: 'username taken' });
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
router.patch('/users/:id', requireAdmin, (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
const { password, role } = req.body || {};
|
||||
const db = getDb();
|
||||
if (password) db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(hashPassword(password), id);
|
||||
if (role && ['admin', 'user'].includes(role)) {
|
||||
db.prepare('UPDATE users SET role = ? WHERE id = ?').run(role, id);
|
||||
}
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
router.delete('/users/:id', requireAdmin, (req, res) => {
|
||||
const id = Number(req.params.id);
|
||||
if (id === req.user.id) return res.status(400).json({ error: 'cannot delete self' });
|
||||
getDb().prepare('DELETE FROM users WHERE id = ?').run(id);
|
||||
res.json({ ok: true });
|
||||
});
|
||||
Reference in New Issue
Block a user