feat: complete dashcam catalog ingestion demo
Master Production Deploy / production (push) Failing after 49s
Master Production Deploy / production (push) Failing after 49s
This commit is contained in:
+34
-28
@@ -74,7 +74,10 @@ Headless browser допустим только как ручной diagnostic to
|
||||
|
||||
## 4. Deployment Model
|
||||
|
||||
Ingestion живет отдельно от облачного runtime приложения.
|
||||
Ingestion деплоится в то же облако, что и web-приложение (один Swarm/Portainer
|
||||
environment), а не на отдельный сервер. Раньше предполагался отдельный cloud, но
|
||||
управлять двумя независимыми облаками оказалось слишком сложно. См. решение
|
||||
`videoreg-nnv.3`.
|
||||
|
||||
Код хранится в этом же репозитории:
|
||||
|
||||
@@ -86,18 +89,20 @@ ingestion/
|
||||
Dockerfile
|
||||
```
|
||||
|
||||
Стек ingestion деплоится отдельным `docker-compose.yml`.
|
||||
|
||||
Минимальные сервисы:
|
||||
|
||||
```text
|
||||
postgres
|
||||
ingestion-worker
|
||||
ingestion-admin
|
||||
export-server
|
||||
```
|
||||
|
||||
Основное Next.js-приложение не подключается напрямую к ingestion DB.
|
||||
`export-server` больше не нужен: ingestion-worker пишет валидированный
|
||||
`catalog.json` + `manifest.json` в общий (shared) export-том, а web читает их
|
||||
напрямую с этого тома. Никакого HTTP-экспорта и Bearer-токена.
|
||||
|
||||
Основное Next.js-приложение по-прежнему не подключается напрямую к ingestion DB —
|
||||
единственный контакт с каталогом идёт через export-артефакт на shared-томе.
|
||||
|
||||
---
|
||||
|
||||
@@ -107,29 +112,27 @@ Source of truth для каталога: PostgreSQL ingestion stack.
|
||||
|
||||
Текущее примитивное хранилище приложения (`data/videoreg.json`) является только read-model cache.
|
||||
|
||||
Bridge работает со стороны приложения:
|
||||
Bridge работает со стороны приложения через общий export-том:
|
||||
|
||||
1. ingestion публикует versioned HTTPS export;
|
||||
2. приложение вручную запускает operator import command;
|
||||
3. import command скачивает manifest;
|
||||
4. проверяет `schemaVersion`, `exportId`, `createdAt`, `sha256`, `itemCount`;
|
||||
5. скачивает catalog artifact;
|
||||
1. ingestion пишет versioned export (`manifest.json` + `catalog.json`) на shared-том;
|
||||
2. приложение/сборка читает `manifest.json` с того же тома;
|
||||
3. проверяет `schemaVersion`, `exportId`, `createdAt`, `sha256`, `itemCount`;
|
||||
4. читает `catalog.json` с тома;
|
||||
5. сверяет `sha256` артефакта;
|
||||
6. валидирует payload через shared Zod schema;
|
||||
7. атомарно заменяет `data/videoreg.json`.
|
||||
7. атомарно заменяет `data/videoreg.json` (или используется как build-time источник).
|
||||
|
||||
В MVP import запускается вручную оператором, не по расписанию.
|
||||
В MVP import запускается вручную оператором, не по расписанию. HTTP/токен в этом
|
||||
обмене не участвуют — оба сервиса в одном облаке и делят том.
|
||||
|
||||
---
|
||||
|
||||
## 6. Export Contract
|
||||
|
||||
Export доступен по HTTPS со статичным токеном:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Токен передается через env import-команды. Токен не передается в query string.
|
||||
Export публикуется как файлы на общем (shared) export-томе: `manifest.json` и
|
||||
`artifacts/<exportId>/catalog.json`. HTTP-эндпоинт и Bearer-токен не используются —
|
||||
ingestion и web живут в одном облаке и делят том. Целостность гарантируется
|
||||
`sha256` в манифесте и shared Zod-схемой при чтении.
|
||||
|
||||
### Manifest
|
||||
|
||||
@@ -138,7 +141,7 @@ type CatalogExportManifest = {
|
||||
schemaVersion: 1;
|
||||
exportId: string;
|
||||
createdAt: string;
|
||||
artifactUrl: string;
|
||||
artifactPath: string;
|
||||
sha256: string;
|
||||
itemCount: number;
|
||||
};
|
||||
@@ -158,6 +161,7 @@ type ExportedDashcamProduct = {
|
||||
id: string;
|
||||
brand: string;
|
||||
model: string;
|
||||
canonicalName?: string;
|
||||
title: string;
|
||||
specs: Record<string, string | number | boolean>;
|
||||
images: ExportedProductImage[];
|
||||
@@ -169,19 +173,21 @@ type ExportedDashcamProduct = {
|
||||
type ExportedProductImage = {
|
||||
url: string;
|
||||
originalUrl: string;
|
||||
source: "yandex_market";
|
||||
source: MarketSource;
|
||||
sha256?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
type ExportedProductSource = {
|
||||
source: "yandex_market";
|
||||
source: MarketSource;
|
||||
sourceProductId?: string;
|
||||
url: string;
|
||||
stableContentHash: string;
|
||||
fetchedAt: string;
|
||||
};
|
||||
|
||||
type MarketSource = "yandex_market" | "mvideo";
|
||||
```
|
||||
|
||||
Runtime validation: shared Zod schema в репозитории. TypeScript-типы без runtime-валидации недостаточны.
|
||||
@@ -260,7 +266,7 @@ Category pages используются вторично:
|
||||
|
||||
```ts
|
||||
type DiscoveredProduct = {
|
||||
source: "yandex_market";
|
||||
source: MarketSource;
|
||||
categoryUrl?: string;
|
||||
productUrl: string;
|
||||
sourceProductId?: string;
|
||||
@@ -344,7 +350,7 @@ JSON-LD > embedded state > semantic DOM > fallback DOM
|
||||
|
||||
```ts
|
||||
type ParsedMarketProduct = {
|
||||
source: "yandex_market";
|
||||
source: MarketSource;
|
||||
sourceProductId?: string;
|
||||
title: string;
|
||||
brand?: string;
|
||||
@@ -372,7 +378,7 @@ type ParsedMarketProduct = {
|
||||
|
||||
```ts
|
||||
type ProductSourceSnapshot = {
|
||||
source: "yandex_market";
|
||||
source: MarketSource;
|
||||
url: string;
|
||||
fetchedAt: string;
|
||||
httpStatus: number;
|
||||
@@ -623,7 +629,7 @@ export_runs
|
||||
* publish versioned manifest
|
||||
* publish versioned catalog artifact
|
||||
* publish versioned image artifacts
|
||||
* protect endpoints with Bearer token
|
||||
* write export artifacts to the shared export volume (no HTTP, no token)
|
||||
* verify checksum in app import
|
||||
* atomic replace of `data/videoreg.json`
|
||||
|
||||
@@ -633,7 +639,7 @@ export_runs
|
||||
|
||||
Система считается готовой, если:
|
||||
|
||||
* ingestion stack запускается отдельно через `docker-compose.yml`
|
||||
* ingestion и web деплоятся в одно облако; export идёт через shared-том, без HTTP/токена
|
||||
* PostgreSQL является source of truth
|
||||
* приложение импортирует только approved canonical catalog
|
||||
* `data/videoreg.json` заменяется только после manifest/checksum/schema validation
|
||||
|
||||
Reference in New Issue
Block a user