From ee7e515f61001f5c1bd2ff25d334aaf2ecd45759 Mon Sep 17 00:00:00 2001 From: HotelSync Date: Thu, 2 Apr 2026 11:10:40 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20TTLock=20=E2=80=94=20scan=20TTHotel=20s?= =?UTF-8?q?ources=20for=20API=20config=20(CE=5FConfigServer)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agent: ttlock:find_api_config command scans TTHotel app JS files for CE_ConfigServer calls, oauth2 endpoints, API server URLs - Backend: POST /ttlock/find-api-config route - UI: Search button on each workstation row to trigger scan Co-Authored-By: Claude Sonnet 4.6 --- backend/src/routes/ttlock.ts | 29 +++++++++++++++++++++++++++++ src/lib/api.ts | 3 +++ src/pages/TTLockPage.tsx | 31 ++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/backend/src/routes/ttlock.ts b/backend/src/routes/ttlock.ts index fdcf2cc..ebcff79 100644 --- a/backend/src/routes/ttlock.ts +++ b/backend/src/routes/ttlock.ts @@ -131,6 +131,35 @@ const ttlockRoutes: FastifyPluginAsync = async (fastify) => { }, ) + // ── POST /api/hotels/:slug/ttlock/find-api-config ─────────────────────────── + // Ищет в исходниках TTHotel вызовы CE_ConfigServer и API-сервер + fastify.post( + '/api/hotels/:slug/ttlock/find-api-config', + { onRequest: [fastify.authenticate] }, + async (req, reply) => { + const { slug } = req.params + if (!canManage(req.user.hotelSlug, req.user.role, slug)) + return reply.code(403).send({ error: 'Forbidden' }) + const hotelId = await getHotelId(slug) + if (!hotelId) return reply.code(404).send({ error: 'Hotel not found' }) + + const { workstation_id } = req.body + if (!workstation_id) return reply.code(400).send({ error: 'workstation_id required' }) + + try { + const result = await sendCommandAndWait(workstation_id, { + type: 'ttlock:find_api_config', + }, 20_000) as { ok?: boolean; appDir?: string; results?: { path: string; matches: string[] }[]; error?: string } + + if (!result.ok) return reply.code(502).send({ error: result.error ?? 'Ничего не найдено', appDir: result.appDir }) + return { ok: true, appDir: result.appDir, results: result.results } + } catch (err) { + const msg = err instanceof Error ? err.message : 'Агент не ответил' + return reply.code(502).send({ error: msg }) + } + }, + ) + // ── POST /api/hotels/:slug/ttlock/test-api ────────────────────────────────── // Проверяет подключение к TTLock Cloud API (OAuth + список замков) fastify.post( diff --git a/src/lib/api.ts b/src/lib/api.ts index 310b620..cda68cb 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -619,6 +619,9 @@ export const api = { testApi: (slug: string, workstationId: string) => req<{ ok: boolean; lockCount?: number }>('POST', `/api/hotels/${slug}/ttlock/test-api`, { workstation_id: workstationId }), + findApiConfig: (slug: string, workstationId: string) => + req<{ ok: boolean; appDir?: string; results?: { path: string; matches: string[] }[] }>('POST', `/api/hotels/${slug}/ttlock/find-api-config`, { workstation_id: workstationId }), + dllInfo: (slug: string, workstationId: string) => req<{ ok: boolean; content: string; path: string }>('GET', `/api/hotels/${slug}/ttlock/dll-info?workstation_id=${workstationId}`), diff --git a/src/pages/TTLockPage.tsx b/src/pages/TTLockPage.tsx index f35bce0..e081047 100644 --- a/src/pages/TTLockPage.tsx +++ b/src/pages/TTLockPage.tsx @@ -3,7 +3,7 @@ import * as XLSX from 'xlsx' import { KeyRound, Save, RefreshCw, Check, X, AlertCircle, ChevronDown, ChevronRight, Trash2, Plus, Eye, EyeOff, - Lock, Unlock, Info, HardDriveDownload, Zap, Terminal, Upload, Globe, + Lock, Unlock, Info, HardDriveDownload, Zap, Terminal, Upload, Globe, Search, } from 'lucide-react' import { api, type TTLockConfig, type RoomLockMapping, type Workstation } from '../lib/api' import { useAuth } from '../contexts/AuthContext' @@ -633,6 +633,35 @@ export function TTLockPage() { > + + {/* Find TTHotel API config */} + ) })}