290 lines
7.2 KiB
PHP
290 lines
7.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../db.php';
|
|
require_once __DIR__ . '/../auth.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
function jsonResponse(array $data, int $status = 200): void
|
|
{
|
|
http_response_code($status);
|
|
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function getInputData(): array
|
|
{
|
|
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
|
|
|
if (str_contains($contentType, 'application/json')) {
|
|
$raw = file_get_contents('php://input');
|
|
$data = json_decode($raw, true);
|
|
|
|
return is_array($data) ? $data : [];
|
|
}
|
|
|
|
return $_POST;
|
|
}
|
|
|
|
function requireUserExists(int $userId): void
|
|
{
|
|
if ($userId <= 0) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project owner must be valid.'
|
|
], 400);
|
|
}
|
|
|
|
$stmt = db()->prepare('SELECT id FROM users WHERE id = ? LIMIT 1');
|
|
$stmt->execute([$userId]);
|
|
|
|
if (!$stmt->fetch()) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project owner not found.'
|
|
], 400);
|
|
}
|
|
}
|
|
|
|
$user = requireApiAuth();
|
|
|
|
$api = $_GET['api'] ?? '';
|
|
|
|
switch ($api) {
|
|
|
|
case 'ListProjects': {
|
|
if (userIsAdmin((int)$user['id'])) {
|
|
$stmt = db()->query(
|
|
'SELECT id
|
|
FROM projects
|
|
ORDER BY id ASC'
|
|
);
|
|
} else {
|
|
$stmt = db()->prepare(
|
|
'SELECT DISTINCT projects.id
|
|
FROM projects
|
|
LEFT JOIN user_access
|
|
ON user_access.project_id = projects.id
|
|
AND user_access.user_id = ?
|
|
WHERE projects.owner = ?
|
|
OR user_access.id IS NOT NULL
|
|
ORDER BY projects.id ASC'
|
|
);
|
|
$stmt->execute([(int)$user['id'], (int)$user['id']]);
|
|
}
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'projects' => $stmt->fetchAll(PDO::FETCH_COLUMN)
|
|
]);
|
|
}
|
|
|
|
case 'ProjectInfo': {
|
|
$projectId = strtoupper(trim($_GET['project_id'] ?? ''));
|
|
|
|
if ($projectId === '') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Missing project_id.'
|
|
], 400);
|
|
}
|
|
|
|
requireProjectAccessForUser((int)$user['id'], $projectId);
|
|
|
|
$stmt = db()->prepare(
|
|
'SELECT id, name, owner, created_date
|
|
FROM projects
|
|
WHERE id = ?
|
|
LIMIT 1'
|
|
);
|
|
|
|
$stmt->execute([$projectId]);
|
|
$project = $stmt->fetch();
|
|
|
|
if (!$project) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project not found.'
|
|
], 404);
|
|
}
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'project' => $project
|
|
]);
|
|
}
|
|
|
|
case 'Create': {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Create requires POST.'
|
|
], 405);
|
|
}
|
|
|
|
requireUserRight((int)$user['id'], 'Create Projects');
|
|
|
|
$data = getInputData();
|
|
|
|
$projectId = strtoupper(trim($data['id'] ?? ''));
|
|
$name = trim($data['name'] ?? '');
|
|
$owner = isset($data['owner']) && $data['owner'] !== ''
|
|
? (int)$data['owner']
|
|
: (int)$user['id'];
|
|
|
|
if (!preg_match('/^[A-Z0-9]{2,12}$/', $projectId)) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project id must be 2-12 characters and contain only A-Z and 0-9.'
|
|
], 400);
|
|
}
|
|
|
|
if ($name === '') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project name is required.'
|
|
], 400);
|
|
}
|
|
|
|
if (strlen($name) > 128) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project name is too long.'
|
|
], 400);
|
|
}
|
|
|
|
requireUserExists($owner);
|
|
|
|
$stmt = db()->prepare(
|
|
'SELECT id FROM projects WHERE id = ? LIMIT 1'
|
|
);
|
|
|
|
$stmt->execute([$projectId]);
|
|
|
|
if ($stmt->fetch()) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project id already exists.'
|
|
], 409);
|
|
}
|
|
|
|
$stmt = db()->prepare(
|
|
'INSERT INTO projects (id, name, owner, created_date)
|
|
VALUES (?, ?, ?, CURDATE())'
|
|
);
|
|
|
|
$stmt->execute([
|
|
$projectId,
|
|
$name,
|
|
$owner
|
|
]);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'project_id' => $projectId
|
|
], 201);
|
|
}
|
|
|
|
case 'Edit': {
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Edit requires POST.'
|
|
], 405);
|
|
}
|
|
|
|
$projectId = strtoupper(trim($_GET['project_id'] ?? ''));
|
|
|
|
if ($projectId === '') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Missing project_id.'
|
|
], 400);
|
|
}
|
|
|
|
requireProjectRight((int)$user['id'], $projectId, 'Edit Projects');
|
|
|
|
$data = getInputData();
|
|
|
|
$fields = [];
|
|
$values = [];
|
|
|
|
if (array_key_exists('name', $data)) {
|
|
$name = trim((string)$data['name']);
|
|
|
|
if ($name === '') {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project name cannot be empty.'
|
|
], 400);
|
|
}
|
|
|
|
if (strlen($name) > 128) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project name is too long.'
|
|
], 400);
|
|
}
|
|
|
|
$fields[] = 'name = ?';
|
|
$values[] = $name;
|
|
}
|
|
|
|
if (array_key_exists('owner', $data)) {
|
|
$owner = (int)$data['owner'];
|
|
requireUserExists($owner);
|
|
|
|
$fields[] = 'owner = ?';
|
|
$values[] = $owner;
|
|
}
|
|
|
|
if (empty($fields)) {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'No editable fields provided.'
|
|
], 400);
|
|
}
|
|
|
|
$values[] = $projectId;
|
|
|
|
$stmt = db()->prepare(
|
|
'UPDATE projects
|
|
SET ' . implode(', ', $fields) . '
|
|
WHERE id = ?'
|
|
);
|
|
|
|
$stmt->execute($values);
|
|
|
|
if ($stmt->rowCount() === 0) {
|
|
$existsStmt = db()->prepare('SELECT id FROM projects WHERE id = ? LIMIT 1');
|
|
$existsStmt->execute([$projectId]);
|
|
|
|
if ($existsStmt->fetch()) {
|
|
jsonResponse([
|
|
'success' => true,
|
|
'project_id' => $projectId
|
|
]);
|
|
}
|
|
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Project not found or nothing changed.'
|
|
], 404);
|
|
}
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'project_id' => $projectId
|
|
]);
|
|
}
|
|
|
|
default: {
|
|
jsonResponse([
|
|
'success' => false,
|
|
'error' => 'Unknown API action.'
|
|
], 404);
|
|
}
|
|
}
|