Improve Figma synchronization to persist version author identity and handle API pagination links reliably during incremental sync jobs. Changes included: - Added support end-to-end: - New migration adds column and index. - now allows . - maps user id from Figma version payload ( / ). - snapshot query now selects . - displays Figma User ID column and updates table colspan states. - Hardened Figma pagination flow in : - Follow absolute URLs when returned by API. - Track seen pagination URLs to prevent loops. - Support additional page-size query key and URL-safe encoding. - Updated request builder to work with absolute endpoints and existing query strings. - Consolidated schema intent: - Moved Volume in drive C: has no label Volume Serial Number is 2B45-1F84/ columns into initial Figma table migration. - Removed superseded follow-up migrations that previously added/dropped these fields. - Updated project docs and dependencies: - Replaced starter README with CRM Summit specific project README. - Refreshed (framework and dependency version bumps). - Added database artifact: . Impact: - Incremental sync should now process paginated version/history feeds more reliably. - Snapshot API and dashboard expose author-level metadata for auditing and filtering. - Fresh installs get cleaner Figma schema history from baseline migration.
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.figma_user_id, 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->syncIncremental(1);
|
|
|
|
$statusCode = $result['success'] ? 200 : 500;
|
|
return $this->respond([
|
|
'status' => $result['success'] ? 'success' : 'error',
|
|
'message' => $result['message'],
|
|
'data' => $result['stats'] ?? [],
|
|
], $statusCode);
|
|
}
|
|
}
|