Files
Projectkiln/ProjectKiln/api/project.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);
}
}