Production Workflow
Обзор
На production интеграции работают как Cloudflare Workers.
Архитектура деплоя
┌─────────────────────────────────────┐
│ Cloudflare Edge │
│ │
External ──────> │ ┌─────────────────────────────┐ │
Request │ │ Gateway Worker │ │
│ │ │ │
│ │ integ.happ.tools/* │ │
│ │ • Auth (x-access-token) │ │
│ │ • Rate limiting │ │
│ │ • Routing │ │
│ └─────────────┬───────────────┘ │
│ │ │
│ Service Binding │
│ │ │
│ ┌─────────────▼───────────────┐ │
│ │ Integration Worker │ │
│ │ (sofa) │ │
│ │ │ │
│ │ • Webhook handler │ │
│ │ • Handler execution │ │
│ │ • CRM API / LLM / DB │ │
│ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────┘Полный Workflow вызова
Шаг 1: Внешний запрос → Gateway
http
POST https://integ.happ.tools/sofa/webhook/webinar-call-originate
Headers:
x-access-token: <gateway_token>
Content-Type: application/json
Body:
{
"phone_number": "+380501234567"
}Шаг 2: Gateway обработка
typescript
// workers/gateway/src/index.ts
// 1. Auth check
const token = req.header("x-access-token");
if (token !== env.ACCESS_TOKEN) return 401;
// 2. Rate limiting (100 req/min per IP)
if (rateLimitExceeded) return 429;
// 3. Generate trace_id
const traceId = crypto.randomUUID();
c.header("x-trace-id", traceId);
// 4. Route via Service Binding
const response = await env.SOFA.fetch(request);Шаг 3: Integration Worker → Handler
typescript
// integrations/sofa/src/index.ts — используется createIntegration()
import { createIntegration } from "@happ-integ/core";
import * as handlers from "./handlers";
export default createIntegration({
name: "sofa",
handlers: Object.values(handlers),
migrations: SOFA_MIGRATIONS,
secrets: ["NETHUNT_EMAIL", "NETHUNT_API_KEY", "NETHUNT_FOLDER_ID", "SAAS_ASSISTANT_ID"],
scheduled: [{ cron: "0 * * * *", handlerName: "retry", payload: { triggered_by: "cron" } }],
});Шаг 4: Handler выполнение (defineHandler)
typescript
// integrations/sofa/src/handlers/webinar-call-originate.ts
export const webinarCallOriginateHandler = defineHandler({
name: "webinar-call-originate",
retries: 3,
endpoint: "POST /webhook/webinar-call-originate",
concurrency: { limit: 3 },
async handler({ payload, env, step, creds, integrationName }) {
const { phone_number } = payload;
// 1. Получить credentials из D1
const sofaCreds = await step.run("creds.get", () => creds.get<ISofaCredentials>(integrationName));
// 2. Инициировать звонок через SaaS API
const saasClient = new SaasApiClient(env.SAAS_API_URL, env.SAAS_ACCESS_TOKEN);
await step.run("saas.originate", () =>
saasClient.originateCall(sofaCreds.SAAS_ASSISTANT_ID, phone_number)
);
return { success: true };
},
});Environments
| Environment | URL |
|---|---|
| Production | integ.happ.tools |
| Dev | integ.dev.happ.tools |
| Reference | integ.happ.tools/reference |
| Docs | integ.docs.happ.tools |
| Local | localhost:8787 |
Примечание: Домены
integ.happ.toolsиinteg.dev.happ.toolsнастраиваются как Custom Domains в Cloudflare Dashboard (не через routes в wrangler.toml). См. SETUP.md → Custom Domains
Деплой
Команды деплоя
bash
# Production
pnpm --filter sofa deploy
# Dev environment
pnpm --filter sofa deploy:dev
# Быстрый deploy (без sync secrets)
pnpm --filter sofa deploy:dev:fast
# Все интеграции
pnpm deploy:prodЧто делает deploy
bash
# package.json интеграции
"deploy": "pnpm build && pnpm sync-secrets:prod && wrangler deploy"pnpm build— сборка через tsuppnpm sync-secrets:prod— синхронизация секретов из Doppler в Cloudflarewrangler deploy— деплой на Cloudflare Workers
Sync Secrets
bash
# Dev
pnpm --filter sofa sync-secrets:dev
# Production
pnpm --filter sofa sync-secrets:prodРучная установка секретов
bash
# Через wrangler
wrangler secret put CRYPTO_KEY --env production
wrangler secret put CRYPTO_SALT --env productionD1 Миграции
Все миграции (глобальные, например creds, и integration-specific) являются code-based и применяются автоматически при вызове POST /{integration}/setup. SQL-файлы и ручные команды wrangler d1 execute / wrangler d1 migrations apply не используются.
Health Checks
bash
# Gateway
curl https://integ.happ.tools/healthОтвет:
json
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00.000Z",
"service": "integ-gateway",
"environment": "production"
}Мониторинг
Логирование
Все логи в JSON формате:
json
{
"level": "info",
"type": "gateway_request",
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"method": "POST",
"path": "/sofa/webhook/webinar-call-originate",
"timestamp": "2024-01-15T10:30:00.000Z"
}Просмотр логов
bash
# Real-time
wrangler tail integ-sofa --env production
# Cloudflare Dashboard
# → Workers & Pages → integ-sofa → LogsTracing
Каждый запрос имеет x-trace-id header для сквозного трейсинга.
Error Handling
LLM Fallback
Primary (OpenAI) → error → Secondary (Claude) → error → throwЛогируется warning при fallback:
Primary provider (openai) failed, falling back to claude: [error]Rate Limiting
- Gateway: 100 req/min per IP (in-memory)
Secrets Management
Структура
Doppler (global) D1 Creds (per-integration)
├── CRYPTO_KEY / CRYPTO_SALT ├── sofa/
├── GROQ_API_KEY │ ├── NETHUNT_EMAIL
├── SAAS_API_URL │ ├── NETHUNT_API_KEY
├── SAAS_ACCESS_TOKEN │ ├── NETHUNT_FOLDER_ID
└── ACCESS_TOKEN │ └── SAAS_ASSISTANT_ID
├── booking/
│ └── ...Добавление credentials
typescript
// Через CLI или admin endpoint
const creds = new Creds({ d1, masterKey });
await creds.set("sofa", "NEW_API_KEY", "secret-value");
// Просмотр ключей
const keys = await creds.listKeys("sofa");
// ["NETHUNT_EMAIL", "NETHUNT_API_KEY", "NEW_API_KEY"]Типичные сценарии
Outbound Call Flow
1. Запрос на /webhook/webinar-call-originate с номером телефона
2. Gateway проверяет auth, роутит на sofa worker
3. Handler инициирует звонок через SaaS API Voice Assistant
4. Voice Assistant отправляет media_start → webinar-call-events создаёт запись
5. Voice Assistant отправляет media_end → webinar-call-events определяет результат:
- duration < 5s → pending_retry (retry по расписанию 10:40/13:40/16:40)
- duration >= 5s → waiting_postcall
6. Post-call webhook → webinar-agent-post-call:
- AI-анализ транскрипции через Claude
- Обновление CRM с квалификацией, резюме, этапом
7. Cron каждый час проверяет pending_retry, повторяет (max 3 попытки)Voice Call Flow
1. POST /webhook/voice-init
2. Handler получает данные клиента
3. LLM генерирует скрипт
4. Voice API инициирует звонок
5. При завершении → POST /webhook/voice-end
6. Результат записывается в CRMChecklist перед деплоем
- [ ] Все тесты проходят:
pnpm test - [ ] Нет ошибок линтера:
pnpm lint - [ ] Сборка успешна:
pnpm build - [ ] Секреты синхронизированы
- [ ] Миграции применены
Полезные ссылки
- Cloudflare Dashboard: https://dash.cloudflare.com
- Doppler: https://dashboard.doppler.com
Связанные документы
- ARCHITECTURE.md — архитектура системы
- DEVELOPMENT.md — локальная разработка
- MONITORING.md — подробнее о мониторинге
- ENV_CONFIG.md — переменные окружения