mirror of
git://f0xx.org/ac/ac-ms-tickets
synced 2026-07-29 03:17:45 +03:00
initial
This commit is contained in:
43
public/api/ticket_attachment.php
Normal file
43
public/api/ticket_attachment.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
http_response_code(401);
|
||||
echo 'Unauthorized';
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
} catch (RuntimeException $e) {
|
||||
http_response_code(503);
|
||||
echo 'Tickets unavailable';
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = (int) ($_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(400);
|
||||
exit;
|
||||
}
|
||||
|
||||
$att = TicketAttachmentRepository::getById($id);
|
||||
if (!$att || ($att['kind'] ?? '') !== 'file') {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
$path = TicketAttachmentRepository::filePath($att);
|
||||
if ($path === null) {
|
||||
http_response_code(404);
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = (string) ($att['original_name'] ?? 'download');
|
||||
$mime = (string) ($att['mime_type'] ?? 'application/octet-stream');
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Content-Length: ' . (string) filesize($path));
|
||||
header('Content-Disposition: attachment; filename="' . str_replace('"', '', $name) . '"');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
readfile($path);
|
||||
109
public/api/ticket_attachments.php
Normal file
109
public/api/ticket_attachments.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
} catch (RuntimeException $e) {
|
||||
json_out(['ok' => false, 'error' => 'tickets_table_missing', 'hint' => $e->getMessage()], 503);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||
|
||||
if ($method === 'GET') {
|
||||
$ticketId = (int) ($_GET['ticket_id'] ?? 0);
|
||||
if ($ticketId <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid ticket_id'], 400);
|
||||
}
|
||||
try {
|
||||
json_out(['ok' => true, 'attachments' => TicketAttachmentRepository::listForTicket($ticketId)]);
|
||||
} catch (Throwable $e) {
|
||||
if (str_contains($e->getMessage(), 'ticket_attachments') || str_contains($e->getMessage(), 'no such table')) {
|
||||
json_out(['ok' => false, 'error' => 'attachments_table_missing', 'hint' => 'Run sql/migrations/005_ticket_attachments.sql'], 503);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method === 'DELETE') {
|
||||
$id = (int) ($_GET['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid id'], 400);
|
||||
}
|
||||
try {
|
||||
TicketAttachmentRepository::delete($id, (string) ($user['username'] ?? ''));
|
||||
json_out(['ok' => true]);
|
||||
} catch (RuntimeException $e) {
|
||||
$code = $e->getMessage() === 'forbidden' ? 403 : 404;
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], $code);
|
||||
} catch (Throwable $e) {
|
||||
if (str_contains($e->getMessage(), 'ticket_attachments')) {
|
||||
json_out(['ok' => false, 'error' => 'attachments_table_missing'], 503);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method !== 'POST') {
|
||||
json_out(['ok' => false, 'error' => 'method not allowed'], 405);
|
||||
}
|
||||
|
||||
$ticketId = (int) ($_POST['ticket_id'] ?? 0);
|
||||
if ($ticketId <= 0) {
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : null;
|
||||
if (is_array($data)) {
|
||||
$ticketId = (int) ($data['ticket_id'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
if ($ticketId <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid ticket_id'], 400);
|
||||
}
|
||||
|
||||
$ticket = TicketRepository::getById($ticketId);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
// Any logged-in user who can view the ticket may add attachments (same as comments).
|
||||
|
||||
try {
|
||||
if (!empty($_FILES['file']['name'])) {
|
||||
$att = TicketAttachmentRepository::addFile(
|
||||
$ticketId,
|
||||
(string) ($user['username'] ?? ''),
|
||||
$_FILES['file'],
|
||||
trim((string) ($_POST['title'] ?? ''))
|
||||
);
|
||||
json_out(['ok' => true, 'attachment' => $att]);
|
||||
}
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : $_POST;
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'file or url required'], 400);
|
||||
}
|
||||
$url = trim((string) ($data['url'] ?? $data['external_url'] ?? ''));
|
||||
if ($url === '') {
|
||||
json_out(['ok' => false, 'error' => 'url required'], 400);
|
||||
}
|
||||
$att = TicketAttachmentRepository::addLink(
|
||||
$ticketId,
|
||||
(string) ($user['username'] ?? ''),
|
||||
$url,
|
||||
trim((string) ($data['title'] ?? ''))
|
||||
);
|
||||
json_out(['ok' => true, 'attachment' => $att]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (Throwable $e) {
|
||||
if (str_contains($e->getMessage(), 'ticket_attachments')) {
|
||||
json_out(['ok' => false, 'error' => 'attachments_table_missing'], 503);
|
||||
}
|
||||
error_log('ticket_attachments: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'save failed'], 500);
|
||||
}
|
||||
75
public/api/ticket_comments.php
Normal file
75
public/api/ticket_comments.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
} catch (RuntimeException $e) {
|
||||
json_out(['ok' => false, 'error' => 'tickets_table_missing', 'hint' => $e->getMessage()], 503);
|
||||
}
|
||||
|
||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||
|
||||
if ($method === 'GET') {
|
||||
$ticketId = (int) ($_GET['ticket_id'] ?? 0);
|
||||
if ($ticketId <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid ticket_id'], 400);
|
||||
}
|
||||
$ticket = TicketRepository::getById($ticketId);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
try {
|
||||
json_out(['ok' => true, 'comments' => TicketCommentRepository::listForTicket($ticketId)]);
|
||||
} catch (Throwable $e) {
|
||||
if (str_contains($e->getMessage(), 'ticket_comments') || str_contains($e->getMessage(), 'no such table')) {
|
||||
json_out(['ok' => false, 'error' => 'comments_table_missing', 'hint' => 'Run sql/migrations/004_ticket_workflow.sql'], 503);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($method !== 'POST') {
|
||||
json_out(['ok' => false, 'error' => 'method not allowed'], 405);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : $_POST;
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'invalid json'], 400);
|
||||
}
|
||||
|
||||
$ticketId = (int) ($data['ticket_id'] ?? 0);
|
||||
if ($ticketId <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid ticket_id'], 400);
|
||||
}
|
||||
|
||||
$ticket = TicketRepository::getById($ticketId);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
if (empty($ticket['can_edit']) && !Auth::canEditTags()) {
|
||||
json_out(['ok' => false, 'error' => 'forbidden'], 403);
|
||||
}
|
||||
|
||||
try {
|
||||
$comment = TicketCommentRepository::add(
|
||||
$ticketId,
|
||||
(string) ($user['username'] ?? ''),
|
||||
(string) ($data['body'] ?? '')
|
||||
);
|
||||
json_out(['ok' => true, 'comment' => $comment]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (Throwable $e) {
|
||||
if (str_contains($e->getMessage(), 'ticket_comments') || str_contains($e->getMessage(), 'no such table')) {
|
||||
json_out(['ok' => false, 'error' => 'comments_table_missing', 'hint' => 'Run sql/migrations/004_ticket_workflow.sql'], 503);
|
||||
}
|
||||
error_log('ticket_comments: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'save failed'], 500);
|
||||
}
|
||||
65
public/api/ticket_create.php
Normal file
65
public/api/ticket_create.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = json_decode($raw !== false ? $raw : '', true);
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'invalid json'], 400);
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$title = trim((string) ($data['title'] ?? ''));
|
||||
$body = trim((string) ($data['body'] ?? ''));
|
||||
$brief = trim((string) ($data['brief'] ?? ''));
|
||||
if ($title === '') {
|
||||
json_out(['ok' => false, 'error' => 'title required'], 400);
|
||||
}
|
||||
if ($body === '' && $brief !== '') {
|
||||
$body = $brief;
|
||||
}
|
||||
|
||||
$tags = is_array($data['tags'] ?? null) ? $data['tags'] : [];
|
||||
if ($tags === []) {
|
||||
$tags = [['id' => 'open', 'label' => 'open', 'bg' => '#22c55e']];
|
||||
}
|
||||
|
||||
$now = (int) round(microtime(true) * 1000);
|
||||
$payload = [
|
||||
'schema_version' => 1,
|
||||
'ingest_kind' => 'ticket',
|
||||
'ticket_id' => 'web-' . date('Ymd-His') . '-' . bin2hex(random_bytes(4)),
|
||||
'title' => mb_substr($title, 0, 256),
|
||||
'brief' => $brief !== '' ? mb_substr($brief, 0, 512) : mb_substr($title, 0, 512),
|
||||
'body' => $body !== '' ? $body : $title,
|
||||
'opened_at_epoch_ms' => $now,
|
||||
'rating' => max(0, min(5, (int) ($data['rating'] ?? 3))),
|
||||
'owner' => (string) ($user['username'] ?? 'admin'),
|
||||
'tags' => $tags,
|
||||
'device' => [
|
||||
'manufacturer' => 'web',
|
||||
'model' => 'console',
|
||||
'sdk_int' => 0,
|
||||
'release' => 'n/a',
|
||||
],
|
||||
'app' => [
|
||||
'package' => 'com.foxx.androidcast',
|
||||
'version_name' => 'console',
|
||||
'version_code' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
$id = TicketRepository::insert($payload);
|
||||
json_out(['ok' => true, 'id' => $id, 'ticket_id' => $payload['ticket_id']]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (Throwable $e) {
|
||||
error_log('ticket_create: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'create failed'], 500);
|
||||
}
|
||||
75
public/api/ticket_tags.php
Normal file
75
public/api/ticket_tags.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
} catch (RuntimeException $e) {
|
||||
json_out(['ok' => false, 'error' => 'tickets_table_missing', 'hint' => $e->getMessage()], 503);
|
||||
}
|
||||
|
||||
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
||||
$id = (int) ($_GET['id'] ?? 0);
|
||||
|
||||
if ($method === 'GET') {
|
||||
if ($id <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid id'], 400);
|
||||
}
|
||||
$ticket = TicketRepository::getById($id);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
json_out([
|
||||
'ok' => true,
|
||||
'id' => $id,
|
||||
'tags' => $ticket['tags'] ?? [],
|
||||
'presets' => TicketRepository::listTagPresets(),
|
||||
'can_edit' => !empty($ticket['can_edit']),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($method !== 'PUT' && $method !== 'POST') {
|
||||
json_out(['ok' => false, 'error' => 'method not allowed'], 405);
|
||||
}
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : $_POST;
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'invalid json'], 400);
|
||||
}
|
||||
$id = (int) ($data['id'] ?? $id);
|
||||
if ($id <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid id'], 400);
|
||||
}
|
||||
|
||||
$ticket = TicketRepository::getById($id);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
if (empty($ticket['can_edit'])) {
|
||||
json_out(['ok' => false, 'error' => 'forbidden'], 403);
|
||||
}
|
||||
|
||||
$tagsIn = $data['tags'] ?? [];
|
||||
if (!is_array($tagsIn)) {
|
||||
json_out(['ok' => false, 'error' => 'tags must be array'], 400);
|
||||
}
|
||||
|
||||
try {
|
||||
TicketRepository::setCustomTags($id, $tagsIn);
|
||||
$updated = TicketRepository::getById($id);
|
||||
json_out([
|
||||
'ok' => true,
|
||||
'id' => $id,
|
||||
'tags' => $updated['tags'] ?? [],
|
||||
]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (Throwable $e) {
|
||||
error_log('ticket_tags: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'save failed'], 500);
|
||||
}
|
||||
56
public/api/ticket_update.php
Normal file
56
public/api/ticket_update.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
} catch (RuntimeException $e) {
|
||||
json_out(['ok' => false, 'error' => 'tickets_table_missing', 'hint' => $e->getMessage()], 503);
|
||||
}
|
||||
|
||||
if (strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'PUT' && strtoupper($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
|
||||
json_out(['ok' => false, 'error' => 'method not allowed'], 405);
|
||||
}
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
$data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : $_POST;
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'invalid json'], 400);
|
||||
}
|
||||
$id = (int) ($data['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
json_out(['ok' => false, 'error' => 'invalid id'], 400);
|
||||
}
|
||||
|
||||
$ticket = TicketRepository::getById($id);
|
||||
if (!$ticket) {
|
||||
json_out(['ok' => false, 'error' => 'not found'], 404);
|
||||
}
|
||||
if (empty($ticket['can_edit'])) {
|
||||
json_out(['ok' => false, 'error' => 'forbidden'], 403);
|
||||
}
|
||||
|
||||
$fields = [];
|
||||
foreach (['title', 'brief', 'body', 'rating', 'owner_username', 'verified_by_username', 'workflow_state'] as $key) {
|
||||
if (array_key_exists($key, $data)) {
|
||||
$fields[$key] = $data[$key];
|
||||
}
|
||||
}
|
||||
if (array_key_exists('assignees', $data)) {
|
||||
$fields['assignees'] = $data['assignees'];
|
||||
}
|
||||
|
||||
try {
|
||||
TicketRepository::update($id, $fields);
|
||||
$updated = TicketRepository::getById($id);
|
||||
json_out(['ok' => true, 'ticket' => $updated]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (Throwable $e) {
|
||||
error_log('ticket_update: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'save failed'], 500);
|
||||
}
|
||||
46
public/api/ticket_upload.php
Normal file
46
public/api/ticket_upload.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
$raw = file_get_contents('php://input');
|
||||
if ($raw === false || $raw === '') {
|
||||
json_out(['ok' => false, 'error' => 'empty body'], 400);
|
||||
}
|
||||
$data = json_decode($raw, true);
|
||||
if (!is_array($data)) {
|
||||
json_out(['ok' => false, 'error' => 'invalid json'], 400);
|
||||
}
|
||||
if ((int) ($data['schema_version'] ?? 0) !== 1) {
|
||||
json_out(['ok' => false, 'error' => 'unsupported schema_version'], 400);
|
||||
}
|
||||
if (($data['ingest_kind'] ?? 'ticket') !== 'ticket') {
|
||||
json_out(['ok' => false, 'error' => 'ingest_kind must be ticket'], 400);
|
||||
}
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
TicketRepository::insert($data);
|
||||
json_out(['ok' => true, 'ticket_id' => $data['ticket_id'] ?? null]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
json_out(['ok' => false, 'error' => $e->getMessage()], 400);
|
||||
} catch (RuntimeException $e) {
|
||||
error_log('ticket upload: ' . $e->getMessage());
|
||||
json_out(['ok' => false, 'error' => 'tickets_table_missing', 'hint' => $e->getMessage()], 503);
|
||||
} catch (PDOException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($e->getCode() === '23000' || stripos($msg, 'UNIQUE') !== false) {
|
||||
json_out(['ok' => false, 'error' => 'duplicate ticket_id'], 409);
|
||||
}
|
||||
error_log('ticket upload PDO: ' . $msg);
|
||||
$out = ['ok' => false, 'error' => 'store failed'];
|
||||
if (cfg('debug')) {
|
||||
$out['hint'] = $msg;
|
||||
}
|
||||
json_out($out, 500);
|
||||
} catch (Throwable $e) {
|
||||
error_log('ticket upload: ' . $e->getMessage());
|
||||
$out = ['ok' => false, 'error' => 'store failed'];
|
||||
if (cfg('debug')) {
|
||||
$out['hint'] = $e->getMessage();
|
||||
}
|
||||
json_out($out, 500);
|
||||
}
|
||||
49
public/api/tickets.php
Normal file
49
public/api/tickets.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
$page = max(1, (int) ($_GET['page'] ?? 1));
|
||||
$perPage = (int) ($_GET['per_page'] ?? 50);
|
||||
if (!in_array($perPage, [25, 50, 75, 100, 200], true)) {
|
||||
$perPage = 50;
|
||||
}
|
||||
$sort = (string) ($_GET['sort'] ?? 'opened_at_ms');
|
||||
$dir = (string) ($_GET['dir'] ?? 'desc');
|
||||
$tag = trim((string) ($_GET['tag'] ?? ''));
|
||||
|
||||
try {
|
||||
Database::requireTicketsTable();
|
||||
$data = TicketRepository::listPage($page, $perPage, $sort, $dir, $tag);
|
||||
json_out(['ok' => true] + $data);
|
||||
} catch (RuntimeException $e) {
|
||||
error_log('tickets api: ' . $e->getMessage());
|
||||
$out = [
|
||||
'ok' => false,
|
||||
'error' => 'tickets_table_missing',
|
||||
'hint' => $e->getMessage(),
|
||||
];
|
||||
if (cfg('debug')) {
|
||||
try {
|
||||
$pdo = Database::pdo();
|
||||
$out['db_driver'] = Database::driver();
|
||||
$out['tickets_table'] = Database::ticketsTableExists($pdo);
|
||||
if (Database::isMysql()) {
|
||||
$out['mysql_database'] = $pdo->query('SELECT DATABASE()')->fetchColumn();
|
||||
}
|
||||
} catch (Throwable $diag) {
|
||||
$out['diag_error'] = $diag->getMessage();
|
||||
}
|
||||
}
|
||||
json_out($out, 503);
|
||||
} catch (Throwable $e) {
|
||||
error_log('tickets api: ' . $e->getMessage());
|
||||
$out = ['ok' => false, 'error' => 'list failed'];
|
||||
if (cfg('debug')) {
|
||||
$out['hint'] = $e->getMessage();
|
||||
}
|
||||
json_out($out, 500);
|
||||
}
|
||||
9
public/api/users.php
Normal file
9
public/api/users.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/../../src/bootstrap.php';
|
||||
|
||||
if (!Auth::user()) {
|
||||
json_out(['ok' => false, 'error' => 'unauthorized'], 401);
|
||||
}
|
||||
|
||||
json_out(['ok' => true, 'users' => UserRepository::listForAssignees()]);
|
||||
2
public/index.php
Normal file
2
public/index.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// API entry — wire nginx to public/
|
||||
Reference in New Issue
Block a user