feat: autogen run_hour/run_minute, publish_slots, scheduled_posts tables and routes

This commit is contained in:
Alexey Pavlov
2026-05-31 16:45:15 +03:00
parent c1d5337680
commit 213dc104f5
3 changed files with 103 additions and 10 deletions
+6 -4
View File
@@ -26,13 +26,15 @@ router.post('/run', async (req, res) => {
// PATCH /api/autogen/settings/:category — обновить настройки
router.patch('/settings/:category', async (req, res) => {
try {
const { enabled, per_day } = req.body;
const { enabled, per_day, run_hour, run_minute } = req.body;
const fields = []; const vals = []; let i = 1;
if (enabled !== undefined) { fields.push(`enabled=$${i++}`); vals.push(enabled); }
if (per_day !== undefined) { fields.push(`per_day=$${i++}`); vals.push(per_day); }
if (enabled !== undefined) { fields.push(`enabled=${i++}`); vals.push(enabled); }
if (per_day !== undefined) { fields.push(`per_day=${i++}`); vals.push(per_day); }
if (run_hour !== undefined) { fields.push(`run_hour=${i++}`); vals.push(run_hour); }
if (run_minute !== undefined) { fields.push(`run_minute=${i++}`); vals.push(run_minute); }
if (!fields.length) return res.status(400).json({ error: 'Nothing to update' });
vals.push(req.params.category);
await query(`UPDATE autogen_settings SET ${fields.join(',')} WHERE category=$${i}`, vals);
await query(`UPDATE autogen_settings SET ${fields.join(',')} WHERE category=${i}`, vals);
res.json({ ok: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
+70
View File
@@ -209,3 +209,73 @@ router.delete('/:id', async (req, res) => {
});
module.exports = router;
// ── Publish slots ─────────────────────────────────────────────────────────────
// GET /api/channels/admin/:id/slots
router.get('/admin/:id/slots', async (req, res) => {
try {
const { rows } = await query(
`SELECT * FROM publish_slots WHERE channel_id=$1 ORDER BY sort_order, slot_hour, slot_minute`,
[req.params.id]
);
res.json(rows);
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/channels/admin/:id/slots
router.post('/admin/:id/slots', async (req, res) => {
try {
const { slot_hour, slot_minute, label, enabled = true } = req.body;
const { rows: existing } = await query(
`SELECT COUNT(*) as cnt FROM publish_slots WHERE channel_id=$1`, [req.params.id]
);
const sort_order = parseInt(existing[0].cnt);
const { rows } = await query(
`INSERT INTO publish_slots (channel_id, slot_hour, slot_minute, label, enabled, sort_order)
VALUES ($1,$2,$3,$4,$5,$6)
ON CONFLICT (channel_id, slot_hour, slot_minute) DO UPDATE
SET label=$4, enabled=$5 RETURNING *`,
[req.params.id, slot_hour, slot_minute, label || null, enabled, sort_order]
);
res.json(rows[0]);
} catch (err) { res.status(500).json({ error: err.message }); }
});
// DELETE /api/channels/admin/:id/slots/:slotId
router.delete('/admin/:id/slots/:slotId', async (req, res) => {
try {
await query(`DELETE FROM publish_slots WHERE id=$1 AND channel_id=$2`,
[req.params.slotId, req.params.id]);
res.json({ ok: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});
// GET /api/channels/admin/:id/scheduled — запланированные посты
router.get('/admin/:id/scheduled', async (req, res) => {
try {
const { rows } = await query(
`SELECT sp.*, a.title as article_title, a.slug as article_slug
FROM scheduled_posts sp
LEFT JOIN articles a ON a.id = sp.article_id
WHERE sp.channel_id=$1
ORDER BY sp.scheduled_at DESC LIMIT 30`,
[req.params.id]
);
res.json(rows);
} catch (err) { res.status(500).json({ error: err.message }); }
});
// POST /api/channels/admin/:id/schedule — поставить пост в очередь
router.post('/admin/:id/schedule', async (req, res) => {
try {
const { article_id, custom_text, scheduled_at } = req.body;
if (!scheduled_at) return res.status(400).json({ error: 'scheduled_at required' });
const { rows } = await query(
`INSERT INTO scheduled_posts (channel_id, article_id, custom_text, scheduled_at)
VALUES ($1,$2,$3,$4) RETURNING *`,
[req.params.id, article_id || null, custom_text || null, scheduled_at]
);
res.json(rows[0]);
} catch (err) { res.status(500).json({ error: err.message }); }
});