199 lines
6.6 KiB
PHP
199 lines
6.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/db.php';
|
|
|
|
try {
|
|
match ($_SERVER['REQUEST_METHOD']) {
|
|
'GET' => handle_get(),
|
|
'POST' => handle_post(),
|
|
'PUT' => handle_put(),
|
|
'DELETE' => handle_delete(),
|
|
default => error_out('Method not allowed', 405),
|
|
};
|
|
} catch (PDOException $e) {
|
|
error_out('Database error: ' . $e->getMessage(), 500);
|
|
}
|
|
|
|
// ─── GET /api/schedule.php ────────────────────────────────────────────────────
|
|
|
|
function handle_get(): never
|
|
{
|
|
$rows = db()->query(
|
|
'SELECT sr.id, sr.mode, sr.offset_value, sr.offset_unit,
|
|
sr.specific_datetime, sr.created_at,
|
|
t.id AS template_id, t.name AS template_name
|
|
FROM scheduled_rules sr
|
|
JOIN sms_templates t ON t.id = sr.template_id
|
|
ORDER BY sr.created_at DESC'
|
|
)->fetchAll();
|
|
|
|
json_out(['rules' => $rows]);
|
|
}
|
|
|
|
// ─── POST /api/schedule.php ───────────────────────────────────────────────────
|
|
// Body: {
|
|
// "template_id": 1,
|
|
// "mode": "relative", -- or "specific"
|
|
// "offset_value": 30, -- required for relative
|
|
// "offset_unit": "minutes", -- required for relative (minutes|hours|days)
|
|
// "specific_datetime": "..." -- required for specific (ISO 8601 or MySQL datetime)
|
|
// }
|
|
|
|
function handle_post(): never
|
|
{
|
|
$body = json_body();
|
|
$templateId = (int) ($body['template_id'] ?? 0);
|
|
$mode = (string) ($body['mode'] ?? '');
|
|
|
|
if ($templateId <= 0) {
|
|
error_out('template_id is required');
|
|
}
|
|
if (!in_array($mode, ['relative', 'specific'], true)) {
|
|
error_out('mode must be "relative" or "specific"');
|
|
}
|
|
|
|
$offsetValue = null;
|
|
$offsetUnit = null;
|
|
$specificDt = null;
|
|
|
|
if ($mode === 'relative') {
|
|
$offsetValue = (int) ($body['offset_value'] ?? 0);
|
|
$offsetUnit = (string) ($body['offset_unit'] ?? '');
|
|
|
|
if ($offsetValue <= 0) {
|
|
error_out('offset_value must be a positive integer');
|
|
}
|
|
if (!in_array($offsetUnit, ['minutes', 'hours', 'days'], true)) {
|
|
error_out('offset_unit must be "minutes", "hours", or "days"');
|
|
}
|
|
} else {
|
|
$specificDt = (string) ($body['specific_datetime'] ?? '');
|
|
if ($specificDt === '') {
|
|
error_out('specific_datetime is required when mode is "specific"');
|
|
}
|
|
if (strtotime($specificDt) === false) {
|
|
error_out('specific_datetime is not a valid date/time');
|
|
}
|
|
}
|
|
|
|
// Verify template exists
|
|
$check = db()->prepare('SELECT id FROM sms_templates WHERE id = :id');
|
|
$check->execute([':id' => $templateId]);
|
|
if (!$check->fetch()) {
|
|
error_out('Template not found', 404);
|
|
}
|
|
|
|
$stmt = db()->prepare(
|
|
'INSERT INTO scheduled_rules (template_id, mode, offset_value, offset_unit, specific_datetime)
|
|
VALUES (:template_id, :mode, :offset_value, :offset_unit, :specific_datetime)'
|
|
);
|
|
$stmt->execute([
|
|
':template_id' => $templateId,
|
|
':mode' => $mode,
|
|
':offset_value' => $offsetValue,
|
|
':offset_unit' => $offsetUnit,
|
|
':specific_datetime' => $specificDt,
|
|
]);
|
|
|
|
json_out(['id' => (int) db()->lastInsertId()], 201);
|
|
}
|
|
|
|
// ─── PUT /api/schedule.php?id=N ─────────────────────────────────────────────
|
|
// Body: same fields as POST
|
|
|
|
function handle_put(): never
|
|
{
|
|
$id = (int) ($_GET['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
error_out('Missing or invalid id');
|
|
}
|
|
|
|
$body = json_body();
|
|
$templateId = (int) ($body['template_id'] ?? 0);
|
|
$mode = (string) ($body['mode'] ?? '');
|
|
|
|
if ($templateId <= 0) {
|
|
error_out('template_id is required');
|
|
}
|
|
if (!in_array($mode, ['relative', 'specific'], true)) {
|
|
error_out('mode must be "relative" or "specific"');
|
|
}
|
|
|
|
$offsetValue = null;
|
|
$offsetUnit = null;
|
|
$specificDt = null;
|
|
|
|
if ($mode === 'relative') {
|
|
$offsetValue = (int) ($body['offset_value'] ?? 0);
|
|
$offsetUnit = (string) ($body['offset_unit'] ?? '');
|
|
if ($offsetValue <= 0) {
|
|
error_out('offset_value must be a positive integer');
|
|
}
|
|
if (!in_array($offsetUnit, ['minutes', 'hours', 'days'], true)) {
|
|
error_out('offset_unit must be "minutes", "hours", or "days"');
|
|
}
|
|
} else {
|
|
$specificDt = (string) ($body['specific_datetime'] ?? '');
|
|
if ($specificDt === '') {
|
|
error_out('specific_datetime is required when mode is "specific"');
|
|
}
|
|
if (strtotime($specificDt) === false) {
|
|
error_out('specific_datetime is not a valid date/time');
|
|
}
|
|
}
|
|
|
|
$db = db();
|
|
$check = $db->prepare('SELECT id FROM sms_templates WHERE id = :id');
|
|
$check->execute([':id' => $templateId]);
|
|
if (!$check->fetch()) {
|
|
error_out('Template not found', 404);
|
|
}
|
|
|
|
$stmt = $db->prepare(
|
|
'UPDATE scheduled_rules
|
|
SET template_id = :template_id, mode = :mode,
|
|
offset_value = :offset_value, offset_unit = :offset_unit,
|
|
specific_datetime = :specific_datetime
|
|
WHERE id = :id'
|
|
);
|
|
$stmt->execute([
|
|
':template_id' => $templateId,
|
|
':mode' => $mode,
|
|
':offset_value' => $offsetValue,
|
|
':offset_unit' => $offsetUnit,
|
|
':specific_datetime' => $specificDt,
|
|
':id' => $id,
|
|
]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
error_out('Rule not found', 404);
|
|
}
|
|
|
|
// Detach previous send_jobs so the updated rule fires fresh on the next
|
|
// scheduler tick (delivery_log history is preserved via the send_jobs FK).
|
|
$db->prepare('UPDATE send_jobs SET scheduled_rule_id = NULL WHERE scheduled_rule_id = :id')
|
|
->execute([':id' => $id]);
|
|
|
|
json_out(['updated' => true]);
|
|
}
|
|
|
|
// ─── DELETE /api/schedule.php?id=N ───────────────────────────────────────────
|
|
|
|
function handle_delete(): never
|
|
{
|
|
$id = (int) ($_GET['id'] ?? 0);
|
|
if ($id <= 0) {
|
|
error_out('Missing or invalid id');
|
|
}
|
|
|
|
$stmt = db()->prepare('DELETE FROM scheduled_rules WHERE id = :id');
|
|
$stmt->execute([':id' => $id]);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
error_out('Rule not found', 404);
|
|
}
|
|
|
|
json_out(['deleted' => true]);
|
|
}
|