diff --git a/backend/src/controllers/studentMaterialsController.js b/backend/src/controllers/studentMaterialsController.js
index 24f773a..cc89127 100644
--- a/backend/src/controllers/studentMaterialsController.js
+++ b/backend/src/controllers/studentMaterialsController.js
@@ -3,6 +3,7 @@
* A user keeps copies of items saved from live lessons; the copies are
* independent of the session lifecycle. */
const db = require('../db/db');
+const { emit } = require('../sse');
const KINDS = ['board', 'note', 'link', 'image'];
@@ -129,4 +130,44 @@ function deleteCollection(req, res) {
res.json({ ok: true });
}
-module.exports = { list, create, update, remove, createCollection, updateCollection, deleteCollection };
+/* POST /api/materials/:id/share — teacher hands a material out to a class or
+ a student. Each recipient gets an independent COPY (survives later edits/
+ deletes by the teacher). Body: { classId } | { userId }. */
+function share(req, res) {
+ const mat = db.prepare('SELECT user_id, kind, title, body, url FROM student_materials WHERE id = ?').get(req.params.id);
+ if (!mat) return res.status(404).json({ error: 'not found' });
+ if (mat.user_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'forbidden' });
+
+ const b = req.body || {};
+ let recipients = [];
+ if (b.classId) {
+ const cls = db.prepare('SELECT id, teacher_id FROM classes WHERE id = ?').get(b.classId);
+ if (!cls) return res.status(404).json({ error: 'class not found' });
+ if (cls.teacher_id !== req.user.id && req.user.role !== 'admin') return res.status(403).json({ error: 'not your class' });
+ recipients = db.prepare('SELECT user_id FROM class_members WHERE class_id = ?').all(b.classId).map(r => r.user_id);
+ } else if (b.userId) {
+ const uid = Number(b.userId);
+ if (Number.isFinite(uid)) recipients = [uid];
+ } else {
+ return res.status(400).json({ error: 'classId or userId required' });
+ }
+
+ const teacherName = (db.prepare('SELECT name FROM users WHERE id = ?').get(req.user.id) || {}).name || 'Учитель';
+ const srcTitle = 'Раздатка: ' + teacherName;
+ const ins = db.prepare(`INSERT INTO student_materials (user_id, kind, title, body, url, source_session_id, source_title) VALUES (?,?,?,?,?,NULL,?)`);
+ let sent = 0;
+ db.transaction(() => {
+ for (const uid of recipients) {
+ if (!uid || uid === req.user.id) continue;
+ ins.run(uid, mat.kind, mat.title, mat.body, mat.url, srcTitle);
+ try {
+ emit(uid, { type: 'notification', notif_type: 'material_shared',
+ message: `Новый материал от ${teacherName}: «${mat.title || 'материал'}»`, link: '/my-materials' });
+ } catch (e) { /* ignore notify failure */ }
+ sent++;
+ }
+ })();
+ res.json({ ok: true, sent });
+}
+
+module.exports = { list, create, update, remove, createCollection, updateCollection, deleteCollection, share };
diff --git a/backend/src/routes/materials.js b/backend/src/routes/materials.js
index 806f0b3..2090da4 100644
--- a/backend/src/routes/materials.js
+++ b/backend/src/routes/materials.js
@@ -1,11 +1,14 @@
'use strict';
const express = require('express');
const router = express.Router();
-const { authMiddleware } = require('../middleware/auth');
+const { authMiddleware, requireRole } = require('../middleware/auth');
const c = require('../controllers/studentMaterialsController');
router.use(authMiddleware);
+// Teacher hands a material out to a class/student (copies to recipients)
+router.post('/:id/share', requireRole('teacher', 'admin'), c.share);
+
router.get('/', c.list);
router.post('/', c.create);
diff --git a/frontend/my-materials.html b/frontend/my-materials.html
index 186b8cc..e0f58e8 100644
--- a/frontend/my-materials.html
+++ b/frontend/my-materials.html
@@ -79,7 +79,8 @@