cleanup
This commit is contained in:
514
bitbylaw/docs/API.md
Normal file
514
bitbylaw/docs/API.md
Normal file
@@ -0,0 +1,514 @@
|
||||
# API Reference
|
||||
|
||||
---
|
||||
title: API Reference
|
||||
description: Vollständige API-Dokumentation für bitbylaw Motia Installation
|
||||
date: 2026-02-07
|
||||
version: 1.1.0
|
||||
---
|
||||
|
||||
## Base URL
|
||||
|
||||
**Production (via KONG)**: `https://api.bitbylaw.com`
|
||||
**Development**: `http://localhost:3000`
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
### KONG API Gateway
|
||||
|
||||
Alle Produktions-API-Calls laufen über KONG mit API-Key-Authentifizierung:
|
||||
|
||||
```bash
|
||||
curl -H "apikey: YOUR_API_KEY" https://api.bitbylaw.com/advoware/proxy?endpoint=employees
|
||||
```
|
||||
|
||||
**Header**: `apikey: <your-api-key>`
|
||||
|
||||
### Development
|
||||
|
||||
Entwicklungs-Environment: Keine Authentifizierung auf Motia-Ebene erforderlich.
|
||||
|
||||
---
|
||||
|
||||
## Advoware Proxy API
|
||||
|
||||
### Universal Proxy Endpoint
|
||||
|
||||
Alle Advoware-API-Aufrufe laufen über einen universellen Proxy.
|
||||
|
||||
#### GET Request
|
||||
|
||||
**Endpoint**: `GET /advoware/proxy`
|
||||
|
||||
**Query Parameters**:
|
||||
- `endpoint` (required): Advoware API endpoint (ohne Base-URL)
|
||||
- Alle weiteren Parameter werden an Advoware weitergeleitet
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl -X GET "http://localhost:3000/advoware/proxy?endpoint=employees&limit=10"
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"result": {
|
||||
"data": [...],
|
||||
"total": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### POST Request
|
||||
|
||||
**Endpoint**: `POST /advoware/proxy`
|
||||
|
||||
**Query Parameters**:
|
||||
- `endpoint` (required): Advoware API endpoint
|
||||
|
||||
**Request Body**: JSON data für Advoware API
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/advoware/proxy?endpoint=appointments" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"datum": "2026-02-10",
|
||||
"uhrzeitVon": "09:00:00",
|
||||
"text": "Meeting"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"result": {
|
||||
"id": "12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT Request
|
||||
|
||||
**Endpoint**: `PUT /advoware/proxy`
|
||||
|
||||
**Query Parameters**:
|
||||
- `endpoint` (required): Advoware API endpoint (inkl. ID)
|
||||
|
||||
**Request Body**: JSON data für Update
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl -X PUT "http://localhost:3000/advoware/proxy?endpoint=appointments/12345" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"text": "Updated Meeting"
|
||||
}'
|
||||
```
|
||||
|
||||
#### DELETE Request
|
||||
|
||||
**Endpoint**: `DELETE /advoware/proxy`
|
||||
|
||||
**Query Parameters**:
|
||||
- `endpoint` (required): Advoware API endpoint (inkl. ID)
|
||||
|
||||
**Example**:
|
||||
```bash
|
||||
curl -X DELETE "http://localhost:3000/advoware/proxy?endpoint=appointments/12345"
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"body": {
|
||||
"result": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error Responses
|
||||
|
||||
**400 Bad Request**:
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"body": {
|
||||
"error": "Endpoint required as query param"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**500 Internal Server Error**:
|
||||
```json
|
||||
{
|
||||
"status": 500,
|
||||
"body": {
|
||||
"error": "Internal server error",
|
||||
"details": "Error message"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Calendar Sync API
|
||||
|
||||
### Trigger Full Sync
|
||||
|
||||
**Endpoint**: `POST /advoware/calendar/sync`
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"kuerzel": "ALL",
|
||||
"full_content": true
|
||||
}
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `kuerzel` (optional): Mitarbeiter-Kürzel oder "ALL" (default: "ALL")
|
||||
- `full_content` (optional): Volle Details vs. anonymisiert (default: true)
|
||||
|
||||
**Examples**:
|
||||
|
||||
Sync all employees:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full_content": true}'
|
||||
```
|
||||
|
||||
Sync single employee:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"kuerzel": "SB", "full_content": true}'
|
||||
```
|
||||
|
||||
Sync with anonymization:
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full_content": false}'
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "triggered",
|
||||
"kuerzel": "ALL",
|
||||
"message": "Calendar sync triggered for ALL"
|
||||
}
|
||||
```
|
||||
|
||||
**Status Codes**:
|
||||
- `200`: Sync triggered successfully
|
||||
- `400`: Invalid request (z.B. lock aktiv)
|
||||
- `500`: Internal error
|
||||
|
||||
## VMH Webhook Endpoints
|
||||
|
||||
Diese Endpoints werden von EspoCRM aufgerufen.
|
||||
|
||||
### Beteiligte Create Webhook
|
||||
|
||||
**Endpoint**: `POST /vmh/webhook/beteiligte/create`
|
||||
|
||||
**Request Body**: Array von Entitäten
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "entity-123",
|
||||
"name": "Max Mustermann",
|
||||
"createdAt": "2026-02-07T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "received",
|
||||
"action": "create",
|
||||
"new_ids_count": 1,
|
||||
"total_ids_in_batch": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Beteiligte Update Webhook
|
||||
|
||||
**Endpoint**: `POST /vmh/webhook/beteiligte/update`
|
||||
|
||||
**Request Body**: Array von Entitäten
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "entity-123",
|
||||
"name": "Max Mustermann Updated",
|
||||
"modifiedAt": "2026-02-07T11:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "received",
|
||||
"action": "update",
|
||||
"new_ids_count": 1,
|
||||
"total_ids_in_batch": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Beteiligte Delete Webhook
|
||||
|
||||
**Endpoint**: `POST /vmh/webhook/beteiligte/delete`
|
||||
|
||||
**Request Body**: Array von Entitäten
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "entity-123",
|
||||
"deletedAt": "2026-02-07T12:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"status": "received",
|
||||
"action": "delete",
|
||||
"new_ids_count": 1,
|
||||
"total_ids_in_batch": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Webhook Features
|
||||
|
||||
**Batch Support**: Alle Webhooks unterstützen Arrays von Entitäten
|
||||
|
||||
**Deduplication**: Redis-basiert, verhindert Mehrfachverarbeitung
|
||||
|
||||
**Async Processing**: Events werden emittiert und asynchron verarbeitet
|
||||
|
||||
## Event Topics
|
||||
|
||||
Interne Event-Topics für Event-Driven Architecture (nicht direkt aufrufbar).
|
||||
|
||||
### calendar_sync_all
|
||||
|
||||
**Emitted by**: `calendar_sync_cron_step`, `calendar_sync_api_step`
|
||||
**Subscribed by**: `calendar_sync_all_step`
|
||||
|
||||
**Payload**:
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
### calendar_sync_employee
|
||||
|
||||
**Emitted by**: `calendar_sync_all_step`, `calendar_sync_api_step`
|
||||
**Subscribed by**: `calendar_sync_event_step`
|
||||
|
||||
**Payload**:
|
||||
```json
|
||||
{
|
||||
"kuerzel": "SB",
|
||||
"full_content": true
|
||||
}
|
||||
```
|
||||
|
||||
### vmh.beteiligte.create
|
||||
|
||||
**Emitted by**: `beteiligte_create_api_step`
|
||||
**Subscribed by**: `beteiligte_sync_event_step`
|
||||
|
||||
**Payload**:
|
||||
```json
|
||||
{
|
||||
"entity_id": "123",
|
||||
"action": "create",
|
||||
"source": "webhook",
|
||||
"timestamp": "2026-02-07T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### vmh.beteiligte.update
|
||||
|
||||
**Emitted by**: `beteiligte_update_api_step`
|
||||
**Subscribed by**: `beteiligte_sync_event_step`
|
||||
|
||||
**Payload**:
|
||||
```json
|
||||
{
|
||||
"entity_id": "123",
|
||||
"action": "update",
|
||||
"source": "webhook",
|
||||
"timestamp": "2026-02-07T11:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### vmh.beteiligte.delete
|
||||
|
||||
**Emitted by**: `beteiligte_delete_api_step`
|
||||
**Subscribed by**: `beteiligte_sync_event_step`
|
||||
|
||||
**Payload**:
|
||||
```json
|
||||
{
|
||||
"entity_id": "123",
|
||||
"action": "delete",
|
||||
"source": "webhook",
|
||||
"timestamp": "2026-02-07T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limits
|
||||
|
||||
### Google Calendar API
|
||||
|
||||
**Limit**: 600 requests/minute (enforced via Redis token bucket)
|
||||
|
||||
**Behavior**:
|
||||
- Requests wait if rate limit reached
|
||||
- Automatic backoff on 403 errors
|
||||
- Max retry: 4 attempts
|
||||
|
||||
### Advoware API
|
||||
|
||||
**Limit**: Unknown (keine offizielle Dokumentation)
|
||||
|
||||
**Behavior**:
|
||||
- 30s timeout per request
|
||||
- Automatic token refresh on 401
|
||||
- No retry logic (fail fast)
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"body": {
|
||||
"error": "Error description",
|
||||
"details": "Detailed error message"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Status Codes
|
||||
|
||||
- `200` - Success
|
||||
- `400` - Bad Request (invalid input)
|
||||
- `401` - Unauthorized (Advoware token invalid)
|
||||
- `403` - Forbidden (rate limit)
|
||||
- `404` - Not Found
|
||||
- `500` - Internal Server Error
|
||||
- `503` - Service Unavailable (Redis down)
|
||||
|
||||
### Common Errors
|
||||
|
||||
**Redis Connection Error**:
|
||||
```json
|
||||
{
|
||||
"status": 503,
|
||||
"body": {
|
||||
"error": "Redis connection failed"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Advoware API Error**:
|
||||
```json
|
||||
{
|
||||
"status": 500,
|
||||
"body": {
|
||||
"error": "Advoware API call failed",
|
||||
"details": "401 Unauthorized"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Lock Active Error**:
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"body": {
|
||||
"error": "Sync already in progress for employee SB"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Versioning
|
||||
|
||||
**Current Version**: v1 (implicit, no version in URL)
|
||||
|
||||
**Future**: API versioning via URL prefix (`/v2/api/...`)
|
||||
|
||||
## Health Check
|
||||
|
||||
**Coming Soon**: `/health` endpoint für Load Balancer
|
||||
|
||||
Expected response:
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"services": {
|
||||
"redis": "up",
|
||||
"advoware": "up",
|
||||
"google": "up"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Postman Collection
|
||||
|
||||
Import diese Collection für schnelles Testing:
|
||||
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"name": "bitbylaw API",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
{
|
||||
"name": "Advoware Proxy GET",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://localhost:3000/advoware/proxy?endpoint=employees"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Calendar Sync Trigger",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://localhost:3000/advoware/calendar/sync",
|
||||
"header": [{"key": "Content-Type", "value": "application/json"}],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\"full_content\": true}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Architecture](ARCHITECTURE.md)
|
||||
- [Development Guide](DEVELOPMENT.md)
|
||||
- [Configuration](CONFIGURATION.md)
|
||||
642
bitbylaw/docs/ARCHITECTURE.md
Normal file
642
bitbylaw/docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,642 @@
|
||||
# Architektur
|
||||
|
||||
## Systemübersicht
|
||||
|
||||
Das bitbylaw-System ist eine event-driven Integration zwischen Advoware, EspoCRM, Google Calendar, Vermieterhelden und 3CX Telefonie, basierend auf dem Motia Framework. Die Architektur folgt einem modularen, mikroservice-orientierten Ansatz mit klarer Separation of Concerns.
|
||||
|
||||
### Kernkomponenten
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
│ KONG API Gateway │
|
||||
│ api.bitbylaw.com │
|
||||
│ (Auth, Rate Limiting) │
|
||||
└──────────────┬──────────────┘
|
||||
│
|
||||
┌──────────────────────────┼──────────────────────────┐
|
||||
│ │ │
|
||||
┌────▼────────┐ ┌──────▼─────────┐ ┌─────▼──────┐
|
||||
│ Vermieter- │ │ Motia │ │ 3CX │
|
||||
│ helden.de │────────▶│ Framework │◀────────│ Telefonie │
|
||||
│ (WordPress) │ │ (Middleware) │ │ (ralup) │
|
||||
└─────────────┘ └────────┬───────┘ └────────────┘
|
||||
Leads Input │ Call Handling
|
||||
│
|
||||
┌───────────────────────────┼───────────────────────────┐
|
||||
│ │ │
|
||||
┌────▼────┐ ┌──────▼──────┐ ┌──────▼─────┐
|
||||
│Advoware │ │ VMH │ │ Calendar │
|
||||
│ Proxy │ │ Webhooks │ │ Sync │
|
||||
└────┬────┘ └─────┬───────┘ └─────┬──────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
┌────▼─────────────────────────▼──────────────────────────▼────┐
|
||||
│ Redis (3 DBs) │
|
||||
│ DB 1: Caching & Locks │
|
||||
│ DB 2: Calendar Sync State │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌────▼────────────────────────────┐
|
||||
│ External Services │
|
||||
├─────────────────────────────────┤
|
||||
│ • Advoware REST API │
|
||||
│ • EspoCRM (VMH) │
|
||||
│ • Google Calendar API │
|
||||
│ • 3CX API (ralup.my3cx.de) │
|
||||
│ • Vermieterhelden WordPress │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Komponenten-Details
|
||||
|
||||
### 0. KONG API Gateway
|
||||
|
||||
**Zweck**: Zentraler API-Gateway für alle öffentlichen APIs mit Authentifizierung und Rate Limiting.
|
||||
|
||||
**Domain**: `api.bitbylaw.com`
|
||||
|
||||
**Funktionen**:
|
||||
- **Authentication**: API-Key-basiert, JWT, OAuth2
|
||||
- **Rate Limiting**: Pro Consumer/API-Key
|
||||
- **Request Routing**: Zu Backend-Services (Motia, etc.)
|
||||
- **SSL/TLS Termination**: HTTPS-Handling
|
||||
- **Logging & Monitoring**: Request-Logs, Metrics
|
||||
- **CORS Handling**: Cross-Origin Requests
|
||||
|
||||
**Upstream Services**:
|
||||
- Motia Framework (Advoware Proxy, Calendar Sync, VMH Webhooks)
|
||||
- Zukünftig: Weitere Microservices
|
||||
|
||||
**Konfiguration**:
|
||||
```yaml
|
||||
# KONG Service Configuration
|
||||
services:
|
||||
- name: motia-backend
|
||||
url: http://localhost:3000
|
||||
routes:
|
||||
- name: advoware-proxy
|
||||
paths: [/advoware/*]
|
||||
- name: calendar-sync
|
||||
paths: [/calendar/*]
|
||||
- name: vmh-webhooks
|
||||
paths: [/vmh/*]
|
||||
plugins:
|
||||
- name: key-auth
|
||||
- name: rate-limiting
|
||||
config:
|
||||
minute: 600
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
```
|
||||
Client → KONG (api.bitbylaw.com) → Auth Check → Rate Limit → Motia Backend
|
||||
```
|
||||
|
||||
### 1. Advoware Proxy Layer
|
||||
|
||||
**Zweck**: Transparente REST-API-Proxy für Advoware mit Authentifizierung und Caching.
|
||||
|
||||
**Module**: `steps/advoware_proxy/`
|
||||
- `advoware_api_proxy_get_step.py` - GET-Requests
|
||||
- `advoware_api_proxy_post_step.py` - POST-Requests (Create)
|
||||
- `advoware_api_proxy_put_step.py` - PUT-Requests (Update)
|
||||
- `advoware_api_proxy_delete_step.py` - DELETE-Requests
|
||||
|
||||
**Services**: `services/advoware.py`
|
||||
- Token-Management (HMAC-512 Authentifizierung)
|
||||
- Redis-basiertes Token-Caching (55min Lifetime)
|
||||
- Automatischer Token-Refresh bei 401-Errors
|
||||
- Async API-Client mit aiohttp
|
||||
|
||||
**Datenfluss**:
|
||||
```
|
||||
Client → API-Step → AdvowareAPI Service → Redis (Token Cache) → Advoware API
|
||||
```
|
||||
|
||||
### 2. Calendar Sync System
|
||||
|
||||
**Zweck**: Bidirektionale Synchronisation zwischen Advoware-Terminen und Google Calendar.
|
||||
|
||||
**Architecture Pattern**: Event-Driven Cascade
|
||||
|
||||
**Integration**: EspoCRM sendet Webhooks an KONG → Motia
|
||||
|
||||
**Datenfluss**:
|
||||
```
|
||||
EspoCRM (Vermieterhelden CRM) → KONG → Motia VMH Webhooks → Redis Dedup → Events
|
||||
```
|
||||
```
|
||||
Cron (täglich)
|
||||
→ calendar_sync_cron_step
|
||||
→ Emit: "calendar_sync_all"
|
||||
→ calendar_sync_all_step
|
||||
→ Fetch Employees
|
||||
→ For each Employee:
|
||||
→ Set Redis Lock
|
||||
→ Emit: "calendar_sync_employee"
|
||||
→ calendar_sync_event_step
|
||||
→ Fetch Advoware Events
|
||||
→ Fetch Google Events
|
||||
→ Sync (Create/Update/Delete)
|
||||
→ Clear Redis Lock
|
||||
```
|
||||
|
||||
**Module**: `steps/advoware_cal_sync/`
|
||||
- `calendar_sync_cron_step.py` - Täglicher Trigger
|
||||
- `calendar_sync_all_step.py` - Employee-List-Handler
|
||||
- `calendar_sync_event_step.py` - Per-Employee Sync-Logic
|
||||
- `calendar_sync_api_step.py` - Manueller Trigger-Endpoint
|
||||
- `calendar_sync_utils.py` - Shared Utilities
|
||||
- `audit_calendar_sync.py` - Audit & Diagnostics
|
||||
|
||||
**Key Features**:
|
||||
- **Redis Locking**: Verhindert parallele Syncs für denselben Employee
|
||||
- **Rate Limiting**: Token-Bucket-Algorithm (7 tokens, Redis-based)
|
||||
- **Normalisierung**: Common format (Berlin TZ) für beide APIs
|
||||
- **Error Isolation**: Employee-Fehler stoppen nicht Gesamt-Sync
|
||||
|
||||
**Datenmapping**:
|
||||
```
|
||||
Advoware Format → Standard Format → Google Calendar Format
|
||||
↓ ↓ ↓
|
||||
datum/uhrzeitVon start (datetime) dateTime
|
||||
datumBis end (datetime) dateTime
|
||||
dauertermin all_day (bool) date
|
||||
turnus/turnusArt recurrence RRULE
|
||||
```
|
||||
|
||||
### 3. VMH Webhook System
|
||||
|
||||
**Zweck**: Empfang und Verarbeitung von EspoCRM Webhooks für Beteiligte-Entitäten.
|
||||
|
||||
**Architecture Pattern**: Webhook → Deduplication → Event Emission
|
||||
|
||||
**Module**: `steps/vmh/`
|
||||
- `webhook/beteiligte_create_api_step.py` - Create Webhook
|
||||
- `webhook/beteiligte_update_api_step.py` - Update Webhook
|
||||
- `webhook/beteiligte_delete_api_step.py` - Delete Webhook
|
||||
- `beteiligte_sync_event_step.py` - Sync Event Handler (Placeholder)
|
||||
|
||||
**Webhook-Flow**:
|
||||
```
|
||||
EspoCRM → POST /vmh/webhook/beteiligte/create
|
||||
↓
|
||||
Webhook Step
|
||||
↓
|
||||
Extract Entity IDs
|
||||
↓
|
||||
Redis Deduplication (SET: vmh:beteiligte:create_pending)
|
||||
↓
|
||||
Emit Event: "vmh.beteiligte.create"
|
||||
↓
|
||||
Sync Event Step (subscribes)
|
||||
↓
|
||||
[TODO: Implementierung]
|
||||
|
||||
### 4. Vermieterhelden Integration
|
||||
|
||||
**Zweck**: Lead-Eingang von Vermieterhelden.de WordPress-Frontend.
|
||||
|
||||
**URL**: `https://vermieterhelden.de`
|
||||
|
||||
**Technologie**: WordPress-basiertes Frontend
|
||||
|
||||
**Funktionen**:
|
||||
- **Lead-Formulare**: Mieter, Vermieter, Anfragen
|
||||
- **Lead-Routing**: Zu EspoCRM (VMH) → Motia
|
||||
- **Webhook-basiert**: POST zu KONG/Motia bei neuem Lead
|
||||
|
||||
**Datenfluss**:
|
||||
```
|
||||
Vermieterhelden.de → Lead erstellt → Webhook → KONG → Motia → EspoCRM/Advoware
|
||||
```
|
||||
|
||||
**Lead-Typen**:
|
||||
- Mieter-Anfragen
|
||||
- Vermieter-Anfragen
|
||||
- Kontaktformulare
|
||||
- Newsletter-Anmeldungen
|
||||
|
||||
**Integration mit Motia**:
|
||||
- Eigener Webhook-Endpoint: `/api/leads/vermieterhelden`
|
||||
- Lead-Validierung und -Enrichment
|
||||
- Weiterleitung an CRM-Systeme
|
||||
|
||||
### 5. 3CX Telefonie-Integration
|
||||
|
||||
**Zweck**: Telefonie-System-Integration für Call-Handling und Lead-Qualifizierung.
|
||||
|
||||
**URL**: `https://ralup.my3cx.de`
|
||||
|
||||
**Technologie**: 3CX Cloud PBX
|
||||
|
||||
**Funktionen**:
|
||||
- **Outbound Calls**: Lead-Anrufe (automatisch oder manuell)
|
||||
- **Inbound Calls**: Stammdatenabfrage (CTI - Computer Telephony Integration)
|
||||
- **Call Logging**: Anrufprotokolle zu CRM
|
||||
- **Call Recording**: Aufzeichnungen speichern und abrufen
|
||||
- **Screen Pops**: Kundeninfo bei eingehendem Anruf
|
||||
|
||||
**API-Integrationen**:
|
||||
|
||||
**A) Outbound: Motia → 3CX**
|
||||
```
|
||||
Motia → KONG → 3CX API
|
||||
- Initiate Call to Lead
|
||||
- Get Call Status
|
||||
```
|
||||
|
||||
**B) Inbound: 3CX → Motia**
|
||||
```
|
||||
3CX Webhook → KONG → Motia
|
||||
- Call Started → Fetch Customer Data
|
||||
- Call Ended → Log Call Record
|
||||
```
|
||||
|
||||
**Datenfluss**:
|
||||
|
||||
**Call Initiation**:
|
||||
```
|
||||
Lead in CRM → Trigger Call → Motia → 3CX API → Dial Number
|
||||
```
|
||||
|
||||
**Inbound Call**:
|
||||
```
|
||||
3CX detects call → Webhook to Motia → Lookup in Advoware/EspoCRM → Return data → 3CX Screen Pop
|
||||
```
|
||||
|
||||
**Call Recording**:
|
||||
```
|
||||
Call ends → 3CX Webhook → Motia → Store metadata → Link to CRM entity
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
- Lead-Qualifizierung nach Eingang
|
||||
- Stammdatenabfrage bei Anruf
|
||||
- Anrufprotokoll in EspoCRM/Advoware
|
||||
- Automatische Follow-up-Tasks
|
||||
```
|
||||
|
||||
**Deduplikation-Mechanismus**:
|
||||
- Redis SET für pending IDs pro Action-Type (create/update/delete)
|
||||
- Neue IDs werden zu SET hinzugefügt
|
||||
- Events nur für neue (nicht-duplizierte) IDs emittiert
|
||||
- SET-TTL verhindert Memory-Leaks
|
||||
|
||||
## Event-Driven Design
|
||||
|
||||
### Event-Topics
|
||||
|
||||
| Topic | Emitter | Subscriber | Payload |
|
||||
|-------|---------|------------|---------|
|
||||
| `calendar_sync_all` | cron_step | all_step | `{}` |
|
||||
| `calendar_sync_employee` | all_step, api_step | event_step | `{kuerzel, full_content}` |
|
||||
| `vmh.beteiligte.create` | create webhook | sync_event_step | `{entity_id, action, source, timestamp}` |
|
||||
| `vmh.beteiligte.update` | update webhook | sync_event_step | `{entity_id, action, source, timestamp}` |
|
||||
| `vmh.beteiligte.delete` | delete webhook | sync_event_step | `{entity_id, action, source, timestamp}` |
|
||||
|
||||
### Event-Flow Patterns
|
||||
|
||||
**1. Cascade Pattern** (Calendar Sync):
|
||||
```
|
||||
Trigger → Fetch List → Emit per Item → Process Item
|
||||
```
|
||||
|
||||
**2. Webhook Pattern** (VMH):
|
||||
```
|
||||
External Event → Dedup → Internal Event → Processing
|
||||
```
|
||||
|
||||
## Redis Architecture
|
||||
|
||||
### Database Layout
|
||||
|
||||
**DB 0**: Default (Motia internal)
|
||||
**DB 1**: Advoware Cache & Locks
|
||||
- `advoware_access_token` - Bearer Token (TTL: 53min)
|
||||
- `advoware_token_timestamp` - Token Creation Time
|
||||
- `calendar_sync:lock:{kuerzel}` - Per-Employee Lock (TTL: 5min)
|
||||
- `vmh:beteiligte:create_pending` - Create Dedup SET
|
||||
- `vmh:beteiligte:update_pending` - Update Dedup SET
|
||||
- `vmh:beteiligte:delete_pending` - Delete Dedup SET
|
||||
|
||||
**DB 2**: Calendar Sync Rate Limiting
|
||||
- `google_calendar_api_tokens` - Token Bucket for Rate Limiting
|
||||
|
||||
---
|
||||
|
||||
## External APIs
|
||||
|
||||
### Advoware REST API
|
||||
|
||||
**Base URL**: `https://advoware-api.example.com/api/v1/`
|
||||
**Auth**: HMAC-512 (siehe `services/advoware.py`)
|
||||
**Rate Limits**: Unknown (keine Limits bekannt)
|
||||
**Documentation**: [Advoware API Swagger](../docs/advoware/advoware_api_swagger.json)
|
||||
|
||||
**Wichtige Endpoints**:
|
||||
- `POST /auth/login` - Token generieren
|
||||
- `GET /employees` - Employee-Liste
|
||||
- `GET /events` - Termine abrufen
|
||||
- `POST /events` - Termin erstellen
|
||||
- `PUT /events/{id}` - Termin aktualisieren
|
||||
|
||||
### Redis Usage Patterns
|
||||
|
||||
**Token Caching**:
|
||||
```python
|
||||
# Set with expiration
|
||||
redis.set('advoware_access_token', token, ex=3180) # 53min
|
||||
|
||||
# Get with fallback
|
||||
token = redis.get('advoware_access_token')
|
||||
if not token:
|
||||
token = fetch_new_token()
|
||||
```
|
||||
|
||||
### EspoCRM (VMH)
|
||||
|
||||
**Integration**: Webhook Sender (Outbound), API Consumer
|
||||
**Endpoints**: Configured in EspoCRM, routed via KONG
|
||||
**Format**: JSON POST with entity data
|
||||
**Note**: Dient als CRM für Vermieterhelden-Leads
|
||||
|
||||
### 3CX Telefonie API
|
||||
|
||||
**Base URL**: `https://ralup.my3cx.de/api/v1/`
|
||||
**Auth**: API Key oder Basic Auth
|
||||
**Rate Limits**: Unknown (typisch 60 req/min)
|
||||
|
||||
**Key Endpoints**:
|
||||
- `POST /calls/initiate` - Anruf starten
|
||||
- `GET /calls/{id}/status` - Call-Status
|
||||
- `GET /calls/{id}/recording` - Aufzeichnung abrufen
|
||||
- `POST /webhook` - Webhook-Konfiguration (eingehend)
|
||||
|
||||
**Webhooks** (Inbound von 3CX):
|
||||
- `call.started` - Anruf beginnt
|
||||
- `call.ended` - Anruf beendet
|
||||
- `call.transferred` - Anruf weitergeleitet
|
||||
|
||||
### Vermieterhelden
|
||||
|
||||
**Integration**: Webhook Sender (Lead-Eingang)
|
||||
**Base**: WordPress mit Custom Plugins
|
||||
**Format**: JSON POST zu Motia
|
||||
|
||||
**Webhook-Events**:
|
||||
- `lead.created` - Neuer Lead
|
||||
- `contact.submitted` - Kontaktformular
|
||||
lock_key = f'calendar_sync:lock:{kuerzel}'
|
||||
if not redis.set(lock_key, '1', nx=True, ex=300):
|
||||
raise LockError("Already locked")
|
||||
|
||||
# Always release
|
||||
redis.delete(lock_key)
|
||||
```
|
||||
|
||||
**Deduplication**:
|
||||
```python
|
||||
# Check & Add atomically
|
||||
existing = redis.smembers('vmh:beteiligte:create_pending')
|
||||
new_ids = input_ids - existing
|
||||
if new_ids:
|
||||
redis.sadd('vmh:beteiligte:create_pending', *new_ids)
|
||||
```
|
||||
|
||||
## Service Layer
|
||||
|
||||
### AdvowareAPI Service
|
||||
|
||||
**Location**: `services/advoware.py`
|
||||
|
||||
**Responsibilities**:
|
||||
- HMAC-512 Authentication
|
||||
- Token Management
|
||||
- HTTP Client (aiohttp)
|
||||
- Error Handling & Retries
|
||||
|
||||
**Key Methods**:
|
||||
```python
|
||||
get_access_token(force_refresh=False) -> str
|
||||
api_call(endpoint, method, params, json_data) -> Any
|
||||
```
|
||||
|
||||
**Authentication Flow**:
|
||||
```
|
||||
1. Generate HMAC-512 signature
|
||||
- Message: "{product_id}:{app_id}:{nonce}:{timestamp}"
|
||||
- Key: Base64-decoded API Key
|
||||
- Hash: SHA512
|
||||
|
||||
2. POST to security.advo-net.net/api/v1/Token
|
||||
- Body: {AppID, User, Password, HMAC512Signature, ...}
|
||||
|
||||
3. Extract access_token from response
|
||||
|
||||
4. Cache in Redis (53min TTL)
|
||||
|
||||
5. Use as Bearer Token: "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
## External API Integration
|
||||
|
||||
### Advoware API
|
||||
|
||||
**Base URL**: `https://www2.advo-net.net:90/`
|
||||
**Auth**: HMAC-512 + Bearer Token
|
||||
**Rate Limits**: Unknown (robust error handling)
|
||||
|
||||
**Key Endpoints**:
|
||||
- `/employees` - Mitarbeiter-Liste
|
||||
- `/appointments` - Termine
|
||||
|
||||
### Google Calendar API
|
||||
|
||||
**Auth**: Service Account (JSON Key)
|
||||
**Rate Limits**: 600 requests/minute (enforced via Redis)
|
||||
**Scopes**: `https://www.googleapis.com/auth/calendar`
|
||||
|
||||
**Key Operations**:
|
||||
- `calendars().get()` - Calendar abrufen
|
||||
- `calendars().insert()` - Calendar erstellen
|
||||
- `events().list()` - Events abrufen
|
||||
- `events().insert()` - Event erstellen
|
||||
- KONG Gateway**: API-Key oder JWT-based Auth für externe Clients
|
||||
**Advoware**: User-based Auth (ADVOWARE_USER + PASSWORD)
|
||||
**Google**: Service Account (domain-wide delegation)
|
||||
**3CX**: API Key oder Basic Auth
|
||||
**Redis**: Localhost only (no password)
|
||||
**Vermieterhelden**: Webhook-Secret für Validation
|
||||
### EspoCRM
|
||||
|
||||
**Integration**: Webhook Sender (Outbound)
|
||||
**Endpoints**: Configured in EspoCRM
|
||||
**Format**: JSON POST with entity data
|
||||
|
||||
## Security
|
||||
|
||||
### Secrets Management
|
||||
|
||||
**Environment Variables**:
|
||||
```bash
|
||||
ADVOWARE_API_KEY # Base64-encoded HMAC Key
|
||||
ADVOWARE_PASSWORD # User Password
|
||||
GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH # Path to JSON Key
|
||||
ESPOCRM_MARVIN_API_KEY # Webhook Validation (optional)
|
||||
```
|
||||
|
||||
**Storage**:
|
||||
- Environment variables in systemd service
|
||||
- Service Account JSON: `/opt/motia-app/service-account.json` (chmod 600)
|
||||
- No secrets in code or Git
|
||||
|
||||
### Access Control
|
||||
|
||||
**Advoware**: User-based Auth (ADVOWARE_USER + PASSWORD)
|
||||
**Google**: Service Account (domain-wide delegation)
|
||||
**Redis**: Localhost only (no password)
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Throughput
|
||||
|
||||
**Calendar Sync**:
|
||||
- ~10 employees: 2-3 minutes
|
||||
- Rate-limited by Google API (600 req/min)
|
||||
- Per-employee parallelization: Nein (sequential via events)
|
||||
|
||||
**Webhooks**:
|
||||
- Instant processing (<100ms)
|
||||
- Batch support (multiple entities per request)
|
||||
- Redis dedup overhead: <10ms
|
||||
|
||||
### Memory Usage
|
||||
|
||||
**Current**: 169MB (Peak: 276MB)
|
||||
**Breakdown**:
|
||||
- Node.js process: ~150MB
|
||||
- Python dependencies: Lazy-loaded per step
|
||||
- Redis memory: <10MB
|
||||
|
||||
### Scalability
|
||||
|
||||
**Horizontal**: Nicht ohne weiteres möglich (Redis Locks, Shared State)
|
||||
**Vertical**: CPU-bound bei vielen parallel Employees
|
||||
**Bottleneck**: Google Calendar API Rate Limits
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
### Logging
|
||||
|
||||
**Framework**: Motia Workbench (structured logging)
|
||||
**Levels**: DEBUG, INFO, ERROR
|
||||
**Output**: journalctl (systemd) + Motia Workbench UI
|
||||
|
||||
**Key Log Points**:
|
||||
- API-Requests (Method, URL, Status)
|
||||
- Event Emission (Topic, Payload)
|
||||
- Redis Operations (Keys, Success/Failure)
|
||||
- Errors (Stack traces, Context)
|
||||
|
||||
### Metrics
|
||||
|
||||
**Available** (via Logs):
|
||||
- Webhook receive count
|
||||
- Calendar sync duration per employee
|
||||
- API call count & latency
|
||||
- Redis hit/miss ratio (implicit)
|
||||
|
||||
**Missing** (Future):
|
||||
- Prometheus metrics
|
||||
- Grafana dashboards
|
||||
- Alerting
|
||||
|
||||
## Deployment
|
||||
|
||||
### systemd Service
|
||||
|
||||
**Unit**: `motia.service`
|
||||
**User**: `www-data`
|
||||
**WorkingDirectory**: `/opt/motia-app/bitbylaw`
|
||||
**Restart**: `always` (10s delay)
|
||||
|
||||
**Environment**:
|
||||
```bash
|
||||
NODE_ENV=production
|
||||
NODE_OPTIONS=--max-old-space-size=8192 --inspect
|
||||
HOST=0.0.0.0
|
||||
MOTIA_LOG_LEVEL=debug
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Runtime**:
|
||||
- Node.js 18+
|
||||
- Python 3.13+
|
||||
- Redis Server
|
||||
- systemd
|
||||
|
||||
**Build**:
|
||||
- npm (Node packages)
|
||||
- pip (Python packages)
|
||||
- Motia CLI
|
||||
|
||||
## Disaster Recovery
|
||||
|
||||
### Backup Strategy
|
||||
|
||||
**Redis**:
|
||||
- RDB snapshots (automatisch)
|
||||
- AOF persistence (optional)
|
||||
|
||||
**Configuration**:
|
||||
- Git-versioniert
|
||||
- Environment Variables in systemd
|
||||
|
||||
**Service Account**:
|
||||
- Manual backup: `/opt/motia-app/service-account.json`
|
||||
|
||||
### Recovery Procedures
|
||||
|
||||
**Service Restart**:
|
||||
```bash
|
||||
systemctl restart motia.service
|
||||
```
|
||||
|
||||
**Clear Redis Cache**:
|
||||
```bash
|
||||
redis-cli -n 1 FLUSHDB # Advoware Cache
|
||||
redis-cli -n 2 FLUSHDB # Calendar Sync
|
||||
```
|
||||
|
||||
**Clear Employee Lock**:
|
||||
```bash
|
||||
python /opt/motia-app/bitbylaw/delete_employee_locks.py
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### P3CX Full Integration**: Complete call handling, CTI features
|
||||
3. **Vermieterhelden Lead Processing**: Automated lead routing and enrichment
|
||||
4. **Horizontal Scaling**: Distributed locking (Redis Cluster)
|
||||
5. **Metrics & Monitoring**: Prometheus exporters
|
||||
6. **Health Checks**: `/health` endpoint via KONG
|
||||
|
||||
### Considered
|
||||
|
||||
1. **PostgreSQL Hub**: Persistent sync state (currently Redis-only)
|
||||
2. **Webhook Signatures**: Validation von Vermieterhelden/3CX requests
|
||||
3. **Multi-Tenant**: Support für mehrere Kanzleien
|
||||
4. **KONG Plugins**: Custom plugins für business logic
|
||||
1. **PostgreSQL Hub**: Persistent sync state (currently Redis-only)
|
||||
2. **Webhook Signatures**: Validation von EspoCRM requests
|
||||
3. **Multi-Tenant**: Support für mehrere Kanzleien
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Development Guide](DEVELOPMENT.md)
|
||||
- [API Reference](API.md)
|
||||
- [Configuration](CONFIGURATION.md)
|
||||
- [Troubleshooting](TROUBLESHOOTING.md)
|
||||
- [Deployment Guide](DEPLOYMENT.md)
|
||||
509
bitbylaw/docs/CONFIGURATION.md
Normal file
509
bitbylaw/docs/CONFIGURATION.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# Configuration Guide
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Alle Konfiguration erfolgt über Environment Variables. Diese können gesetzt werden:
|
||||
1. In `.env` Datei (lokale Entwicklung)
|
||||
2. In systemd service file (production)
|
||||
3. Export in shell
|
||||
|
||||
## Advoware API Configuration
|
||||
|
||||
### Required Variables
|
||||
|
||||
```bash
|
||||
# Advoware API Base URL
|
||||
ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
|
||||
|
||||
# Product ID (typischerweise 64)
|
||||
ADVOWARE_PRODUCT_ID=64
|
||||
|
||||
# Application ID (von Advoware bereitgestellt)
|
||||
ADVOWARE_APP_ID=your_app_id_here
|
||||
|
||||
# API Key (Base64-encoded für HMAC-512 Signatur)
|
||||
ADVOWARE_API_KEY=your_base64_encoded_key_here
|
||||
|
||||
# Kanzlei-Kennung
|
||||
ADVOWARE_KANZLEI=your_kanzlei_name
|
||||
|
||||
# Database Name
|
||||
ADVOWARE_DATABASE=your_database_name
|
||||
|
||||
# User für API-Zugriff
|
||||
ADVOWARE_USER=api_user
|
||||
|
||||
# User Role (typischerweise 2)
|
||||
ADVOWARE_ROLE=2
|
||||
|
||||
# User Password
|
||||
ADVOWARE_PASSWORD=secure_password_here
|
||||
|
||||
# Token Lifetime in Minuten (Standard: 55)
|
||||
ADVOWARE_TOKEN_LIFETIME_MINUTES=55
|
||||
|
||||
# API Timeout in Sekunden (Standard: 30)
|
||||
ADVOWARE_API_TIMEOUT_SECONDS=30
|
||||
|
||||
# Write Protection (true = keine Schreibzugriffe auf Advoware)
|
||||
ADVOWARE_WRITE_PROTECTION=true
|
||||
```
|
||||
|
||||
### Advoware API Key
|
||||
|
||||
Der API Key muss Base64-encoded sein für HMAC-512 Signatur:
|
||||
|
||||
```bash
|
||||
# Wenn Sie einen Raw Key haben, encodieren Sie ihn:
|
||||
echo -n "your_raw_key" | base64
|
||||
```
|
||||
|
||||
## Redis Configuration
|
||||
|
||||
```bash
|
||||
# Redis Host (Standard: localhost)
|
||||
REDIS_HOST=localhost
|
||||
|
||||
# Redis Port (Standard: 6379)
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Redis Database für Advoware Cache (Standard: 1)
|
||||
REDIS_DB_ADVOWARE_CACHE=1
|
||||
|
||||
# Redis Database für Calendar Sync (Standard: 2)
|
||||
REDIS_DB_CALENDAR_SYNC=2
|
||||
|
||||
# Redis Timeout in Sekunden (Standard: 5)
|
||||
REDIS_TIMEOUT_SECONDS=5
|
||||
```
|
||||
|
||||
### Redis Database Layout
|
||||
|
||||
- **DB 0**: Motia Framework (nicht konfigurierbar)
|
||||
- **DB 1**: Advoware Cache & Locks (`REDIS_DB_ADVOWARE_CACHE`)
|
||||
- Token Cache
|
||||
- Employee Locks
|
||||
- Webhook Deduplication
|
||||
- **DB 2**: Calendar Sync Rate Limiting (`REDIS_DB_CALENDAR_SYNC`)
|
||||
|
||||
---
|
||||
|
||||
## KONG API Gateway Configuration
|
||||
|
||||
```bash
|
||||
# KONG Admin API URL (für Konfiguration)
|
||||
KONG_ADMIN_URL=http://localhost:8001
|
||||
|
||||
# KONG Proxy URL (öffentlich erreichbar)
|
||||
KONG_PROXY_URL=https://api.bitbylaw.com
|
||||
```
|
||||
|
||||
**Hinweis**: KONG-Konfiguration erfolgt typischerweise über Admin API oder Declarative Config (kong.yml).
|
||||
|
||||
---
|
||||
|
||||
## 3CX Telefonie Configuration
|
||||
|
||||
```bash
|
||||
# 3CX API Base URL
|
||||
THREECX_API_URL=https://ralup.my3cx.de/api/v1
|
||||
|
||||
# 3CX API Key für Authentifizierung
|
||||
THREECX_API_KEY=your_3cx_api_key_here
|
||||
|
||||
# 3CX Webhook Secret (optional, für Signatur-Validierung)
|
||||
THREECX_WEBHOOK_SECRET=your_webhook_secret_here
|
||||
```
|
||||
|
||||
### 3CX Setup
|
||||
|
||||
1. Erstellen Sie API Key in 3CX Management Console
|
||||
2. Konfigurieren Sie Webhook URLs in 3CX:
|
||||
- Call Started: `https://api.bitbylaw.com/telephony/3cx/webhook`
|
||||
- Call Ended: `https://api.bitbylaw.com/telephony/3cx/webhook`
|
||||
3. Aktivieren Sie Call Recording (optional)
|
||||
|
||||
---
|
||||
|
||||
## Vermieterhelden Integration Configuration
|
||||
|
||||
```bash
|
||||
# Vermieterhelden Webhook Secret (für Signatur-Validierung)
|
||||
VH_WEBHOOK_SECRET=your_vermieterhelden_webhook_secret
|
||||
|
||||
# Lead Routing Target (wohin werden Leads geschickt)
|
||||
VH_LEAD_TARGET=espocrm # Options: espocrm, advoware, both
|
||||
|
||||
# Lead Auto-Assignment (optional)
|
||||
VH_AUTO_ASSIGN_LEADS=true
|
||||
VH_DEFAULT_ASSIGNEE=user_id_123
|
||||
```
|
||||
|
||||
### Vermieterhelden Setup
|
||||
|
||||
1. Konfigurieren Sie Webhook URL im WordPress:
|
||||
- URL: `https://api.bitbylaw.com/leads/vermieterhelden`
|
||||
2. Generieren Sie Shared Secret
|
||||
3. Aktivieren Sie Webhook-Events für Lead-Erstellung
|
||||
|
||||
---
|
||||
|
||||
## Google Calendar Configuration
|
||||
|
||||
```bash
|
||||
# Pfad zur Service Account JSON Datei
|
||||
GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=/opt/motia-app/service-account.json
|
||||
|
||||
# Google Calendar Scopes (Standard: calendar)
|
||||
# GOOGLE_CALENDAR_SCOPES wird im Code gesetzt, keine ENV Variable nötig
|
||||
```
|
||||
|
||||
### Service Account Setup
|
||||
|
||||
1. Erstellen Sie einen Service Account in Google Cloud Console
|
||||
2. Laden Sie die JSON-Schlüsseldatei herunter
|
||||
3. Speichern Sie sie als `service-account.json`
|
||||
4. Setzen Sie sichere Berechtigungen:
|
||||
|
||||
```bash
|
||||
chmod 600 /opt/motia-app/service-account.json
|
||||
chown www-data:www-data /opt/motia-app/service-account.json
|
||||
```
|
||||
|
||||
Siehe auch: [GOOGLE_SETUP_README.md](../GOOGLE_SETUP_README.md)
|
||||
|
||||
## PostgreSQL Configuration
|
||||
|
||||
**Status**: Aktuell nicht verwendet (zukünftige Erweiterung)
|
||||
|
||||
```bash
|
||||
# PostgreSQL Host
|
||||
POSTGRES_HOST=localhost
|
||||
|
||||
# PostgreSQL User
|
||||
POSTGRES_USER=calendar_sync_user
|
||||
|
||||
# PostgreSQL Password
|
||||
POSTGRES_PASSWORD=secure_password
|
||||
|
||||
# PostgreSQL Database Name
|
||||
POSTGRES_DB_NAME=calendar_sync_db
|
||||
```
|
||||
|
||||
## Calendar Sync Configuration
|
||||
|
||||
```bash
|
||||
# Anonymisierung von Google Events (true/false)
|
||||
CALENDAR_SYNC_ANONYMIZE_GOOGLE_EVENTS=true
|
||||
|
||||
# Debug: Nur bestimmte Mitarbeiter synchronisieren (Komma-separiert)
|
||||
# Leer = alle Mitarbeiter
|
||||
CALENDAR_SYNC_DEBUG_KUERZEL=SB,AI,RO,OK,BI,ST,UR,PB,VB
|
||||
```
|
||||
|
||||
### Anonymisierung
|
||||
|
||||
Wenn `CALENDAR_SYNC_ANONYMIZE_GOOGLE_EVENTS=true`:
|
||||
- Titel: "Blocked"
|
||||
- Beschreibung: Leer
|
||||
- Ort: Leer
|
||||
|
||||
Wenn `false`:
|
||||
- Volle Details aus Advoware werden synchronisiert
|
||||
|
||||
### Debug-Modus
|
||||
|
||||
Für Development/Testing nur bestimmte Mitarbeiter synchronisieren:
|
||||
|
||||
```bash
|
||||
# Nur diese Kürzel
|
||||
CALENDAR_SYNC_DEBUG_KUERZEL=SB,AI
|
||||
|
||||
# Alle (Standard)
|
||||
CALENDAR_SYNC_DEBUG_KUERZEL=
|
||||
```
|
||||
|
||||
## EspoCRM Configuration
|
||||
|
||||
```bash
|
||||
# API Key für Webhook-Validierung (optional)
|
||||
ESPOCRM_MARVIN_API_KEY=your_webhook_secret_here
|
||||
```
|
||||
|
||||
**Hinweis**: Aktuell wird der API Key nicht für Validierung verwendet. Zukünftige Implementierung kann HMAC-Signatur-Validierung hinzufügen.
|
||||
|
||||
## Motia Framework Configuration
|
||||
|
||||
```bash
|
||||
# Node Environment (development|production)
|
||||
NODE_ENV=production
|
||||
|
||||
# Node Memory Limit (in MB)
|
||||
# NODE_OPTIONS wird in systemd gesetzt
|
||||
NODE_OPTIONS=--max-old-space-size=8192 --inspect --heapsnapshot-signal=SIGUSR2
|
||||
|
||||
# Host Binding (0.0.0.0 = alle Interfaces)
|
||||
HOST=0.0.0.0
|
||||
|
||||
# Port (Standard: 3000)
|
||||
# PORT=3000
|
||||
|
||||
# Log Level (debug|info|warning|error)
|
||||
MOTIA_LOG_LEVEL=debug
|
||||
|
||||
# npm Cache (für systemd user www-data)
|
||||
NPM_CONFIG_CACHE=/opt/motia-app/.npm-cache
|
||||
```
|
||||
|
||||
## Configuration Loading
|
||||
|
||||
### config.py
|
||||
|
||||
Zentrale Konfiguration wird in `config.py` geladen:
|
||||
|
||||
```python
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
# Load .env file if exists
|
||||
load_dotenv()
|
||||
|
||||
class Config:
|
||||
# Alle Variablen mit Defaults
|
||||
REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
|
||||
REDIS_PORT = int(os.getenv('REDIS_PORT', '6379'))
|
||||
# ...
|
||||
```
|
||||
|
||||
### Usage in Steps
|
||||
|
||||
```python
|
||||
from config import Config
|
||||
|
||||
# Access configuration
|
||||
redis_host = Config.REDIS_HOST
|
||||
api_key = Config.ADVOWARE_API_KEY
|
||||
```
|
||||
|
||||
### Usage in Services
|
||||
|
||||
```python
|
||||
from config import Config
|
||||
|
||||
class AdvowareAPI:
|
||||
def __init__(self):
|
||||
self.api_key = Config.ADVOWARE_API_KEY
|
||||
self.base_url = Config.ADVOWARE_API_BASE_URL
|
||||
```
|
||||
|
||||
## Environment-Specific Configuration
|
||||
|
||||
### Development (.env)
|
||||
|
||||
Erstellen Sie eine `.env` Datei im Root:
|
||||
|
||||
```bash
|
||||
# .env (nicht in Git committen!)
|
||||
ADVOWARE_API_BASE_URL=https://staging.advo-net.net:90/
|
||||
ADVOWARE_API_KEY=dev_key_here
|
||||
REDIS_HOST=localhost
|
||||
MOTIA_LOG_LEVEL=debug
|
||||
ADVOWARE_WRITE_PROTECTION=true
|
||||
```
|
||||
|
||||
**Wichtig**: `.env` zu `.gitignore` hinzufügen!
|
||||
|
||||
### Production (systemd)
|
||||
|
||||
In `/etc/systemd/system/motia.service`:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
Environment=NODE_ENV=production
|
||||
Environment=ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
|
||||
Environment=ADVOWARE_API_KEY=production_key_here
|
||||
Environment=ADVOWARE_PASSWORD=production_password_here
|
||||
Environment=REDIS_HOST=localhost
|
||||
Environment=MOTIA_LOG_LEVEL=info
|
||||
Environment=ADVOWARE_WRITE_PROTECTION=false
|
||||
```
|
||||
|
||||
Nach Änderungen:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart motia.service
|
||||
```
|
||||
|
||||
### Staging
|
||||
|
||||
Eigene Service-Datei oder separate Environment-Datei.
|
||||
|
||||
## Validation
|
||||
|
||||
### Check Configuration
|
||||
|
||||
Script zum Validieren der Konfiguration:
|
||||
|
||||
```python
|
||||
# scripts/check_config.py
|
||||
from config import Config
|
||||
import sys
|
||||
|
||||
required_vars = [
|
||||
'ADVOWARE_API_BASE_URL',
|
||||
'ADVOWARE_APP_ID',
|
||||
'ADVOWARE_API_KEY',
|
||||
'REDIS_HOST',
|
||||
]
|
||||
|
||||
missing = []
|
||||
for var in required_vars:
|
||||
if not getattr(Config, var, None):
|
||||
missing.append(var)
|
||||
|
||||
if missing:
|
||||
print(f"ERROR: Missing configuration: {', '.join(missing)}")
|
||||
sys.exit(1)
|
||||
|
||||
print("✓ Configuration valid")
|
||||
```
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python scripts/check_config.py
|
||||
```
|
||||
|
||||
## Secrets Management
|
||||
|
||||
### DO NOT
|
||||
|
||||
❌ Commit secrets to Git
|
||||
❌ Hardcode passwords in code
|
||||
❌ Share `.env` files
|
||||
❌ Log sensitive data
|
||||
|
||||
### DO
|
||||
|
||||
✅ Use environment variables
|
||||
✅ Use `.gitignore` for `.env`
|
||||
✅ Use systemd for production secrets
|
||||
✅ Rotate keys regularly
|
||||
✅ Use `chmod 600` for sensitive files
|
||||
|
||||
### Rotation
|
||||
|
||||
Wenn API Keys rotiert werden:
|
||||
|
||||
```bash
|
||||
# 1. Update environment variable
|
||||
sudo nano /etc/systemd/system/motia.service
|
||||
|
||||
# 2. Reload systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 3. Clear Redis cache
|
||||
redis-cli -n 1 DEL advoware_access_token advoware_token_timestamp
|
||||
|
||||
# 4. Restart service
|
||||
sudo systemctl restart motia.service
|
||||
|
||||
# 5. Verify
|
||||
sudo journalctl -u motia.service -f
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Complete Example
|
||||
|
||||
```bash
|
||||
# Advoware API
|
||||
ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
|
||||
ADVOWARE_PRODUCT_ID=64
|
||||
ADVOWARE_APP_ID=your_app_id
|
||||
ADVOWARE_API_KEY=your_base64_key
|
||||
ADVOWARE_KANZLEI=your_kanzlei
|
||||
ADVOWARE_DATABASE=your_db
|
||||
ADVOWARE_USER=api_user
|
||||
ADVOWARE_ROLE=2
|
||||
ADVOWARE_PASSWORD=your_password
|
||||
ADVOWARE_TOKEN_LIFETIME_MINUTES=55
|
||||
ADVOWARE_API_TIMEOUT_SECONDS=30
|
||||
ADVOWARE_WRITE_PROTECTION=true
|
||||
|
||||
# Redis
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB_ADVOWARE_CACHE=1
|
||||
REDIS_DB_CALENDAR_SYNC=2
|
||||
REDIS_TIMEOUT_SECONDS=5
|
||||
|
||||
# Google Calendar
|
||||
GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=/opt/motia-app/service-account.json
|
||||
|
||||
# Calendar Sync
|
||||
CALENDAR_SYNC_ANONYMIZE_GOOGLE_EVENTS=true
|
||||
CALENDAR_SYNC_DEBUG_KUERZEL=
|
||||
|
||||
# PostgreSQL (optional)
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_USER=calendar_sync_user
|
||||
POSTGRES_PASSWORD=your_pg_password
|
||||
POSTGRES_DB_NAME=calendar_sync_db
|
||||
|
||||
# EspoCRM
|
||||
ESPOCRM_MARVIN_API_KEY=your_webhook_key
|
||||
|
||||
# Motia
|
||||
NODE_ENV=production
|
||||
HOST=0.0.0.0
|
||||
MOTIA_LOG_LEVEL=info
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Configuration not found"
|
||||
|
||||
```bash
|
||||
# Check if .env exists
|
||||
ls -la .env
|
||||
|
||||
# Check environment variables
|
||||
env | grep ADVOWARE
|
||||
|
||||
# Check systemd environment
|
||||
systemctl show motia.service -p Environment
|
||||
```
|
||||
|
||||
### "Redis connection failed"
|
||||
|
||||
```bash
|
||||
# Check Redis is running
|
||||
sudo systemctl status redis-server
|
||||
|
||||
# Test connection
|
||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping
|
||||
|
||||
# Check config
|
||||
echo "REDIS_HOST: $REDIS_HOST"
|
||||
echo "REDIS_PORT: $REDIS_PORT"
|
||||
```
|
||||
|
||||
### "API authentication failed"
|
||||
|
||||
```bash
|
||||
# Check if API key is valid Base64
|
||||
echo $ADVOWARE_API_KEY | base64 -d
|
||||
|
||||
# Clear token cache
|
||||
redis-cli -n 1 DEL advoware_access_token
|
||||
|
||||
# Check logs
|
||||
sudo journalctl -u motia.service | grep -i "token\|auth"
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Development Guide](DEVELOPMENT.md)
|
||||
- [Deployment Guide](DEPLOYMENT.md)
|
||||
- [Troubleshooting](TROUBLESHOOTING.md)
|
||||
- [Google Setup](../GOOGLE_SETUP_README.md)
|
||||
624
bitbylaw/docs/DEPLOYMENT.md
Normal file
624
bitbylaw/docs/DEPLOYMENT.md
Normal file
@@ -0,0 +1,624 @@
|
||||
# Deployment Guide
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Root/sudo access zum Server
|
||||
- Ubuntu/Debian Linux (tested on Ubuntu 22.04+)
|
||||
- Internet-Zugang für Package-Installation
|
||||
|
||||
### Installation Steps
|
||||
|
||||
#### 1. System Dependencies
|
||||
|
||||
```bash
|
||||
# Update system
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
|
||||
# Install Node.js 18.x
|
||||
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
# Install Python 3.13
|
||||
sudo apt-get install -y python3.13 python3.13-venv python3.13-dev
|
||||
|
||||
# Install Redis
|
||||
sudo apt-get install -y redis-server
|
||||
|
||||
# Install Git
|
||||
sudo apt-get install -y git
|
||||
|
||||
# Start Redis
|
||||
sudo systemctl enable redis-server
|
||||
sudo systemctl start redis-server
|
||||
```
|
||||
|
||||
#### 2. Application Setup
|
||||
|
||||
```bash
|
||||
# Create application directory
|
||||
sudo mkdir -p /opt/motia-app
|
||||
cd /opt/motia-app
|
||||
|
||||
# Clone repository (oder rsync von Development)
|
||||
git clone <repository-url> bitbylaw
|
||||
cd bitbylaw
|
||||
|
||||
# Create www-data user if not exists
|
||||
sudo useradd -r -s /bin/bash www-data || true
|
||||
|
||||
# Set ownership
|
||||
sudo chown -R www-data:www-data /opt/motia-app
|
||||
```
|
||||
|
||||
#### 3. Node.js Dependencies
|
||||
|
||||
```bash
|
||||
# Als www-data user
|
||||
sudo -u www-data bash
|
||||
cd /opt/motia-app/bitbylaw
|
||||
|
||||
# Install Node.js packages
|
||||
npm install
|
||||
|
||||
# Build TypeScript (falls nötig)
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### 4. Python Dependencies
|
||||
|
||||
```bash
|
||||
# Als www-data user
|
||||
cd /opt/motia-app/bitbylaw
|
||||
|
||||
# Create virtual environment
|
||||
python3.13 -m venv python_modules
|
||||
|
||||
# Activate
|
||||
source python_modules/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Deactivate
|
||||
deactivate
|
||||
```
|
||||
|
||||
#### 5. Service Account Setup
|
||||
|
||||
```bash
|
||||
# Copy service account JSON
|
||||
sudo cp service-account.json /opt/motia-app/service-account.json
|
||||
|
||||
# Set secure permissions
|
||||
sudo chmod 600 /opt/motia-app/service-account.json
|
||||
sudo chown www-data:www-data /opt/motia-app/service-account.json
|
||||
```
|
||||
|
||||
Siehe auch: [GOOGLE_SETUP_README.md](../GOOGLE_SETUP_README.md)
|
||||
|
||||
#### 6. systemd Service
|
||||
|
||||
Erstellen Sie `/etc/systemd/system/motia.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Motia Backend Framework
|
||||
After=network.target redis-server.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/opt/motia-app/bitbylaw
|
||||
|
||||
# Environment Variables
|
||||
Environment=NODE_ENV=production
|
||||
Environment=NODE_OPTIONS=--max-old-space-size=8192 --inspect --heapsnapshot-signal=SIGUSR2
|
||||
Environment=HOST=0.0.0.0
|
||||
Environment=MOTIA_LOG_LEVEL=info
|
||||
Environment=NPM_CONFIG_CACHE=/opt/motia-app/.npm-cache
|
||||
|
||||
# Advoware Configuration (ADJUST VALUES!)
|
||||
Environment=ADVOWARE_API_BASE_URL=https://www2.advo-net.net:90/
|
||||
Environment=ADVOWARE_PRODUCT_ID=64
|
||||
Environment=ADVOWARE_APP_ID=your_app_id
|
||||
Environment=ADVOWARE_API_KEY=your_api_key_base64
|
||||
Environment=ADVOWARE_KANZLEI=your_kanzlei
|
||||
Environment=ADVOWARE_DATABASE=your_database
|
||||
Environment=ADVOWARE_USER=your_user
|
||||
Environment=ADVOWARE_ROLE=2
|
||||
Environment=ADVOWARE_PASSWORD=your_password
|
||||
Environment=ADVOWARE_WRITE_PROTECTION=false
|
||||
|
||||
# Redis Configuration
|
||||
Environment=REDIS_HOST=localhost
|
||||
Environment=REDIS_PORT=6379
|
||||
Environment=REDIS_DB_ADVOWARE_CACHE=1
|
||||
Environment=REDIS_DB_CALENDAR_SYNC=2
|
||||
|
||||
# Google Calendar
|
||||
Environment=GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=/opt/motia-app/service-account.json
|
||||
|
||||
# EspoCRM (if used)
|
||||
Environment=ESPOCRM_MARVIN_API_KEY=your_webhook_key
|
||||
|
||||
# Start Command
|
||||
ExecStart=/bin/bash -c 'source /opt/motia-app/python_modules/bin/activate && /usr/bin/npm start'
|
||||
|
||||
# Restart Policy
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
# Security
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**WICHTIG**: Passen Sie alle `your_*` Werte an!
|
||||
|
||||
#### 7. Enable and Start Service
|
||||
|
||||
```bash
|
||||
# Reload systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# Enable service (autostart)
|
||||
sudo systemctl enable motia.service
|
||||
|
||||
# Start service
|
||||
sudo systemctl start motia.service
|
||||
|
||||
# Check status
|
||||
sudo systemctl status motia.service
|
||||
```
|
||||
|
||||
#### 8. Verify Installation
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
sudo journalctl -u motia.service -f
|
||||
|
||||
# Test API
|
||||
curl http://localhost:3000/health # (wenn implementiert)
|
||||
|
||||
# Test Advoware Proxy
|
||||
curl "http://localhost:3000/advoware/proxy?endpoint=employees"
|
||||
```
|
||||
|
||||
## Reverse Proxy Setup (nginx)
|
||||
|
||||
### Install nginx
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y nginx
|
||||
```
|
||||
|
||||
### Configure
|
||||
|
||||
`/etc/nginx/sites-available/motia`:
|
||||
|
||||
```nginx
|
||||
upstream motia_backend {
|
||||
server 127.0.0.1:3000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
# Redirect to HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
# SSL Configuration (Let's Encrypt)
|
||||
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
||||
|
||||
# Security Headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Proxy Settings
|
||||
location / {
|
||||
proxy_pass http://motia_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Access Log
|
||||
access_log /var/log/nginx/motia-access.log;
|
||||
error_log /var/log/nginx/motia-error.log;
|
||||
}
|
||||
```
|
||||
|
||||
### Enable and Restart
|
||||
|
||||
```bash
|
||||
# Enable site
|
||||
sudo ln -s /etc/nginx/sites-available/motia /etc/nginx/sites-enabled/
|
||||
|
||||
# Test configuration
|
||||
sudo nginx -t
|
||||
|
||||
# Restart nginx
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### SSL Certificate (Let's Encrypt)
|
||||
|
||||
```bash
|
||||
# Install certbot
|
||||
sudo apt-get install -y certbot python3-certbot-nginx
|
||||
|
||||
# Obtain certificate
|
||||
sudo certbot --nginx -d your-domain.com
|
||||
|
||||
# Auto-renewal is configured automatically
|
||||
```
|
||||
|
||||
## Firewall Configuration
|
||||
|
||||
```bash
|
||||
# Allow SSH
|
||||
sudo ufw allow 22/tcp
|
||||
|
||||
# Allow HTTP/HTTPS (if using nginx)
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
|
||||
# Enable firewall
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
**Wichtig**: Port 3000 NICHT öffentlich öffnen (nur via nginx reverse proxy)
|
||||
|
||||
## Monitoring
|
||||
|
||||
### systemd Service Status
|
||||
|
||||
```bash
|
||||
# Status anzeigen
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# Ist enabled?
|
||||
sudo systemctl is-enabled motia.service
|
||||
|
||||
# Ist aktiv?
|
||||
sudo systemctl is-active motia.service
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# Live logs
|
||||
sudo journalctl -u motia.service -f
|
||||
|
||||
# Last 100 lines
|
||||
sudo journalctl -u motia.service -n 100
|
||||
|
||||
# Since today
|
||||
sudo journalctl -u motia.service --since today
|
||||
|
||||
# Filter by priority (error only)
|
||||
sudo journalctl -u motia.service -p err
|
||||
```
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
# CPU and Memory
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# Detailed process info
|
||||
ps aux | grep motia
|
||||
|
||||
# Memory usage
|
||||
sudo pmap $(pgrep -f "motia start") | tail -n 1
|
||||
```
|
||||
|
||||
### Redis Monitoring
|
||||
|
||||
```bash
|
||||
# Connect to Redis
|
||||
redis-cli
|
||||
|
||||
# Show info
|
||||
INFO
|
||||
|
||||
# Show database sizes
|
||||
INFO keyspace
|
||||
|
||||
# Monitor commands (real-time)
|
||||
MONITOR
|
||||
|
||||
# Show memory usage
|
||||
MEMORY USAGE <key>
|
||||
```
|
||||
|
||||
## Backup Strategy
|
||||
|
||||
### Application Code
|
||||
|
||||
```bash
|
||||
# Git-based backup
|
||||
cd /opt/motia-app/bitbylaw
|
||||
git pull origin main
|
||||
|
||||
# Or: rsync backup
|
||||
rsync -av /opt/motia-app/bitbylaw/ /backup/motia-app/
|
||||
```
|
||||
|
||||
### Redis Data
|
||||
|
||||
```bash
|
||||
# RDB snapshot (automatic by Redis)
|
||||
# Location: /var/lib/redis/dump.rdb
|
||||
|
||||
# Manual backup
|
||||
sudo cp /var/lib/redis/dump.rdb /backup/redis-dump-$(date +%Y%m%d).rdb
|
||||
|
||||
# Restore
|
||||
sudo systemctl stop redis-server
|
||||
sudo cp /backup/redis-dump-20260207.rdb /var/lib/redis/dump.rdb
|
||||
sudo chown redis:redis /var/lib/redis/dump.rdb
|
||||
sudo systemctl start redis-server
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
```bash
|
||||
# Backup systemd service
|
||||
sudo cp /etc/systemd/system/motia.service /backup/motia.service
|
||||
|
||||
# Backup nginx config
|
||||
sudo cp /etc/nginx/sites-available/motia /backup/nginx-motia.conf
|
||||
|
||||
# Backup service account
|
||||
sudo cp /opt/motia-app/service-account.json /backup/service-account.json.backup
|
||||
```
|
||||
|
||||
## Updates & Maintenance
|
||||
|
||||
### Application Update
|
||||
|
||||
```bash
|
||||
# 1. Pull latest code
|
||||
cd /opt/motia-app/bitbylaw
|
||||
sudo -u www-data git pull origin main
|
||||
|
||||
# 2. Update dependencies
|
||||
sudo -u www-data npm install
|
||||
sudo -u www-data bash -c 'source python_modules/bin/activate && pip install -r requirements.txt'
|
||||
|
||||
# 3. Restart service
|
||||
sudo systemctl restart motia.service
|
||||
|
||||
# 4. Verify
|
||||
sudo journalctl -u motia.service -f
|
||||
```
|
||||
|
||||
### Zero-Downtime Deployment
|
||||
|
||||
Für zukünftige Implementierung mit Blue-Green Deployment:
|
||||
|
||||
```bash
|
||||
# 1. Deploy to staging directory
|
||||
# 2. Run health checks
|
||||
# 3. Switch symlink
|
||||
# 4. Reload service
|
||||
# 5. Rollback if issues
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
|
||||
**Aktuell**: Keine Datenbank-Migrationen (nur Redis)
|
||||
|
||||
**Zukünftig** (PostgreSQL):
|
||||
```bash
|
||||
# Run migrations
|
||||
python manage.py migrate
|
||||
```
|
||||
|
||||
## Security Hardening
|
||||
|
||||
### File Permissions
|
||||
|
||||
```bash
|
||||
# Application files
|
||||
sudo chown -R www-data:www-data /opt/motia-app
|
||||
sudo chmod 755 /opt/motia-app
|
||||
sudo chmod 755 /opt/motia-app/bitbylaw
|
||||
|
||||
# Service account
|
||||
sudo chmod 600 /opt/motia-app/service-account.json
|
||||
sudo chown www-data:www-data /opt/motia-app/service-account.json
|
||||
|
||||
# No world-readable secrets
|
||||
sudo find /opt/motia-app -type f -name "*.json" -exec chmod 600 {} \;
|
||||
```
|
||||
|
||||
### Redis Security
|
||||
|
||||
```bash
|
||||
# Edit Redis config
|
||||
sudo nano /etc/redis/redis.conf
|
||||
|
||||
# Bind to localhost only
|
||||
bind 127.0.0.1 ::1
|
||||
|
||||
# Disable dangerous commands (optional)
|
||||
rename-command FLUSHDB ""
|
||||
rename-command FLUSHALL ""
|
||||
rename-command CONFIG ""
|
||||
|
||||
# Restart Redis
|
||||
sudo systemctl restart redis-server
|
||||
```
|
||||
|
||||
### systemd Hardening
|
||||
|
||||
Bereits in Service-Datei enthalten:
|
||||
- `NoNewPrivileges=true` - Verhindert Privilege-Escalation
|
||||
- `PrivateTmp=true` - Isoliertes /tmp
|
||||
- User: `www-data` (non-root)
|
||||
|
||||
Weitere Optionen:
|
||||
```ini
|
||||
[Service]
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/opt/motia-app
|
||||
```
|
||||
|
||||
## Disaster Recovery
|
||||
|
||||
### Service Crashed
|
||||
|
||||
```bash
|
||||
# Check status
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u motia.service -n 100
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart motia.service
|
||||
|
||||
# If still failing, check:
|
||||
# - Redis is running
|
||||
# - Service account file exists
|
||||
# - Environment variables are set
|
||||
```
|
||||
|
||||
### Redis Data Loss
|
||||
|
||||
```bash
|
||||
# Restore from backup
|
||||
sudo systemctl stop redis-server
|
||||
sudo cp /backup/redis-dump-latest.rdb /var/lib/redis/dump.rdb
|
||||
sudo chown redis:redis /var/lib/redis/dump.rdb
|
||||
sudo systemctl start redis-server
|
||||
|
||||
# Clear specific data if corrupted
|
||||
redis-cli -n 1 FLUSHDB # Advoware cache
|
||||
redis-cli -n 2 FLUSHDB # Calendar sync
|
||||
```
|
||||
|
||||
### Complete System Failure
|
||||
|
||||
```bash
|
||||
# 1. Fresh server setup (siehe Installation Steps)
|
||||
# 2. Restore application code from Git/Backup
|
||||
# 3. Restore configuration (systemd, nginx)
|
||||
# 4. Restore service-account.json
|
||||
# 5. Restore Redis data (optional, will rebuild)
|
||||
# 6. Start services
|
||||
```
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### Node.js Memory
|
||||
|
||||
In systemd service:
|
||||
```ini
|
||||
Environment=NODE_OPTIONS=--max-old-space-size=8192 # 8GB
|
||||
```
|
||||
|
||||
### Redis Memory
|
||||
|
||||
In `/etc/redis/redis.conf`:
|
||||
```
|
||||
maxmemory 2gb
|
||||
maxmemory-policy allkeys-lru
|
||||
```
|
||||
|
||||
### Linux Kernel
|
||||
|
||||
```bash
|
||||
# Increase file descriptors
|
||||
echo "fs.file-max = 65536" | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
|
||||
# For www-data user
|
||||
sudo nano /etc/security/limits.conf
|
||||
# Add:
|
||||
www-data soft nofile 65536
|
||||
www-data hard nofile 65536
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
### Automated Monitoring
|
||||
|
||||
Cron job für Health Checks:
|
||||
|
||||
```bash
|
||||
# /usr/local/bin/motia-health-check.sh
|
||||
#!/bin/bash
|
||||
if ! systemctl is-active --quiet motia.service; then
|
||||
echo "Motia service is down!" | mail -s "ALERT: Motia Down" admin@example.com
|
||||
systemctl start motia.service
|
||||
fi
|
||||
```
|
||||
|
||||
```bash
|
||||
# Add to crontab
|
||||
sudo crontab -e
|
||||
# Add line:
|
||||
*/5 * * * * /usr/local/bin/motia-health-check.sh
|
||||
```
|
||||
|
||||
### External Monitoring
|
||||
|
||||
Services wie Uptime Robot, Pingdom, etc. können verwendet werden:
|
||||
- HTTP Endpoint: `https://your-domain.com/health`
|
||||
- Check-Interval: 5 Minuten
|
||||
- Alert via Email/SMS
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
```bash
|
||||
# 1. Stop current service
|
||||
sudo systemctl stop motia.service
|
||||
|
||||
# 2. Revert to previous version
|
||||
cd /opt/motia-app/bitbylaw
|
||||
sudo -u www-data git log # Find previous commit
|
||||
sudo -u www-data git reset --hard <commit-hash>
|
||||
|
||||
# 3. Restore dependencies (if needed)
|
||||
sudo -u www-data npm install
|
||||
|
||||
# 4. Start service
|
||||
sudo systemctl start motia.service
|
||||
|
||||
# 5. Verify
|
||||
sudo journalctl -u motia.service -f
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Architecture](ARCHITECTURE.md)
|
||||
- [Configuration](CONFIGURATION.md)
|
||||
- [Troubleshooting](TROUBLESHOOTING.md)
|
||||
656
bitbylaw/docs/DEVELOPMENT.md
Normal file
656
bitbylaw/docs/DEVELOPMENT.md
Normal file
@@ -0,0 +1,656 @@
|
||||
# Development Guide
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js**: 18.x oder höher
|
||||
- **Python**: 3.13 oder höher
|
||||
- **Redis**: 6.x oder höher
|
||||
- **Git**: Für Version Control
|
||||
- **Motia CLI**: Wird automatisch via npm installiert
|
||||
|
||||
### Initial Setup
|
||||
|
||||
```bash
|
||||
# 1. Repository navigieren
|
||||
cd /opt/motia-app/bitbylaw
|
||||
|
||||
# 2. Node.js Dependencies installieren
|
||||
npm install
|
||||
|
||||
# 3. Python Virtual Environment erstellen (falls nicht vorhanden)
|
||||
python3.13 -m venv python_modules
|
||||
|
||||
# 4. Python Virtual Environment aktivieren
|
||||
source python_modules/bin/activate
|
||||
|
||||
# 5. Python Dependencies installieren
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 6. Redis starten (falls nicht läuft)
|
||||
sudo systemctl start redis-server
|
||||
|
||||
# 7. Environment Variables konfigurieren (siehe CONFIGURATION.md)
|
||||
# Erstellen Sie eine .env Datei oder setzen Sie in systemd
|
||||
|
||||
# 8. Development Mode starten
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Entwicklungsumgebung
|
||||
|
||||
**Empfohlene IDE**: VS Code mit Extensions:
|
||||
- Python (Microsoft)
|
||||
- TypeScript (Built-in)
|
||||
- ESLint
|
||||
- Prettier
|
||||
|
||||
**VS Code Settings** (`.vscode/settings.json`):
|
||||
```json
|
||||
{
|
||||
"python.defaultInterpreterPath": "${workspaceFolder}/python_modules/bin/python",
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.pylintEnabled": false,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"editor.formatOnSave": true,
|
||||
"files.exclude": {
|
||||
"**/__pycache__": true,
|
||||
"**/node_modules": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
bitbylaw/
|
||||
├── docs/ # Dokumentation
|
||||
│ ├── ARCHITECTURE.md # System-Architektur
|
||||
│ ├── DEVELOPMENT.md # Dieser Guide
|
||||
│ ├── API.md # API-Referenz
|
||||
│ ├── CONFIGURATION.md # Environment & Config
|
||||
│ ├── DEPLOYMENT.md # Deployment-Guide
|
||||
│ └── TROUBLESHOOTING.md # Fehlerbehebung
|
||||
├── steps/ # Motia Steps (Business Logic)
|
||||
│ ├── advoware_proxy/ # API Proxy Steps
|
||||
│ │ ├── README.md # Modul-Dokumentation
|
||||
│ │ ├── *.py # Step-Implementierungen
|
||||
│ │ └── *.md # Step-Detail-Doku
|
||||
│ ├── advoware_cal_sync/ # Calendar Sync Steps
|
||||
│ │ ├── README.md
|
||||
│ │ ├── *.py
|
||||
│ │ └── *.md
|
||||
│ └── vmh/ # VMH Webhook Steps
|
||||
│ ├── README.md
|
||||
│ ├── webhook/ # Webhook Receiver
|
||||
│ └── *.py
|
||||
├── services/ # Shared Services
|
||||
│ └── advoware.py # Advoware API Client
|
||||
├── config.py # Configuration Loader
|
||||
├── package.json # Node.js Dependencies
|
||||
├── requirements.txt # Python Dependencies
|
||||
├── tsconfig.json # TypeScript Config
|
||||
├── motia-workbench.json # Motia Flow Definitions
|
||||
└── README.md # Projekt-Übersicht
|
||||
```
|
||||
|
||||
### Konventionen
|
||||
|
||||
**Verzeichnisse**:
|
||||
- `steps/` - Motia Steps (Handler-Funktionen)
|
||||
- `services/` - Wiederverwendbare Service-Layer
|
||||
- `docs/` - Dokumentation
|
||||
- `python_modules/` - Python Virtual Environment (nicht committen)
|
||||
- `node_modules/` - Node.js Dependencies (nicht committen)
|
||||
|
||||
**Dateinamen**:
|
||||
- Steps: `{module}_{action}_step.py` (z.B. `calendar_sync_cron_step.py`)
|
||||
- Services: `{service_name}.py` (z.B. `advoware.py`)
|
||||
- Dokumentation: `{STEP_NAME}.md` oder `{TOPIC}.md`
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Python
|
||||
|
||||
**Style Guide**: PEP 8 mit folgenden Anpassungen:
|
||||
- Line length: 120 Zeichen (statt 79)
|
||||
- String quotes: Single quotes bevorzugt
|
||||
|
||||
**Linting**:
|
||||
```bash
|
||||
# Flake8 check
|
||||
flake8 steps/ services/
|
||||
|
||||
# Autopep8 formatting
|
||||
autopep8 --in-place --aggressive --aggressive steps/**/*.py
|
||||
```
|
||||
|
||||
**Type Hints**:
|
||||
```python
|
||||
from typing import Dict, List, Optional, Any
|
||||
|
||||
async def handler(req: Dict[str, Any], context: Any) -> Dict[str, Any]:
|
||||
pass
|
||||
```
|
||||
|
||||
**Docstrings**:
|
||||
```python
|
||||
def function_name(param1: str, param2: int) -> bool:
|
||||
"""
|
||||
Brief description of function.
|
||||
|
||||
Args:
|
||||
param1: Description of param1
|
||||
param2: Description of param2
|
||||
|
||||
Returns:
|
||||
Description of return value
|
||||
|
||||
Raises:
|
||||
ValueError: When something goes wrong
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### TypeScript/JavaScript
|
||||
|
||||
**Style Guide**: Standard mit Motia-Konventionen
|
||||
|
||||
**Formatting**: Prettier (automatisch via Motia)
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
**Variables**: `snake_case` (Python), `camelCase` (TypeScript)
|
||||
**Constants**: `UPPER_CASE`
|
||||
**Classes**: `PascalCase`
|
||||
**Functions**: `snake_case` (Python), `camelCase` (TypeScript)
|
||||
**Files**: `snake_case.py`, `kebab-case.ts`
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Pattern**:
|
||||
```python
|
||||
async def handler(req, context):
|
||||
try:
|
||||
# Main logic
|
||||
result = await some_operation()
|
||||
return {'status': 200, 'body': {'result': result}}
|
||||
|
||||
except SpecificError as e:
|
||||
# Handle known errors
|
||||
context.logger.error(f"Specific error: {e}")
|
||||
return {'status': 400, 'body': {'error': 'Bad request'}}
|
||||
|
||||
except Exception as e:
|
||||
# Catch-all
|
||||
context.logger.error(f"Unexpected error: {e}", exc_info=True)
|
||||
return {'status': 500, 'body': {'error': 'Internal error'}}
|
||||
```
|
||||
|
||||
**Logging**:
|
||||
```python
|
||||
# Use context.logger for Motia Workbench integration
|
||||
context.logger.debug("Detailed information")
|
||||
context.logger.info("Normal operation")
|
||||
context.logger.warning("Warning message")
|
||||
context.logger.error("Error message", exc_info=True) # Include stack trace
|
||||
```
|
||||
|
||||
## Motia Step Development
|
||||
|
||||
### Step Structure
|
||||
|
||||
Every Step must have:
|
||||
1. **Config Dictionary**: Defines step metadata
|
||||
2. **Handler Function**: Implements business logic
|
||||
|
||||
**Minimal Example**:
|
||||
```python
|
||||
config = {
|
||||
'type': 'api', # api|event|cron
|
||||
'name': 'My API Step',
|
||||
'description': 'Brief description',
|
||||
'path': '/api/my-endpoint', # For API steps
|
||||
'method': 'GET', # For API steps
|
||||
'schedule': '0 2 * * *', # For cron steps
|
||||
'emits': ['topic.name'], # Events this step emits
|
||||
'subscribes': ['other.topic'], # Events this step subscribes to (event steps)
|
||||
'flows': ['my-flow'] # Flow membership
|
||||
}
|
||||
|
||||
async def handler(req, context):
|
||||
"""Handler function - must be async."""
|
||||
# req: Request object (API) or Event data (event step)
|
||||
# context: Motia context (logger, emit, etc.)
|
||||
|
||||
# Business logic here
|
||||
|
||||
# For API steps: return HTTP response
|
||||
return {'status': 200, 'body': {'result': 'success'}}
|
||||
|
||||
# For event steps: no return value (or None)
|
||||
```
|
||||
|
||||
### Step Types
|
||||
|
||||
**1. API Steps** (`type: 'api'`):
|
||||
```python
|
||||
config = {
|
||||
'type': 'api',
|
||||
'name': 'My Endpoint',
|
||||
'path': '/api/resource',
|
||||
'method': 'POST',
|
||||
'emits': [],
|
||||
'flows': ['main']
|
||||
}
|
||||
|
||||
async def handler(req, context):
|
||||
# Access request data
|
||||
body = req.get('body')
|
||||
query_params = req.get('queryParams')
|
||||
headers = req.get('headers')
|
||||
|
||||
# Return HTTP response
|
||||
return {
|
||||
'status': 200,
|
||||
'body': {'data': 'response'},
|
||||
'headers': {'X-Custom': 'value'}
|
||||
}
|
||||
```
|
||||
|
||||
**2. Event Steps** (`type: 'event'`):
|
||||
```python
|
||||
config = {
|
||||
'type': 'event',
|
||||
'name': 'Process Event',
|
||||
'subscribes': ['my.topic'],
|
||||
'emits': ['other.topic'],
|
||||
'flows': ['main']
|
||||
}
|
||||
|
||||
async def handler(event_data, context):
|
||||
# Process event
|
||||
entity_id = event_data.get('entity_id')
|
||||
|
||||
# Emit new event
|
||||
await context.emit({
|
||||
'topic': 'other.topic',
|
||||
'data': {'processed': True}
|
||||
})
|
||||
|
||||
# No return value needed
|
||||
```
|
||||
|
||||
**3. Cron Steps** (`type: 'cron'`):
|
||||
```python
|
||||
config = {
|
||||
'type': 'cron',
|
||||
'name': 'Daily Job',
|
||||
'schedule': '0 2 * * *', # Cron expression
|
||||
'emits': ['job.complete'],
|
||||
'flows': ['main']
|
||||
}
|
||||
|
||||
async def handler(req, context):
|
||||
# Scheduled logic
|
||||
context.logger.info("Cron job triggered")
|
||||
|
||||
# Emit event to start pipeline
|
||||
await context.emit({
|
||||
'topic': 'job.complete',
|
||||
'data': {}
|
||||
})
|
||||
```
|
||||
|
||||
### Context API
|
||||
|
||||
**Available Methods**:
|
||||
```python
|
||||
# Logging
|
||||
context.logger.debug(msg)
|
||||
context.logger.info(msg)
|
||||
context.logger.warning(msg)
|
||||
context.logger.error(msg, exc_info=True)
|
||||
|
||||
# Event Emission
|
||||
await context.emit({
|
||||
'topic': 'my.topic',
|
||||
'data': {'key': 'value'}
|
||||
})
|
||||
|
||||
# Flow information
|
||||
context.flow_id # Current flow ID
|
||||
context.step_name # Current step name
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
**Location**: Tests neben dem Code (z.B. `*_test.py`)
|
||||
|
||||
**Framework**: pytest
|
||||
|
||||
```python
|
||||
# test_my_step.py
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from my_step import handler, config
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handler_success():
|
||||
# Arrange
|
||||
req = {'body': {'key': 'value'}}
|
||||
context = MagicMock()
|
||||
context.logger = MagicMock()
|
||||
|
||||
# Act
|
||||
result = await handler(req, context)
|
||||
|
||||
# Assert
|
||||
assert result['status'] == 200
|
||||
assert 'result' in result['body']
|
||||
```
|
||||
|
||||
**Run Tests**:
|
||||
```bash
|
||||
pytest steps/
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Manual Testing mit curl**:
|
||||
|
||||
```bash
|
||||
# API Step testen
|
||||
curl -X POST "http://localhost:3000/api/my-endpoint" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"key": "value"}'
|
||||
|
||||
# Mit Query Parameters
|
||||
curl -X GET "http://localhost:3000/advoware/proxy?endpoint=employees"
|
||||
```
|
||||
|
||||
**Motia Workbench**: Nutzen Sie die Workbench UI zum Testen und Debugging
|
||||
|
||||
### Test-Daten
|
||||
|
||||
**Redis Mock Data**:
|
||||
```bash
|
||||
# Set test token
|
||||
redis-cli -n 1 SET advoware_access_token "test_token" EX 3600
|
||||
|
||||
# Set test lock
|
||||
redis-cli -n 1 SET "calendar_sync:lock:TEST" "1" EX 300
|
||||
|
||||
# Check dedup set
|
||||
redis-cli -n 1 SMEMBERS "vmh:beteiligte:create_pending"
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Local Development
|
||||
|
||||
**Start in Dev Mode**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Enable Debug Logging**:
|
||||
```bash
|
||||
export MOTIA_LOG_LEVEL=debug
|
||||
npm start
|
||||
```
|
||||
|
||||
**Node.js Inspector**:
|
||||
```bash
|
||||
# Already enabled in systemd (--inspect)
|
||||
# Connect with Chrome DevTools: chrome://inspect
|
||||
```
|
||||
|
||||
### Motia Workbench
|
||||
|
||||
**Access**: `http://localhost:3000/workbench` (wenn verfügbar)
|
||||
|
||||
**Features**:
|
||||
- Live logs
|
||||
- Flow visualization
|
||||
- Event traces
|
||||
- Step execution history
|
||||
|
||||
### Redis Debugging
|
||||
|
||||
```bash
|
||||
# Connect to Redis
|
||||
redis-cli
|
||||
|
||||
# Switch database
|
||||
SELECT 1
|
||||
|
||||
# List all keys
|
||||
KEYS *
|
||||
|
||||
# Get value
|
||||
GET advoware_access_token
|
||||
|
||||
# Check SET members
|
||||
SMEMBERS vmh:beteiligte:create_pending
|
||||
|
||||
# Monitor live commands
|
||||
MONITOR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Utility Scripts
|
||||
|
||||
### Calendar Sync Utilities
|
||||
|
||||
Helper-Scripts für Wartung und Debugging der Calendar-Sync-Funktionalität.
|
||||
|
||||
**Standort**: `scripts/calendar_sync/`
|
||||
|
||||
**Verfügbare Scripts**:
|
||||
|
||||
```bash
|
||||
# Alle Employee-Locks in Redis löschen (bei hängenden Syncs)
|
||||
python3 scripts/calendar_sync/delete_employee_locks.py
|
||||
|
||||
# Alle Google Kalender löschen (außer Primary) - VORSICHT!
|
||||
python3 scripts/calendar_sync/delete_all_calendars.py
|
||||
```
|
||||
|
||||
**Use Cases**:
|
||||
- **Lock Cleanup**: Wenn ein Sync-Prozess abgestürzt ist und Locks nicht aufgeräumt wurden
|
||||
- **Calendar Reset**: Bei fehlerhafter Synchronisation oder Tests
|
||||
- **Debugging**: Untersuchung von Sync-Problemen
|
||||
|
||||
**Dokumentation**: [scripts/calendar_sync/README.md](../scripts/calendar_sync/README.md)
|
||||
|
||||
**⚠️ Wichtig**:
|
||||
- Immer Motia Service stoppen vor Cleanup: `sudo systemctl stop motia`
|
||||
- Nach Cleanup Service neu starten: `sudo systemctl start motia`
|
||||
- `delete_all_calendars.py` löscht unwiderruflich alle Kalender!
|
||||
|
||||
---
|
||||
|
||||
### Common Issues
|
||||
|
||||
**1. Import Errors**:
|
||||
```bash
|
||||
# Ensure PYTHONPATH is set
|
||||
export PYTHONPATH=/opt/motia-app/bitbylaw
|
||||
source python_modules/bin/activate
|
||||
```
|
||||
|
||||
**2. Redis Connection Errors**:
|
||||
```bash
|
||||
# Check Redis is running
|
||||
sudo systemctl status redis-server
|
||||
|
||||
# Test connection
|
||||
redis-cli ping
|
||||
```
|
||||
|
||||
**3. Token Errors**:
|
||||
```bash
|
||||
# Clear cached token
|
||||
redis-cli -n 1 DEL advoware_access_token advoware_token_timestamp
|
||||
```
|
||||
|
||||
## Git Workflow
|
||||
|
||||
### Branch Strategy
|
||||
|
||||
- `main` - Production code
|
||||
- `develop` - Integration branch
|
||||
- `feature/*` - Feature branches
|
||||
- `fix/*` - Bugfix branches
|
||||
|
||||
### Commit Messages
|
||||
|
||||
**Format**: `<type>: <subject>`
|
||||
|
||||
**Types**:
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation only
|
||||
- `refactor`: Code refactoring
|
||||
- `test`: Adding tests
|
||||
- `chore`: Maintenance tasks
|
||||
|
||||
**Examples**:
|
||||
```
|
||||
feat: add calendar sync retry logic
|
||||
fix: prevent duplicate webhook processing
|
||||
docs: update API documentation
|
||||
refactor: extract common validation logic
|
||||
```
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
1. Create feature branch from `develop`
|
||||
2. Implement changes
|
||||
3. Write/update tests
|
||||
4. Update documentation
|
||||
5. Create PR with description
|
||||
6. Code review
|
||||
7. Merge to `develop`
|
||||
8. Deploy to staging
|
||||
9. Merge to `main` (production)
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Profiling
|
||||
|
||||
**Python Memory Profiling**:
|
||||
```bash
|
||||
# Install memory_profiler
|
||||
pip install memory_profiler
|
||||
|
||||
# Profile a function
|
||||
python -m memory_profiler steps/my_step.py
|
||||
```
|
||||
|
||||
**Node.js Profiling**:
|
||||
```bash
|
||||
# Already enabled with --inspect flag
|
||||
# Use Chrome DevTools Performance tab
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
**Async/Await**:
|
||||
```python
|
||||
# Good: Concurrent requests
|
||||
results = await asyncio.gather(
|
||||
fetch_data_1(),
|
||||
fetch_data_2()
|
||||
)
|
||||
|
||||
# Bad: Sequential (slow)
|
||||
result1 = await fetch_data_1()
|
||||
result2 = await fetch_data_2()
|
||||
```
|
||||
|
||||
**Redis Pipelining**:
|
||||
```python
|
||||
# Good: Batch operations
|
||||
pipe = redis.pipeline()
|
||||
pipe.get('key1')
|
||||
pipe.get('key2')
|
||||
results = pipe.execute()
|
||||
|
||||
# Bad: Multiple round-trips
|
||||
val1 = redis.get('key1')
|
||||
val2 = redis.get('key2')
|
||||
```
|
||||
|
||||
**Avoid N+1 Queries**:
|
||||
```python
|
||||
# Good: Batch fetch
|
||||
employee_ids = [1, 2, 3]
|
||||
employees = await advoware.api_call(
|
||||
'/employees',
|
||||
params={'ids': ','.join(map(str, employee_ids))}
|
||||
)
|
||||
|
||||
# Bad: Loop with API calls
|
||||
employees = []
|
||||
for emp_id in employee_ids:
|
||||
emp = await advoware.api_call(f'/employees/{emp_id}')
|
||||
employees.append(emp)
|
||||
```
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
- [ ] Code follows style guide
|
||||
- [ ] Type hints present (Python)
|
||||
- [ ] Error handling implemented
|
||||
- [ ] Logging added at key points
|
||||
- [ ] Tests written/updated
|
||||
- [ ] Documentation updated
|
||||
- [ ] No secrets in code
|
||||
- [ ] Performance considered
|
||||
- [ ] Redis keys documented
|
||||
- [ ] Events documented
|
||||
|
||||
## Deployment
|
||||
|
||||
See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed deployment instructions.
|
||||
|
||||
**Quick Deploy to Production**:
|
||||
```bash
|
||||
# 1. Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# 2. Install dependencies
|
||||
npm install
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 3. Restart service
|
||||
sudo systemctl restart motia.service
|
||||
|
||||
# 4. Check status
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# 5. Monitor logs
|
||||
sudo journalctl -u motia.service -f
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- [Motia Framework](https://motia.dev)
|
||||
- [Advoware API](docs/advoware/) (internal)
|
||||
- [Google Calendar API](https://developers.google.com/calendar)
|
||||
|
||||
### Tools
|
||||
- [Redis Commander](http://localhost:8081) (if installed)
|
||||
- [Motia Workbench](http://localhost:3000/workbench)
|
||||
|
||||
### Team Contacts
|
||||
- Architecture Questions: [Lead Developer]
|
||||
- Deployment Issues: [DevOps Team]
|
||||
- API Access: [Integration Team]
|
||||
92
bitbylaw/docs/GOOGLE_SETUP.md
Normal file
92
bitbylaw/docs/GOOGLE_SETUP.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Google Service Account Setup für Advoware Calendar Sync
|
||||
|
||||
## Übersicht
|
||||
Dieser Calendar Sync verwendet **ausschließlich Google Service Accounts** für die Authentifizierung. Kein OAuth, kein Browser-Interaktion - perfekt für Server-Umgebungen!
|
||||
|
||||
## Voraussetzungen
|
||||
- Google Cloud Console Zugang
|
||||
- Berechtigung zum Erstellen von Service Accounts
|
||||
- (Optional) Google Workspace Admin Zugang für Domain-wide Delegation
|
||||
|
||||
## Schritt 1: Google Cloud Console aufrufen
|
||||
1. Gehen Sie zu: https://console.cloud.google.com/
|
||||
2. Melden Sie sich mit Ihrem Google-Konto an
|
||||
3. Wählen Sie ein bestehendes Projekt aus oder erstellen Sie ein neues
|
||||
|
||||
## Schritt 2: Google Calendar API aktivieren
|
||||
1. Klicken Sie auf "APIs & Dienste" → "Bibliothek"
|
||||
2. Suchen Sie nach "Google Calendar API"
|
||||
3. Klicken Sie auf "Google Calendar API" → "Aktivieren"
|
||||
|
||||
## Schritt 3: Service Account erstellen
|
||||
1. Gehen Sie zu "IAM & Verwaltung" → "Service-Konten"
|
||||
2. Klicken Sie auf "+ Service-Konto erstellen"
|
||||
3. Grundlegende Informationen:
|
||||
- **Service-Kontoname**: `advoware-calendar-sync`
|
||||
- **Beschreibung**: `Service Account für Advoware-Google Calendar Synchronisation`
|
||||
- **E-Mail**: wird automatisch generiert
|
||||
4. Klicken Sie auf "Erstellen und fortfahren"
|
||||
|
||||
## Schritt 4: Berechtigungen zuweisen
|
||||
1. **Rolle zuweisen**: Wählen Sie eine der folgenden Optionen:
|
||||
- Für volle Zugriffe: `Editor`
|
||||
- Für eingeschränkte Zugriffe: `Calendar API Admin` (falls verfügbar)
|
||||
2. Klicken Sie auf "Fertig"
|
||||
|
||||
## Schritt 5: JSON-Schlüssel erstellen und installieren
|
||||
1. Klicken Sie auf das neu erstellte Service-Konto
|
||||
2. Gehen Sie zum Tab "Schlüssel"
|
||||
3. Klicken Sie auf "Schlüssel hinzufügen" → "Neuen Schlüssel erstellen"
|
||||
4. Wählen Sie "JSON" als Schlüsseltyp
|
||||
5. Klicken Sie auf "Erstellen"
|
||||
6. Die JSON-Datei wird automatisch heruntergeladen
|
||||
7. **Benennen Sie die Datei um zu: `service-account.json`**
|
||||
8. **Kopieren Sie die Datei nach: `/opt/motia-app/service-account.json`**
|
||||
9. **Stellen Sie sichere Berechtigungen ein:**
|
||||
```bash
|
||||
chmod 600 /opt/motia-app/service-account.json
|
||||
```
|
||||
|
||||
## Schritt 6: Domain-wide Delegation (nur für Google Workspace)
|
||||
Falls Sie Google Workspace verwenden und auf Kalender anderer Benutzer zugreifen möchten:
|
||||
|
||||
1. Gehen Sie zurück zum Service-Konto
|
||||
2. Aktivieren Sie "Google Workspace Domain-wide Delegation"
|
||||
3. Notieren Sie sich die "Unique ID" des Service-Kontos
|
||||
4. Gehen Sie zu Google Admin Console: https://admin.google.com/
|
||||
5. "Sicherheit" → "API-Berechtigungen"
|
||||
6. "Domain-wide Delegation" → "API-Clienten verwalten"
|
||||
7. Fügen Sie die Unique ID hinzu
|
||||
8. Berechtigungen: `https://www.googleapis.com/auth/calendar`
|
||||
|
||||
## Schritt 7: Testen
|
||||
Nach dem Setup können Sie den Calendar Sync testen:
|
||||
|
||||
```bash
|
||||
# Vollständige Termindetails synchronisieren
|
||||
curl -X POST http://localhost:3000/api/flows/advoware_cal_sync \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full_content": true}'
|
||||
|
||||
# Nur "blocked" Termine synchronisieren (weniger Details)
|
||||
curl -X POST http://localhost:3000/api/flows/advoware_cal_sync \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full_content": false}'
|
||||
```
|
||||
|
||||
## Wichtige Hinweise
|
||||
- ✅ **Kein Browser nötig** - läuft komplett server-seitig
|
||||
- ✅ **Automatisch** - einmal setup, läuft für immer
|
||||
- ✅ **Sicher** - Service Accounts haben granulare Berechtigungen
|
||||
- ✅ **Skalierbar** - perfekt für Produktionsumgebungen
|
||||
|
||||
## Fehlerbehebung
|
||||
- **"service-account.json nicht gefunden"**: Überprüfen Sie den Pfad `/opt/motia-app/service-account.json`
|
||||
- **"Access denied"**: Überprüfen Sie die Berechtigungen des Service Accounts
|
||||
- **"API not enabled"**: Stellen Sie sicher, dass Calendar API aktiviert ist
|
||||
- **"Invalid credentials"**: Überprüfen Sie die service-account.json Datei
|
||||
|
||||
## Sicherheit
|
||||
- Halten Sie die `service-account.json` Datei sicher und versionieren Sie sie nicht
|
||||
- Verwenden Sie IAM-Rollen in GCP-Umgebungen statt JSON-Keys
|
||||
- Rotiere Service Account Keys regelmäßig
|
||||
194
bitbylaw/docs/INDEX.md
Normal file
194
bitbylaw/docs/INDEX.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Documentation Index
|
||||
|
||||
## Getting Started
|
||||
|
||||
**New to the project?** Start here:
|
||||
|
||||
1. [README.md](../README.md) - Project Overview & Quick Start
|
||||
2. [DEVELOPMENT.md](DEVELOPMENT.md) - Setup Development Environment
|
||||
3. [CONFIGURATION.md](CONFIGURATION.md) - Configure Environment Variables
|
||||
|
||||
## Core Documentation
|
||||
|
||||
### For Developers
|
||||
- **[DEVELOPMENT.md](DEVELOPMENT.md)** - Complete development guide
|
||||
- Setup, Coding Standards, Testing, Debugging
|
||||
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System design and architecture
|
||||
- Components, Data Flow, Event-Driven Design
|
||||
- **[API.md](API.md)** - HTTP Endpoints and Event Topics
|
||||
- Proxy API, Calendar Sync API, Webhook Endpoints
|
||||
|
||||
### For Operations
|
||||
- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Production deployment
|
||||
- Installation, systemd, nginx, Monitoring
|
||||
- **[CONFIGURATION.md](CONFIGURATION.md)** - Environment configuration
|
||||
- All environment variables, secrets management
|
||||
- **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** - Problem solving
|
||||
- Common issues, debugging, log analysis
|
||||
|
||||
### Special Topics
|
||||
- **[GOOGLE_SETUP.md](GOOGLE_SETUP.md)** - Google Service Account setup
|
||||
- Step-by-step guide for Calendar API access
|
||||
|
||||
## Component Documentation
|
||||
|
||||
### Steps (Business Logic)
|
||||
|
||||
**Advoware Proxy** ([Module README](../steps/advoware_proxy/README.md)):
|
||||
- [advoware_api_proxy_get_step.md](../steps/advoware_proxy/advoware_api_proxy_get_step.md)
|
||||
- [advoware_api_proxy_post_step.md](../steps/advoware_proxy/advoware_api_proxy_post_step.md)
|
||||
- [advoware_api_proxy_put_step.md](../steps/advoware_proxy/advoware_api_proxy_put_step.md)
|
||||
- [advoware_api_proxy_delete_step.md](../steps/advoware_proxy/advoware_api_proxy_delete_step.md)
|
||||
|
||||
**Calendar Sync** ([Module README](../steps/advoware_cal_sync/README.md)):
|
||||
- [calendar_sync_cron_step.md](../steps/advoware_cal_sync/calendar_sync_cron_step.md) - Daily trigger
|
||||
- [calendar_sync_api_step.md](../steps/advoware_cal_sync/calendar_sync_api_step.md) - Manual trigger
|
||||
- [calendar_sync_all_step.md](../steps/advoware_cal_sync/calendar_sync_all_step.md) - Employee cascade
|
||||
- [calendar_sync_event_step.md](../steps/advoware_cal_sync/calendar_sync_event_step.md) - Per-employee sync (complex)
|
||||
|
||||
**VMH Webhooks** ([Module README](../steps/vmh/README.md)):
|
||||
- [beteiligte_create_api_step.md](../steps/vmh/webhook/beteiligte_create_api_step.md) - Create webhook
|
||||
- [beteiligte_update_api_step.md](../steps/vmh/webhook/beteiligte_update_api_step.md) - Update webhook (similar)
|
||||
- [beteiligte_delete_api_step.md](../steps/vmh/webhook/beteiligte_delete_api_step.md) - Delete webhook (similar)
|
||||
- [beteiligte_sync_event_step.md](../steps/vmh/beteiligte_sync_event_step.md) - Sync handler (placeholder)
|
||||
|
||||
### Services
|
||||
|
||||
- [Advoware Service](../services/ADVOWARE_SERVICE.md) - API Client mit HMAC-512 Auth
|
||||
- [Advoware API Swagger](advoware/advoware_api_swagger.json) - Vollständige API-Dokumentation (JSON)
|
||||
|
||||
### Utility Scripts
|
||||
|
||||
- [Calendar Sync Scripts](../scripts/calendar_sync/README.md) - Wartung und Debugging
|
||||
- `delete_employee_locks.py` - Redis Lock Cleanup
|
||||
- `delete_all_calendars.py` - Google Calendar Reset
|
||||
|
||||
---
|
||||
|
||||
## Documentation Structure
|
||||
|
||||
```
|
||||
docs/
|
||||
├── INDEX.md # This file
|
||||
├── ARCHITECTURE.md # System design
|
||||
├── API.md # API reference
|
||||
├── CONFIGURATION.md # Configuration
|
||||
├── DEPLOYMENT.md # Deployment guide
|
||||
├── DEVELOPMENT.md # Development guide
|
||||
├── GOOGLE_SETUP.md # Google Calendar setup
|
||||
├── TROUBLESHOOTING.md # Debugging guide
|
||||
└── advoware/
|
||||
└── advoware_api_swagger.json # Advoware API spec
|
||||
|
||||
steps/{module}/
|
||||
├── README.md # Module overview
|
||||
└── {step_name}.md # Step documentation
|
||||
|
||||
services/
|
||||
└── {service_name}.md # Service documentation
|
||||
|
||||
scripts/{category}/
|
||||
├── README.md # Script documentation
|
||||
└── *.py # Utility scripts
|
||||
```
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
### YAML Frontmatter
|
||||
|
||||
Each step documentation includes metadata:
|
||||
|
||||
```yaml
|
||||
---
|
||||
type: step
|
||||
category: api|event|cron
|
||||
name: Step Name
|
||||
version: 1.0.0
|
||||
status: active|deprecated|placeholder
|
||||
tags: [tag1, tag2]
|
||||
dependencies: [...]
|
||||
emits: [...]
|
||||
subscribes: [...]
|
||||
---
|
||||
```
|
||||
|
||||
### Sections
|
||||
|
||||
Standard sections in step documentation:
|
||||
|
||||
1. **Zweck** - Purpose (one sentence)
|
||||
2. **Config** - Motia step configuration
|
||||
3. **Input** - Request structure, parameters
|
||||
4. **Output** - Response structure
|
||||
5. **Verhalten** - Behavior, logic flow
|
||||
6. **Abhängigkeiten** - Dependencies (services, Redis, APIs)
|
||||
7. **Testing** - Test examples
|
||||
8. **KI Guidance** - Tips for AI assistants
|
||||
|
||||
### Cross-References
|
||||
|
||||
- Use relative paths for links
|
||||
- Link related steps and services
|
||||
- Link to parent module READMEs
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Common Tasks
|
||||
|
||||
| Task | Documentation |
|
||||
|------|---------------|
|
||||
| Setup development environment | [DEVELOPMENT.md](DEVELOPMENT.md#setup) |
|
||||
| Configure environment variables | [CONFIGURATION.md](CONFIGURATION.md) |
|
||||
| Deploy to production | [DEPLOYMENT.md](DEPLOYMENT.md#installation-steps) |
|
||||
| Setup Google Calendar | [GOOGLE_SETUP.md](GOOGLE_SETUP.md) |
|
||||
| Debug service issues | [TROUBLESHOOTING.md](TROUBLESHOOTING.md#service-issues) |
|
||||
| Understand architecture | [ARCHITECTURE.md](ARCHITECTURE.md) |
|
||||
| Test API endpoints | [API.md](API.md) |
|
||||
|
||||
### Code Locations
|
||||
|
||||
| Component | Location | Documentation |
|
||||
|-----------|----------|---------------|
|
||||
| API Proxy Steps | `steps/advoware_proxy/` | [README](../steps/advoware_proxy/README.md) |
|
||||
| Calendar Sync Steps | `steps/advoware_cal_sync/` | [README](../steps/advoware_cal_sync/README.md) |
|
||||
| VMH Webhook Steps | `steps/vmh/` | [README](../steps/vmh/README.md) |
|
||||
| Advoware API Client | `services/advoware.py` | [DOC](../services/ADVOWARE_SERVICE.md) |
|
||||
| Configuration | `config.py` | [CONFIGURATION.md](CONFIGURATION.md) |
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
### Adding New Step Documentation
|
||||
|
||||
1. Create `{step_name}.md` next to `.py` file
|
||||
2. Use YAML frontmatter (see template)
|
||||
3. Follow standard sections
|
||||
4. Add to module README
|
||||
5. Add to this INDEX
|
||||
|
||||
### Updating Documentation
|
||||
|
||||
- Keep code and docs in sync
|
||||
- Update version history in step docs
|
||||
- Update INDEX when adding new files
|
||||
- Test all code examples
|
||||
|
||||
### Documentation Reviews
|
||||
|
||||
- Verify all links work
|
||||
- Check code examples execute correctly
|
||||
- Ensure terminology is consistent
|
||||
- Validate configuration examples
|
||||
|
||||
## External Resources
|
||||
|
||||
- [Motia Framework Docs](https://motia.dev) (if available)
|
||||
- [Advoware API](https://www2.advo-net.net:90/) (requires auth)
|
||||
- [Google Calendar API](https://developers.google.com/calendar)
|
||||
- [Redis Documentation](https://redis.io/documentation)
|
||||
|
||||
## Support
|
||||
|
||||
- **Questions**: Check TROUBLESHOOTING.md first
|
||||
- **Bugs**: Document in logs (`journalctl -u motia.service`)
|
||||
- **Features**: Propose in team discussions
|
||||
- **Urgent**: Check systemd logs and Redis state
|
||||
800
bitbylaw/docs/TROUBLESHOOTING.md
Normal file
800
bitbylaw/docs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,800 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
## Service Issues
|
||||
|
||||
### Service Won't Start
|
||||
|
||||
**Symptoms**: `systemctl start motia.service` schlägt fehl
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check service status
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# View detailed logs
|
||||
sudo journalctl -u motia.service -n 100 --no-pager
|
||||
|
||||
# Check for port conflicts
|
||||
sudo netstat -tlnp | grep 3000
|
||||
```
|
||||
|
||||
**Häufige Ursachen**:
|
||||
|
||||
1. **Port 3000 bereits belegt**:
|
||||
```bash
|
||||
# Find process
|
||||
sudo lsof -i :3000
|
||||
|
||||
# Kill process
|
||||
sudo kill -9 <PID>
|
||||
```
|
||||
|
||||
2. **Fehlende Dependencies**:
|
||||
```bash
|
||||
cd /opt/motia-app/bitbylaw
|
||||
sudo -u www-data npm install
|
||||
sudo -u www-data bash -c 'source python_modules/bin/activate && pip install -r requirements.txt'
|
||||
```
|
||||
|
||||
3. **Falsche Permissions**:
|
||||
```bash
|
||||
sudo chown -R www-data:www-data /opt/motia-app
|
||||
sudo chmod 600 /opt/motia-app/service-account.json
|
||||
```
|
||||
|
||||
4. **Environment Variables fehlen**:
|
||||
```bash
|
||||
# Check systemd environment
|
||||
sudo systemctl show motia.service -p Environment
|
||||
|
||||
# Verify required vars
|
||||
sudo systemctl cat motia.service | grep Environment
|
||||
```
|
||||
|
||||
### Service Keeps Crashing
|
||||
|
||||
**Symptoms**: Service startet, crashed aber nach kurzer Zeit
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Watch logs in real-time
|
||||
sudo journalctl -u motia.service -f
|
||||
|
||||
# Check for OOM (Out of Memory)
|
||||
dmesg | grep -i "out of memory"
|
||||
sudo grep -i "killed process" /var/log/syslog
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Memory Limit erhöhen**:
|
||||
```ini
|
||||
# In /etc/systemd/system/motia.service
|
||||
Environment=NODE_OPTIONS=--max-old-space-size=8192
|
||||
```
|
||||
|
||||
2. **Python Memory Leak**:
|
||||
```bash
|
||||
# Check memory usage
|
||||
ps aux | grep python
|
||||
|
||||
# Restart service periodically (workaround)
|
||||
# Add to crontab:
|
||||
0 3 * * * systemctl restart motia.service
|
||||
```
|
||||
|
||||
3. **Unhandled Exception**:
|
||||
```bash
|
||||
# Check error logs
|
||||
sudo journalctl -u motia.service -p err
|
||||
|
||||
# Add try-catch in problematic step
|
||||
```
|
||||
|
||||
## Redis Issues
|
||||
|
||||
### Redis Connection Failed
|
||||
|
||||
**Symptoms**: "Redis connection failed" in logs
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check Redis status
|
||||
sudo systemctl status redis-server
|
||||
|
||||
# Test connection
|
||||
redis-cli ping
|
||||
|
||||
# Check config
|
||||
redis-cli CONFIG GET bind
|
||||
redis-cli CONFIG GET port
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Redis not running**:
|
||||
```bash
|
||||
sudo systemctl start redis-server
|
||||
sudo systemctl enable redis-server
|
||||
```
|
||||
|
||||
2. **Wrong host/port**:
|
||||
```bash
|
||||
# Check environment
|
||||
echo $REDIS_HOST
|
||||
echo $REDIS_PORT
|
||||
|
||||
# Test connection
|
||||
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping
|
||||
```
|
||||
|
||||
3. **Permission denied**:
|
||||
```bash
|
||||
# Check Redis log
|
||||
sudo tail -f /var/log/redis/redis-server.log
|
||||
|
||||
# Fix permissions
|
||||
sudo chown redis:redis /var/lib/redis
|
||||
sudo chmod 750 /var/lib/redis
|
||||
```
|
||||
|
||||
### Redis Out of Memory
|
||||
|
||||
**Symptoms**: "OOM command not allowed" errors
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check memory usage
|
||||
redis-cli INFO memory
|
||||
|
||||
# Check maxmemory setting
|
||||
redis-cli CONFIG GET maxmemory
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Increase maxmemory**:
|
||||
```bash
|
||||
# In /etc/redis/redis.conf
|
||||
maxmemory 2gb
|
||||
maxmemory-policy allkeys-lru
|
||||
|
||||
sudo systemctl restart redis-server
|
||||
```
|
||||
|
||||
2. **Clear old data**:
|
||||
```bash
|
||||
# Clear cache (safe for Advoware tokens)
|
||||
redis-cli -n 1 FLUSHDB
|
||||
|
||||
# Clear calendar sync state
|
||||
redis-cli -n 2 FLUSHDB
|
||||
```
|
||||
|
||||
3. **Check for memory leaks**:
|
||||
```bash
|
||||
# Find large keys
|
||||
redis-cli --bigkeys
|
||||
|
||||
# Check specific key size
|
||||
redis-cli MEMORY USAGE <key>
|
||||
```
|
||||
|
||||
## Advoware API Issues
|
||||
|
||||
### Authentication Failed
|
||||
|
||||
**Symptoms**: "401 Unauthorized" oder "HMAC signature invalid"
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check logs for auth errors
|
||||
sudo journalctl -u motia.service | grep -i "auth\|token\|401"
|
||||
|
||||
# Test token fetch manually
|
||||
python3 << 'EOF'
|
||||
from services.advoware import AdvowareAPI
|
||||
api = AdvowareAPI()
|
||||
token = api.get_access_token(force_refresh=True)
|
||||
print(f"Token: {token[:20]}...")
|
||||
EOF
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Invalid API Key**:
|
||||
```bash
|
||||
# Verify API Key is Base64
|
||||
echo $ADVOWARE_API_KEY | base64 -d
|
||||
|
||||
# Re-encode if needed
|
||||
echo -n "raw_key" | base64
|
||||
```
|
||||
|
||||
2. **Wrong credentials**:
|
||||
```bash
|
||||
# Verify environment variables
|
||||
sudo systemctl show motia.service -p Environment | grep ADVOWARE
|
||||
|
||||
# Update in systemd service
|
||||
sudo nano /etc/systemd/system/motia.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart motia.service
|
||||
```
|
||||
|
||||
3. **Token expired**:
|
||||
```bash
|
||||
# Clear cached token
|
||||
redis-cli -n 1 DEL advoware_access_token advoware_token_timestamp
|
||||
|
||||
# Retry request (will fetch new token)
|
||||
```
|
||||
|
||||
### API Timeout
|
||||
|
||||
**Symptoms**: "Request timeout" oder "API call failed"
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check API response time
|
||||
time curl "http://localhost:3000/advoware/proxy?endpoint=employees"
|
||||
|
||||
# Check network connectivity
|
||||
ping www2.advo-net.net
|
||||
curl -I https://www2.advo-net.net:90/
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Increase timeout**:
|
||||
```bash
|
||||
# In environment
|
||||
export ADVOWARE_API_TIMEOUT_SECONDS=60
|
||||
|
||||
# Or in systemd service
|
||||
Environment=ADVOWARE_API_TIMEOUT_SECONDS=60
|
||||
```
|
||||
|
||||
2. **Network issues**:
|
||||
```bash
|
||||
# Check firewall
|
||||
sudo ufw status
|
||||
|
||||
# Test direct connection
|
||||
curl -v https://www2.advo-net.net:90/
|
||||
```
|
||||
|
||||
3. **Advoware API down**:
|
||||
```bash
|
||||
# Wait and retry
|
||||
# Implement exponential backoff in code
|
||||
```
|
||||
|
||||
## Google Calendar Issues
|
||||
|
||||
### Service Account Not Found
|
||||
|
||||
**Symptoms**: "service-account.json not found"
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check file exists
|
||||
ls -la /opt/motia-app/service-account.json
|
||||
|
||||
# Check permissions
|
||||
ls -la /opt/motia-app/service-account.json
|
||||
|
||||
# Check environment variable
|
||||
echo $GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **File missing**:
|
||||
```bash
|
||||
# Copy from backup
|
||||
sudo cp /backup/service-account.json /opt/motia-app/
|
||||
|
||||
# Set permissions
|
||||
sudo chmod 600 /opt/motia-app/service-account.json
|
||||
sudo chown www-data:www-data /opt/motia-app/service-account.json
|
||||
```
|
||||
|
||||
2. **Wrong path**:
|
||||
```bash
|
||||
# Update environment
|
||||
# In /etc/systemd/system/motia.service:
|
||||
Environment=GOOGLE_CALENDAR_SERVICE_ACCOUNT_PATH=/opt/motia-app/service-account.json
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart motia.service
|
||||
```
|
||||
|
||||
### Calendar API Rate Limit
|
||||
|
||||
**Symptoms**: "403 Rate limit exceeded" oder "429 Too Many Requests"
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check rate limiting in logs
|
||||
sudo journalctl -u motia.service | grep -i "rate\|403\|429"
|
||||
|
||||
# Check Redis rate limit tokens
|
||||
redis-cli -n 2 GET google_calendar_api_tokens
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Wait for rate limit reset**:
|
||||
```bash
|
||||
# Rate limit resets every minute
|
||||
# Wait 60 seconds and retry
|
||||
```
|
||||
|
||||
2. **Adjust rate limit settings**:
|
||||
```python
|
||||
# In calendar_sync_event_step.py
|
||||
MAX_TOKENS = 7 # Decrease if hitting limits
|
||||
REFILL_RATE_PER_MS = 7 / 1000
|
||||
```
|
||||
|
||||
3. **Request quota increase**:
|
||||
- Go to Google Cloud Console
|
||||
- Navigate to "APIs & Services" → "Quotas"
|
||||
- Request increase for Calendar API
|
||||
|
||||
### Calendar Access Denied
|
||||
|
||||
**Symptoms**: "Access denied" oder "Insufficient permissions"
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check service account email
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
with open('/opt/motia-app/service-account.json') as f:
|
||||
data = json.load(f)
|
||||
print(f"Service Account: {data['client_email']}")
|
||||
EOF
|
||||
|
||||
# Test API access
|
||||
python3 << 'EOF'
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
creds = service_account.Credentials.from_service_account_file(
|
||||
'/opt/motia-app/service-account.json',
|
||||
scopes=['https://www.googleapis.com/auth/calendar']
|
||||
)
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
result = service.calendarList().list().execute()
|
||||
print(f"Calendars: {len(result.get('items', []))}")
|
||||
EOF
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Calendar not shared**:
|
||||
```bash
|
||||
# Share calendar with service account email
|
||||
# In Google Calendar UI: Settings → Share → Add service account email
|
||||
```
|
||||
|
||||
2. **Wrong scopes**:
|
||||
```bash
|
||||
# Verify scopes in code
|
||||
# Should be: https://www.googleapis.com/auth/calendar
|
||||
```
|
||||
|
||||
3. **Domain-wide delegation**:
|
||||
```bash
|
||||
# For G Suite, enable domain-wide delegation
|
||||
# See GOOGLE_SETUP_README.md
|
||||
```
|
||||
|
||||
## Calendar Sync Issues
|
||||
|
||||
### Sync Not Running
|
||||
|
||||
**Symptoms**: Keine Calendar-Updates, keine Sync-Logs
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check if cron is triggering
|
||||
sudo journalctl -u motia.service | grep -i "calendar_sync_cron"
|
||||
|
||||
# Manually trigger sync
|
||||
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full_content": true}'
|
||||
|
||||
# Check for locks
|
||||
redis-cli -n 1 KEYS "calendar_sync:lock:*"
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Cron not configured**:
|
||||
```python
|
||||
# Verify calendar_sync_cron_step.py has correct schedule
|
||||
config = {
|
||||
'schedule': '0 2 * * *', # Daily at 2 AM
|
||||
}
|
||||
```
|
||||
|
||||
2. **Lock stuck**:
|
||||
```bash
|
||||
# Clear all locks
|
||||
python /opt/motia-app/bitbylaw/delete_employee_locks.py
|
||||
|
||||
# Or manually
|
||||
redis-cli -n 1 DEL calendar_sync:lock:SB
|
||||
```
|
||||
|
||||
3. **Errors in sync**:
|
||||
```bash
|
||||
# Check error logs
|
||||
sudo journalctl -u motia.service -p err | grep calendar
|
||||
```
|
||||
|
||||
### Duplicate Events
|
||||
|
||||
**Symptoms**: Events erscheinen mehrfach in Google Calendar
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check for concurrent syncs
|
||||
redis-cli -n 1 KEYS "calendar_sync:lock:*"
|
||||
|
||||
# Check logs for duplicate processing
|
||||
sudo journalctl -u motia.service | grep -i "duplicate\|already exists"
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Locking not working**:
|
||||
```bash
|
||||
# Verify Redis lock TTL
|
||||
redis-cli -n 1 TTL calendar_sync:lock:SB
|
||||
|
||||
# Should return positive number if locked
|
||||
```
|
||||
|
||||
2. **Manual cleanup**:
|
||||
```bash
|
||||
# Delete duplicates in Google Calendar UI
|
||||
# Or use cleanup script (if available)
|
||||
```
|
||||
|
||||
3. **Improve deduplication logic**:
|
||||
```python
|
||||
# In calendar_sync_event_step.py
|
||||
# Add better event matching logic
|
||||
```
|
||||
|
||||
### Events Not Syncing
|
||||
|
||||
**Symptoms**: Advoware events nicht in Google Calendar
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check specific employee
|
||||
curl -X POST "http://localhost:3000/advoware/calendar/sync" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"kuerzel": "SB", "full_content": true}'
|
||||
|
||||
# Check logs for that employee
|
||||
sudo journalctl -u motia.service | grep "SB"
|
||||
|
||||
# Check if calendar exists
|
||||
python3 << 'EOF'
|
||||
from google.oauth2 import service_account
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
creds = service_account.Credentials.from_service_account_file(
|
||||
'/opt/motia-app/service-account.json',
|
||||
scopes=['https://www.googleapis.com/auth/calendar']
|
||||
)
|
||||
service = build('calendar', 'v3', credentials=creds)
|
||||
result = service.calendarList().list().execute()
|
||||
for cal in result.get('items', []):
|
||||
if 'AW-SB' in cal['summary']:
|
||||
print(f"Found: {cal['summary']} - {cal['id']}")
|
||||
EOF
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Calendar doesn't exist**:
|
||||
```bash
|
||||
# Will be auto-created on first sync
|
||||
# Force sync to trigger creation
|
||||
```
|
||||
|
||||
2. **Date range mismatch**:
|
||||
```python
|
||||
# Check FETCH_FROM and FETCH_TO in calendar_sync_event_step.py
|
||||
# Default: Previous year to 9 years ahead
|
||||
```
|
||||
|
||||
3. **Write protection enabled**:
|
||||
```bash
|
||||
# Check environment
|
||||
echo $ADVOWARE_WRITE_PROTECTION
|
||||
|
||||
# Should be "false" for two-way sync
|
||||
```
|
||||
|
||||
## Webhook Issues
|
||||
|
||||
### Webhooks Not Received
|
||||
|
||||
**Symptoms**: EspoCRM sendet Webhooks, aber keine Verarbeitung
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check if endpoint reachable
|
||||
curl -X POST "http://localhost:3000/vmh/webhook/beteiligte/create" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '[{"id": "test-123"}]'
|
||||
|
||||
# Check firewall
|
||||
sudo ufw status
|
||||
|
||||
# Check nginx logs (if using reverse proxy)
|
||||
sudo tail -f /var/log/nginx/motia-access.log
|
||||
sudo tail -f /var/log/nginx/motia-error.log
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Firewall blocking**:
|
||||
```bash
|
||||
# Allow port (if direct access)
|
||||
sudo ufw allow 3000/tcp
|
||||
|
||||
# Or use reverse proxy (recommended)
|
||||
```
|
||||
|
||||
2. **Wrong URL in EspoCRM**:
|
||||
```bash
|
||||
# Verify URL in EspoCRM webhook configuration
|
||||
# Should be: https://your-domain.com/vmh/webhook/beteiligte/create
|
||||
```
|
||||
|
||||
3. **SSL certificate issues**:
|
||||
```bash
|
||||
# Check certificate
|
||||
openssl s_client -connect your-domain.com:443
|
||||
|
||||
# Renew certificate
|
||||
sudo certbot renew
|
||||
```
|
||||
|
||||
### Webhook Deduplication Not Working
|
||||
|
||||
**Symptoms**: Mehrfache Verarbeitung derselben Webhooks
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check Redis dedup sets
|
||||
redis-cli -n 1 SMEMBERS vmh:beteiligte:create_pending
|
||||
redis-cli -n 1 SMEMBERS vmh:beteiligte:update_pending
|
||||
redis-cli -n 1 SMEMBERS vmh:beteiligte:delete_pending
|
||||
|
||||
# Check for concurrent webhook processing
|
||||
sudo journalctl -u motia.service | grep "Webhook.*received"
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Redis SET not working**:
|
||||
```bash
|
||||
# Test Redis SET operations
|
||||
redis-cli -n 1 SADD test_set "value1"
|
||||
redis-cli -n 1 SMEMBERS test_set
|
||||
redis-cli -n 1 DEL test_set
|
||||
```
|
||||
|
||||
2. **Clear dedup sets**:
|
||||
```bash
|
||||
# If corrupted
|
||||
redis-cli -n 1 DEL vmh:beteiligte:create_pending
|
||||
redis-cli -n 1 DEL vmh:beteiligte:update_pending
|
||||
redis-cli -n 1 DEL vmh:beteiligte:delete_pending
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check CPU usage
|
||||
top -p $(pgrep -f "motia start")
|
||||
|
||||
# Profile with Node.js
|
||||
# Already enabled with --inspect flag
|
||||
# Connect to chrome://inspect
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Too many parallel syncs**:
|
||||
```bash
|
||||
# Reduce concurrent syncs
|
||||
# Adjust DEBUG_KUERZEL to process fewer employees
|
||||
```
|
||||
|
||||
2. **Infinite loop**:
|
||||
```bash
|
||||
# Check logs for repeated patterns
|
||||
sudo journalctl -u motia.service | tail -n 1000 | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
### High Memory Usage
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Check memory
|
||||
ps aux | grep motia | awk '{print $6}'
|
||||
|
||||
# Heap snapshot (if enabled)
|
||||
kill -SIGUSR2 $(pgrep -f "motia start")
|
||||
# Snapshot saved to current directory
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Increase memory limit**:
|
||||
```ini
|
||||
# In systemd service
|
||||
Environment=NODE_OPTIONS=--max-old-space-size=16384
|
||||
```
|
||||
|
||||
2. **Memory leak**:
|
||||
```bash
|
||||
# Restart service periodically
|
||||
# Add to crontab:
|
||||
0 3 * * * systemctl restart motia.service
|
||||
```
|
||||
|
||||
### Slow API Responses
|
||||
|
||||
**Diagnose**:
|
||||
```bash
|
||||
# Measure response time
|
||||
time curl "http://localhost:3000/advoware/proxy?endpoint=employees"
|
||||
|
||||
# Check for database/Redis latency
|
||||
redis-cli --latency
|
||||
```
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Redis slow**:
|
||||
```bash
|
||||
# Check slow log
|
||||
redis-cli SLOWLOG GET 10
|
||||
|
||||
# Optimize Redis
|
||||
redis-cli CONFIG SET tcp-backlog 511
|
||||
```
|
||||
|
||||
2. **Advoware API slow**:
|
||||
```bash
|
||||
# Increase timeout
|
||||
export ADVOWARE_API_TIMEOUT_SECONDS=60
|
||||
|
||||
# Add caching layer
|
||||
```
|
||||
|
||||
## Debugging Tools
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
```bash
|
||||
# Set in systemd service
|
||||
Environment=MOTIA_LOG_LEVEL=debug
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart motia.service
|
||||
```
|
||||
|
||||
### Redis Debugging
|
||||
|
||||
```bash
|
||||
# Connect to Redis
|
||||
redis-cli
|
||||
|
||||
# Monitor all commands
|
||||
MONITOR
|
||||
|
||||
# Slow log
|
||||
SLOWLOG GET 10
|
||||
|
||||
# Info
|
||||
INFO all
|
||||
```
|
||||
|
||||
### Python Debugging
|
||||
|
||||
```python
|
||||
# Add to step code
|
||||
import pdb; pdb.set_trace()
|
||||
|
||||
# Or use logging
|
||||
context.logger.debug(f"Variable value: {variable}")
|
||||
```
|
||||
|
||||
### Node.js Debugging
|
||||
|
||||
```bash
|
||||
# Connect to inspector
|
||||
# Chrome DevTools: chrome://inspect
|
||||
# VSCode: Attach to Process
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Check Logs First
|
||||
|
||||
```bash
|
||||
# Last 100 lines
|
||||
sudo journalctl -u motia.service -n 100
|
||||
|
||||
# Errors only
|
||||
sudo journalctl -u motia.service -p err
|
||||
|
||||
# Specific time range
|
||||
sudo journalctl -u motia.service --since "1 hour ago"
|
||||
```
|
||||
|
||||
### Common Log Patterns
|
||||
|
||||
**Success**:
|
||||
```
|
||||
[INFO] Calendar sync completed for SB
|
||||
[INFO] VMH Webhook received
|
||||
```
|
||||
|
||||
**Warning**:
|
||||
```
|
||||
[WARNING] Rate limit approaching
|
||||
[WARNING] Lock already exists for SB
|
||||
```
|
||||
|
||||
**Error**:
|
||||
```
|
||||
[ERROR] Redis connection failed
|
||||
[ERROR] API call failed: 401 Unauthorized
|
||||
[ERROR] Unexpected error: ...
|
||||
```
|
||||
|
||||
### Collect Debug Information
|
||||
|
||||
```bash
|
||||
# System info
|
||||
uname -a
|
||||
node --version
|
||||
python3 --version
|
||||
|
||||
# Service status
|
||||
sudo systemctl status motia.service
|
||||
|
||||
# Recent logs
|
||||
sudo journalctl -u motia.service -n 200 > motia-logs.txt
|
||||
|
||||
# Redis info
|
||||
redis-cli INFO > redis-info.txt
|
||||
|
||||
# Configuration (redact secrets!)
|
||||
sudo systemctl show motia.service -p Environment > env.txt
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Architecture](ARCHITECTURE.md)
|
||||
- [Configuration](CONFIGURATION.md)
|
||||
- [Deployment](DEPLOYMENT.md)
|
||||
- [Development Guide](DEVELOPMENT.md)
|
||||
Reference in New Issue
Block a user