N8N_BACKEND.md•11.4 kB
# Бэкенд Human Design на n8n
## Полная архитектура
### Схема работы:
```
Клиент → n8n Webhook → Валидация → Railway API (Swiss Ephemeris) → Обогащение → Форматирование → Ответ клиенту
```
**Преимущества:**
- ✅ Вся бизнес-логика в n8n
- ✅ Централизованное логирование
- ✅ Легко добавить дополнительные проверки
- ✅ Swiss Ephemeris расчеты на Railway
- ✅ Можно добавить БД, кэш, rate limiting
## Развертывание
### 1. Импорт workflow
Импортируйте `n8n-backend-workflow.json` в n8n.
### 2. Настройка переменных окружения
В n8n Settings → Environment Variables:
```
RAILWAY_API_URL=https://balanced-generosity-test.up.railway.app
```
### 3. Активация
Активируйте workflow в n8n.
## Workflow узлы
### 1. Webhook - Main Entry
- Метод: POST
- Path: `/human-design`
- Получает JSON с birthDate, birthTime, birthLocation
### 2. Validate Input
Проверяет:
- Все поля заполнены
- Формат даты: YYYY-MM-DD
- Формат времени: HH:MM
### 3. Is Valid?
IF node для ветвления логики
### 4. Calculate Swiss Ephemeris
HTTP Request к Railway API
### 5. Calculation Success?
IF node для проверки результата
### 6. Enrich Data
Добавляет:
- Характеристики типа
- Статистику
- Ключевые ворота
- Краткое резюме
### 7. Format Output
Форматирует для красивого вывода клиенту
### 8. Respond Success / Respond Error
Отправляет результат обратно клиенту
## Использование
### Запрос
```bash
curl -X POST https://your-n8n-instance.com/webhook/human-design-backend \
  -H "Content-Type: application/json" \
  -d '{
    "birthDate": "1990-05-15",
    "birthTime": "14:30",
    "birthLocation": "Москва, Россия"
  }'
```
### Ответ
```json
{
  "basic": {
    "type": "Проектор (Projector)",
    "strategy": "Ждать приглашения",
    "authority": "Эмоциональная авторитет (Emotional)",
    "profile": "3/4"
  },
  "type_info": {
    "name": "Проектор",
    "description": "Проекторы имеют сосредоточенную ауру...",
    "strategy": "Ждать приглашения"
  },
  "prominent_gates": [
    {
      "gate": 19,
      "planet": "Sun",
      "name": "Подход",
      "line": 2
    }
  ],
  "defined_centers": ["SolarPlexus", "Throat", "Ajna"],
  "stats": {
    "total_gates": 9,
    "defined_centers_count": 6,
    "open_centers_count": 3
  },
  "source": "Swiss Ephemeris"
}
```
## Добавление дополнительной логики
### Сохранение в БД
Добавьте узел после "Enrich Data":
```javascript
// Save to Database node
const hd = $input.item.json;
// Подключение к БД (PostgreSQL)
const query = `
  INSERT INTO human_design_charts 
  (birth_date, birth_time, birth_location, type, strategy, profile, gates, created_at)
  VALUES 
  ($1, $2, $3, $4, $5, $6, $7, NOW())
  RETURNING id
`;
return await executeQuery(query, [
  hd.birthDate,
  hd.birthTime,
  hd.birthLocation,
  hd.type.name,
  hd.strategy,
  hd.profile.number,
  JSON.stringify(hd.gates)
]);
```
### Кэширование
Добавьте Redis узел:
```javascript
// Cache check (перед "Calculate Swiss Ephemeris")
const cacheKey = `hd:${birthDate}:${birthTime}:${birthLocation}`;
const cached = await redis.get(cacheKey);
if (cached) {
  return { json: JSON.parse(cached) };
}
// После "Format Output" - save to cache
await redis.setex(cacheKey, 86400, JSON.stringify(hd)); // 24 часа
```
### Rate Limiting
Добавьте проверку после "Validate Input":
```javascript
// Rate limit per IP
const clientIP = $json.headers?.['x-forwarded-for'] || 'unknown';
const key = `rate:${clientIP}`;
const count = await redis.incr(key);
if (count === 1) {
  await redis.expire(key, 3600); // 1 час
}
if (count > 100) { // max 100 requests/hour
  return {
    json: {
      success: false,
      error: 'Rate limit exceeded',
      code: 'RATE_LIMIT'
    }
  };
}
```
### Авторизация
Добавьте API Key проверку:
```javascript
// Check API Key (после Webhook)
const apiKey = $json.headers?.['x-api-key'] || $json.apiKey;
const validKey = process.env.API_KEYS?.split(',').includes(apiKey);
if (!validKey) {
  return {
    json: {
      success: false,
      error: 'Invalid API key',
      code: 'UNAUTHORIZED'
    }
  };
}
```
### Логирование
Добавьте Log узел после каждого важного шага:
```javascript
// Log calculation
console.log(JSON.stringify({
  event: 'human_design_calculated',
  birthDate: hd.birthDate,
  type: hd.type.name,
  timestamp: new Date().toISOString()
}));
```
### Email уведомления
Для важных событий:
```javascript
// After error
await sendEmail({
  to: 'admin@example.com',
  subject: 'HD Calculation Failed',
  body: `Error: ${error.message}\nRequest: ${JSON.stringify(request)}`
});
```
## Мониторинг
### Метрики
Добавьте сбор метрик:
```javascript
// After Format Output
await updateMetrics({
  calculations_total: 1,
  calculations_by_type: { [hd.type.name]: 1 },
  avg_gates_count: hd.gates.length
});
```
### Алерты
Настройте Slack/Telegram для критических ошибок:
```javascript
if (error.message.includes('Railway')) {
  await sendSlackMessage({
    channel: '#alerts',
    text: `🚨 Railway API failed for HD calculation`
  });
}
```
## Production настройки
### Переменные окружения
```env
RAILWAY_API_URL=https://balanced-generosity-test.up.railway.app
API_KEYS=key1,key2,key3
REDIS_URL=redis://localhost:6379
DB_CONNECTION_STRING=postgresql://...
SLACK_WEBHOOK=https://hooks.slack.com/...
LOG_LEVEL=info
```
### Error Handling
Всегда логируйте ошибки:
```javascript
try {
  // logic
} catch (error) {
  console.error('HD Calculation Error:', {
    error: error.message,
    stack: error.stack,
    input: $input.item.json,
    timestamp: new Date().toISOString()
  });
  throw error;
}
```
### Retry logic
Для Railway API:
```javascript
// Retry on failure
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
  try {
    const result = await callRailway();
    return result;
  } catch (error) {
    attempt++;
    if (attempt >= maxRetries) throw error;
    await sleep(1000 * attempt); // exponential backoff
  }
}
```
## Тестирование
### Unit тесты (в n8n)
Создайте тестовый workflow:
```javascript
// Test validate
const testCases = [
  { input: {}, expected: 'VALIDATION_ERROR' },
  { input: { birthDate: 'invalid' }, expected: 'INVALID_DATE' },
  { input: { birthDate: '1990-05-15', birthTime: '14:30', birthLocation: 'Москва' }, expected: 'success' }
];
for (const test of testCases) {
  const result = await validateInput(test.input);
  assert(result.code === test.expected);
}
```
## Дополнительные endpoints
### GET /health
```javascript
app.get('/health', (req, res) => {
  res.json({
    status: 'ok',
    service: 'human-design-backend',
    railway: await checkRailwayHealth()
  });
});
```
### GET /types
```javascript
// Вернуть список всех типов Human Design
const types = [
  { name: 'Generator', ru_name: 'Генератор', strategy: 'Отвечать' },
  { name: 'Manifestor', ru_name: 'Манифестор', strategy: 'Информировать' },
  // ...
];
return types;
```
## Архитектура
```
┌─────────────┐
│   Client    │
└──────┬──────┘
       │
       ↓ HTTP POST
┌─────────────────┐
│  n8n Webhook    │ ◄─── API Key
└──────┬──────────┘
       │
       ↓
┌─────────────────┐
│ Validate Input  │ ◄─── Input validation
└──────┬──────────┘
       │
       ↓
┌──────────────────────────┐
│ Calculate Swiss Ephemeris│ ◄─── Railway API
└──────┬───────────────────┘
       │
       ↓
┌─────────────────┐
│  Enrich Data    │ ◄─── Add insights
└──────┬──────────┘
       │
       ↓
┌─────────────────┐
│ Format Output   │ ◄─── Format response
└──────┬──────────┘
       │
       ↓
┌─────────────────┐
│  Save to DB     │ ◄─── PostgreSQL (optional)
└──────┬──────────┘
       │
       ↓
┌─────────────────┐
│  Cache Result   │ ◄─── Redis (optional)
└──────┬──────────┘
       │
       ↓
┌─────────────────┐
│ Respond to Webhook
└─────────────────┘
```
## Рекомендации
1. **Используйте Railway** для Swiss Ephemeris (тяжелые расчеты)
2. **Держите бизнес-логику в n8n** (валидация, обогащение, форматирование)
3. **Добавьте кэширование** для частых запросов
4. **Логируйте все** для мониторинга
5. **Настройте алерты** для критических ошибок
6. **Добавьте rate limiting** для защиты от спама
7. **Используйте БД** для истории расчетов
## Примеры расширения
### Telegram Bot
Добавьте Telegram Trigger перед workflow:
```javascript
// Telegram command /hd
const message = $input.item.json.message.text;
const match = message.match(/(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+(.+)/);
if (!match) {
  // Show help
  return { chat_id, text: 'Формат: /hd YYYY-MM-DD HH:MM Город' };
}
// Continue to main workflow
```
### Analytics Dashboard
После каждого расчета отправляйте в analytics:
```javascript
await fetch('https://analytics.example.com/events', {
  method: 'POST',
  body: JSON.stringify({
    event: 'hd_calculation',
    properties: {
      type: hd.type.name,
      profile: hd.profile.number,
      timestamp: Date.now()
    }
  })
});
```
## Поддержка
- См. `N8N_SETUP.md` для базовой интеграции
- См. `RAILWAY_DEPLOY.md` для настройки Railway
- См. `EXAMPLES.md` для примеров использования