Files
Learn_System/backend/src/middleware/ownership.js
T
Maxim Dolgolyov 29301ff87d feat(flashcards): фаза 1 полировки — хоткеи, поиск, drag-reorder, честные интервалы
- study: хоткеи Space/стрелки=флип, 1-4/←→=оценка
- превью интервалов = точная копия серверного SM-2 (было враньё «<1 мин»)
- поиск/фильтр карточек внутри колоды
- drag-reorder карточек + endpoint PUT /decks/:id/reorder (requireOwnership)
- flashcard_decks добавлен в ALLOWED_TABLES requireOwnership
- эмодзи в empty-state → inline SVG .ic
- deleteCard: нативный confirm() → LS.confirm

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:53:03 +03:00

55 lines
2.0 KiB
JavaScript

'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','flashcard_decks']);
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 };