Skip to content

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

EnvironmentURL
Productioninteg.happ.tools
Devinteg.dev.happ.tools
Referenceinteg.happ.tools/reference
Docsinteg.docs.happ.tools
Locallocalhost: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"
  1. pnpm build — сборка через tsup
  2. pnpm sync-secrets:prod — синхронизация секретов из Doppler в Cloudflare
  3. wrangler 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 production

D1 Миграции

Все миграции (глобальные, например 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 → Logs

Tracing

Каждый запрос имеет 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. Результат записывается в CRM

Checklist перед деплоем

  • [ ] Все тесты проходят: pnpm test
  • [ ] Нет ошибок линтера: pnpm lint
  • [ ] Сборка успешна: pnpm build
  • [ ] Секреты синхронизированы
  • [ ] Миграции применены

Полезные ссылки

Связанные документы