Add Figma persistence and sync flow for one file source. - create figma_files, figma_file_versions, and figma_comments tables with supporting migrations - add FigmaSyncService for full and incremental sync, API fetch, pagination, dedupe, and upserts - add CLI commands and shell wrappers for full and incremental sync runs - expose Figma dashboard plus API endpoints for summary, snapshots, comments, and admin sync trigger - wire route and sidebar entry for dashboard access - trim legacy file_url and thumbnail_url fields, add version label/description support
190 lines
5.0 KiB
PHP
190 lines
5.0 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Api;
|
|
|
|
use App\Controllers\BaseController;
|
|
use App\Libraries\FigmaSyncService;
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
class FigmaApi extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
private function ensureLoggedIn()
|
|
{
|
|
if (!session()->get('userid')) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Unauthorized',
|
|
], 401);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function ensureAdmin()
|
|
{
|
|
$level = (int) session()->get('level');
|
|
if (!in_array($level, [0, 1, 2], true)) {
|
|
return $this->respond([
|
|
'status' => 'error',
|
|
'message' => 'Forbidden. Admin only.',
|
|
], 403);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function summary()
|
|
{
|
|
if ($response = $this->ensureLoggedIn()) {
|
|
return $response;
|
|
}
|
|
|
|
$db = \Config\Database::connect();
|
|
$file = $db->table('figma_files')->orderBy('id', 'DESC')->get()->getRowArray();
|
|
$versionsCount = $db->table('figma_file_versions')->countAllResults();
|
|
$commentsCount = $db->table('figma_comments')->countAllResults();
|
|
$latestVersion = $db->table('figma_file_versions')->orderBy('created_at_figma', 'DESC')->get()->getRowArray();
|
|
$latestComment = $db->table('figma_comments')->selectMax('created_at_figma', 'latest')->get()->getRowArray();
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Summary fetched',
|
|
'data' => [
|
|
'file' => $file,
|
|
'versions' => $versionsCount,
|
|
'comments' => $commentsCount,
|
|
'latest_version_at' => $latestVersion['created_at_figma'] ?? null,
|
|
'latest_version_label' => $latestVersion['label'] ?? $latestVersion['version'] ?? null,
|
|
'latest_version_description' => $latestVersion['description'] ?? null,
|
|
'latest_comment_at' => $latestComment['latest'] ?? null,
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
private function getPaginationParams(): array
|
|
{
|
|
$page = (int) ($this->request->getGet('page') ?? 1);
|
|
if ($page <= 0) {
|
|
$page = 1;
|
|
}
|
|
|
|
$perPage = (int) ($this->request->getGet('per_page') ?? $this->request->getGet('limit') ?? 25);
|
|
if ($perPage <= 0 || $perPage > 100) {
|
|
$perPage = 25;
|
|
}
|
|
|
|
return [$page, $perPage];
|
|
}
|
|
|
|
public function snapshots()
|
|
{
|
|
if ($response = $this->ensureLoggedIn()) {
|
|
return $response;
|
|
}
|
|
|
|
$startDate = $this->request->getGet('start_date');
|
|
$endDate = $this->request->getGet('end_date');
|
|
[$page, $perPage] = $this->getPaginationParams();
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
$db = \Config\Database::connect();
|
|
$baseBuilder = $db->table('figma_file_versions v')
|
|
->join('figma_files f', 'f.id = v.file_id', 'inner');
|
|
|
|
if (!empty($startDate)) {
|
|
$baseBuilder->where('v.created_at_figma >=', $startDate . ' 00:00:00');
|
|
}
|
|
|
|
if (!empty($endDate)) {
|
|
$baseBuilder->where('v.created_at_figma <=', $endDate . ' 23:59:59');
|
|
}
|
|
|
|
$total = (int) (clone $baseBuilder)->countAllResults();
|
|
$rows = $baseBuilder
|
|
->select('v.id, v.figma_version_id, v.version, v.label, v.description, v.name, v.editor_type, v.last_modified_figma, v.created_at_figma, f.file_key, f.last_synced_at')
|
|
->orderBy('v.created_at_figma', 'DESC')
|
|
->limit($perPage, $offset)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Snapshots fetched',
|
|
'data' => $rows,
|
|
'meta' => [
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'total_pages' => $perPage > 0 ? (int) ceil($total / $perPage) : 0,
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
public function comments()
|
|
{
|
|
if ($response = $this->ensureLoggedIn()) {
|
|
return $response;
|
|
}
|
|
|
|
$startDate = $this->request->getGet('start_date');
|
|
$endDate = $this->request->getGet('end_date');
|
|
[$page, $perPage] = $this->getPaginationParams();
|
|
$offset = ($page - 1) * $perPage;
|
|
|
|
$db = \Config\Database::connect();
|
|
$baseBuilder = $db->table('figma_comments c')
|
|
->join('figma_files f', 'f.id = c.file_id', 'inner');
|
|
|
|
if (!empty($startDate)) {
|
|
$baseBuilder->where('c.created_at_figma >=', $startDate . ' 00:00:00');
|
|
}
|
|
|
|
if (!empty($endDate)) {
|
|
$baseBuilder->where('c.created_at_figma <=', $endDate . ' 23:59:59');
|
|
}
|
|
|
|
$total = (int) (clone $baseBuilder)->countAllResults();
|
|
$rows = $baseBuilder
|
|
->select('c.id, c.figma_comment_id, c.user_name, c.message, c.is_resolved, c.resolved_at, c.created_at_figma, c.client_meta_json, f.file_key')
|
|
->orderBy('c.created_at_figma', 'DESC')
|
|
->limit($perPage, $offset)
|
|
->get()
|
|
->getResultArray();
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Comments fetched',
|
|
'data' => $rows,
|
|
'meta' => [
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'per_page' => $perPage,
|
|
'total_pages' => $perPage > 0 ? (int) ceil($total / $perPage) : 0,
|
|
],
|
|
], 200);
|
|
}
|
|
|
|
public function sync()
|
|
{
|
|
if ($response = $this->ensureLoggedIn()) {
|
|
return $response;
|
|
}
|
|
|
|
if ($response = $this->ensureAdmin()) {
|
|
return $response;
|
|
}
|
|
|
|
$service = new FigmaSyncService();
|
|
$result = $service->syncAll();
|
|
|
|
$statusCode = $result['success'] ? 200 : 500;
|
|
return $this->respond([
|
|
'status' => $result['success'] ? 'success' : 'error',
|
|
'message' => $result['message'],
|
|
'data' => $result['stats'] ?? [],
|
|
], $statusCode);
|
|
}
|
|
}
|