LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
const db = require('../db/db');
|
||||
|
||||
/**
|
||||
* Factory: returns middleware that loads a resource by req.params[paramKey],
|
||||
* verifies ownership against req.user.id, and attaches the row as req.resource.
|
||||
*
|
||||
* Simple usage (table lookup):
|
||||
* requireOwnership({ table: 'tests', ownerField: 'created_by' })
|
||||
*
|
||||
* Complex usage (JOIN / aliased field):
|
||||
* requireOwnership({
|
||||
* fetchFn: id => db.prepare(`
|
||||
* SELECT a.id, COALESCE(c.teacher_id, a.created_by) AS teacher_id
|
||||
* FROM assignments a LEFT JOIN classes c ON c.id = a.class_id WHERE a.id = ?
|
||||
* `).get(id),
|
||||
* ownerField: 'teacher_id',
|
||||
* })
|
||||
*
|
||||
* Options:
|
||||
* table — DB table name for `SELECT * FROM {table} WHERE id = ?`
|
||||
* fetchFn — (id) => row|undefined (alternative to `table`)
|
||||
* ownerField — row field compared to req.user.id (required)
|
||||
* paramKey — req.params key for the record ID (default: 'id')
|
||||
* adminBypass — admin role always passes (default: true)
|
||||
*/
|
||||
const ALLOWED_TABLES = new Set(['tests','classes','assignments','questions','courses','lessons','files','folders','shop_items','live_sessions']);
|
||||
|
||||
function requireOwnership({ table, fetchFn, ownerField, paramKey = 'id', adminBypass = true }) {
|
||||
if (table && !ALLOWED_TABLES.has(table)) throw new Error(`requireOwnership: unknown table "${table}"`);
|
||||
// Pre-compile statement once at middleware creation (server startup)
|
||||
const stmt = table ? db.prepare(`SELECT * FROM ${table} WHERE id = ?`) : null;
|
||||
const fetch = fetchFn || (id => stmt.get(id));
|
||||
|
||||
return (req, res, next) => {
|
||||
const row = fetch(req.params[paramKey]);
|
||||
|
||||
if (!row) return res.status(404).json({ error: 'Not found' });
|
||||
|
||||
if (adminBypass && req.user?.role === 'admin') {
|
||||
req.resource = row;
|
||||
return next();
|
||||
}
|
||||
|
||||
if (row[ownerField] !== req.user?.id) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
|
||||
req.resource = row;
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { requireOwnership };
|
||||
Reference in New Issue
Block a user