docs(diagrams): add component-level architecture diagrams
- DLP Endpoint Monitoring diagram with data flows - Browser Domains Monitoring with categorization - WebUI Russian Localization patches structure - DLP Events Aggregation pipeline - Prometheus Metrics Exporter architecture - System overview with all components and connections Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
db1998c428
commit
d8320799e6
@@ -0,0 +1,305 @@
|
||||
# Browser Domains Monitoring - Компонентная диаграмма
|
||||
|
||||
## Обзор
|
||||
Мониторинг посещаемых доменов в браузерах с категоризацией и проверкой DLP правил.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Browser Domains Collector"
|
||||
A[Main Loop]
|
||||
B[Window Detection]
|
||||
C[URL Extraction]
|
||||
D[Domain Parsing]
|
||||
E[Web Categorization]
|
||||
F[DLP Rule Check]
|
||||
G[Incident Handler]
|
||||
H[Event Logger]
|
||||
I[Screenshot Capture]
|
||||
end
|
||||
|
||||
subgraph "External Dependencies"
|
||||
J[Windows API]
|
||||
K[Web Category API]
|
||||
L[DLP Policy]
|
||||
M[ActivityWatch API]
|
||||
N[Custom Category Rules]
|
||||
end
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
C --> D
|
||||
D --> E
|
||||
D --> F
|
||||
E --> F
|
||||
F --> G
|
||||
G --> H
|
||||
G --> I
|
||||
|
||||
B --> J
|
||||
C --> J
|
||||
E --> K
|
||||
F --> L
|
||||
H --> M
|
||||
E --> N
|
||||
F --> N
|
||||
|
||||
style B fill:#ff6b6b
|
||||
style E fill:#4ecdc4
|
||||
style F fill:#4ecdc4
|
||||
style G fill:#ffe66d
|
||||
```
|
||||
|
||||
## Потоки данных
|
||||
|
||||
### Domain Detection Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Browser as Browser Window
|
||||
participant Windows as Windows API
|
||||
participant Collector as Browser Collector
|
||||
participant Parser as Domain Parser
|
||||
participant Category as Category API
|
||||
participant DLP as DLP Engine
|
||||
participant AW as ActivityWatch
|
||||
|
||||
User->>Browser: Navigate to website
|
||||
Browser->>Windows: Window title change
|
||||
Windows->>Collector: Window event
|
||||
Collector->>Collector: Check if browser
|
||||
Collector->>Collector: Extract URL from window
|
||||
Collector->>Parser: Parse domain
|
||||
Parser->>Collector: Return domain
|
||||
Collector->>Category: Get web category
|
||||
Category->>Collector: Return category
|
||||
Collector->>DLP: Check DLP rules
|
||||
alt Violation
|
||||
DLP->>Collector: Trigger incident
|
||||
Collector->>Collector: Capture screenshot
|
||||
Collector->>AW: Send DLP incident
|
||||
else Normal
|
||||
Collector->>AW: Send browsing event
|
||||
end
|
||||
```
|
||||
|
||||
### Domain Categorization Flow
|
||||
```mermaid
|
||||
graph LR
|
||||
A[URL] --> B[Extract Domain]
|
||||
B --> C{Custom Rules?}
|
||||
C -->|Yes| D[Apply Custom Category]
|
||||
C -->|No| E[Call Category API]
|
||||
D --> F[Return Category]
|
||||
E --> F
|
||||
F --> G[Cache Result]
|
||||
G --> H[Use in DLP Check]
|
||||
```
|
||||
|
||||
## Ключевые функции
|
||||
|
||||
### Обнаружение и извлечение
|
||||
- `browser_domains_native_collector_get_foregroundwindowcontext()` - контекст активного окна
|
||||
- `browser_domains_native_collector_get_browserurlfromwindow()` - извлечение URL из окна
|
||||
- `browser_domains_native_collector_get_hostfromurl()` - извлечение домена из URL
|
||||
- `browser_domains_native_collector_get_rootdomain()` - получение корневого домена
|
||||
|
||||
### Категоризация
|
||||
- `browser_domains_native_collector_get_webcategory()` - определение категории сайта
|
||||
- `browser_domains_native_collector_load_customcategoryrules()` - загрузка кастомных правил
|
||||
- `browser_domains_native_collector_test_domainlistmatch()` - проверка по спискам доменов
|
||||
|
||||
### DLP проверка
|
||||
- `browser_domains_native_collector_get_dlpdecision()` - решение DLP
|
||||
- `browser_domains_native_collector_test_dlprulematch()` - проверка DLP правила
|
||||
- `browser_domains_native_collector_test_dlpruletimewindow()` - проверка временного окна
|
||||
- `browser_domains_native_collector_should_emitincident()` - решение о создании инцидента
|
||||
|
||||
### Логирование
|
||||
- `browser_domains_native_collector_write_collectorlog()` - лог коллектора
|
||||
- `browser_domains_native_collector_write_dlpincidentlog()` - лог DLP инцидента
|
||||
- `browser_domains_native_collector_send_heartbeat()` - heartbeat
|
||||
- `browser_domains_native_collector_send_categoryheartbeat()` - heartbeat категорий
|
||||
|
||||
### Утилиты
|
||||
- `browser_domains_native_collector_convertto_normalizedurl()` - нормализация URL
|
||||
- `browser_domains_native_collector_test_domainmatch()` - проверка совпадения домена
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Custom Category Rules
|
||||
```json
|
||||
{
|
||||
"custom_categories": {
|
||||
"social_media": [
|
||||
"*.facebook.com",
|
||||
"*.twitter.com",
|
||||
"*.instagram.com"
|
||||
],
|
||||
"news": [
|
||||
"*.news.com",
|
||||
"*.media.com"
|
||||
],
|
||||
"blocked": [
|
||||
"*.malware.com",
|
||||
"*.phishing.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DLP Domain Rules
|
||||
```json
|
||||
{
|
||||
"domain_rules": [
|
||||
{
|
||||
"pattern": "*gambling*",
|
||||
"category": "gambling",
|
||||
"action": "alert",
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"pattern": "*adult*",
|
||||
"category": "adult",
|
||||
"action": "block",
|
||||
"severity": "high"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Deployment Config
|
||||
```json
|
||||
{
|
||||
"aw_server_url": "http://aw-server:5600",
|
||||
"bucket_prefix": "aw-watcher-browser-domains",
|
||||
"category_api_enabled": true,
|
||||
"custom_categories_enabled": true,
|
||||
"screenshot_on_incident": true,
|
||||
"heartbeat_interval": 60
|
||||
}
|
||||
```
|
||||
|
||||
## События
|
||||
|
||||
### Browsing Event
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"type": "browsing",
|
||||
"data": {
|
||||
"url": "https://www.example.com/page",
|
||||
"domain": "example.com",
|
||||
"root_domain": "example.com",
|
||||
"category": "technology",
|
||||
"browser": "chrome.exe",
|
||||
"title": "Example Page Title",
|
||||
"user": "user1",
|
||||
"host": "WORKSTATION01"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DLP Incident Event
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"type": "dlp_incident",
|
||||
"source": "browser_domain",
|
||||
"rule_id": "blocked_domains",
|
||||
"severity": "high",
|
||||
"data": {
|
||||
"url": "https://blocked.com",
|
||||
"domain": "blocked.com",
|
||||
"category": "malicious",
|
||||
"user": "user1",
|
||||
"host": "WORKSTATION01",
|
||||
"screenshot": "path/to/screenshot.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Category Heartbeat
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"type": "category_heartbeat",
|
||||
"data": {
|
||||
"domains_seen": 150,
|
||||
"categories": {
|
||||
"technology": 50,
|
||||
"news": 30,
|
||||
"social_media": 40,
|
||||
"other": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Поддерживаемые браузеры
|
||||
|
||||
- Google Chrome
|
||||
- Mozilla Firefox
|
||||
- Microsoft Edge
|
||||
- Opera
|
||||
- Яндекс.Браузер
|
||||
|
||||
## Зависимости
|
||||
|
||||
### Windows API
|
||||
- Window enumeration
|
||||
- Window title extraction
|
||||
- Process information
|
||||
|
||||
### Внешние сервисы
|
||||
- Web categorization API (опционально)
|
||||
- ActivityWatch HTTP API
|
||||
|
||||
## Алгоритм категоризации
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Domain extracted] --> B{Custom rule match?}
|
||||
B -->|Yes| C[Return custom category]
|
||||
B -->|No| D{Cache hit?}
|
||||
D -->|Yes| E[Return cached category]
|
||||
D -->|No| F[Call category API]
|
||||
F --> G{API success?}
|
||||
G -->|Yes| H[Cache result]
|
||||
G -->|No| I[Return 'uncategorized']
|
||||
H --> J[Return category]
|
||||
I --> J
|
||||
```
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### Метрики
|
||||
- Количество уникальных доменов
|
||||
- Распределение по категориям
|
||||
- Частота DLP инцидентов
|
||||
- Кэш hit rate для категорий
|
||||
|
||||
### Алерты
|
||||
- Коллектор не активен > 5 минут
|
||||
- Высокий процент blocked категорий
|
||||
- Ошибки category API
|
||||
- Много неизвестных категорий
|
||||
|
||||
## Производительность
|
||||
|
||||
### Оптимизации
|
||||
- Кэширование категорий доменов
|
||||
- Batch запросы к category API
|
||||
- Debouncing быстрых переходов
|
||||
- Асинхронная отправка событий
|
||||
|
||||
### Настройки производительности
|
||||
```json
|
||||
{
|
||||
"cache_ttl": 86400,
|
||||
"batch_size": 50,
|
||||
"debounce_ms": 1000,
|
||||
"max_events_per_minute": 100
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,375 @@
|
||||
# DLP Events Aggregation - Компонентная диаграмма
|
||||
|
||||
## Обзор
|
||||
Система агрегации DLP событий из ActivityWatch buckets в PostgreSQL для аналитики и отчетности.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Aggregation Pipeline"
|
||||
A[Main Orchestrator]
|
||||
B[Bucket Discovery]
|
||||
C[Event Fetcher]
|
||||
D[Event Parser]
|
||||
E[Normalizer]
|
||||
F[PostgreSQL Writer]
|
||||
G[State Manager]
|
||||
end
|
||||
|
||||
subgraph "Data Sources"
|
||||
H[ActivityWatch API]
|
||||
I[SQLite Buckets]
|
||||
J[PostgreSQL Database]
|
||||
end
|
||||
|
||||
subgraph "Output"
|
||||
K[Aggregated Events]
|
||||
L[Statistics]
|
||||
M[Reports]
|
||||
end
|
||||
|
||||
A --> B
|
||||
A --> C
|
||||
A --> G
|
||||
B --> H
|
||||
C --> H
|
||||
C --> I
|
||||
D --> C
|
||||
E --> D
|
||||
F --> E
|
||||
F --> J
|
||||
G --> J
|
||||
|
||||
F --> K
|
||||
F --> L
|
||||
F --> M
|
||||
|
||||
style A fill:#4ecdc4
|
||||
style F fill:#ff6b6b
|
||||
style G fill:#ffe66d
|
||||
```
|
||||
|
||||
## Потоки данных
|
||||
|
||||
### Aggregation Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Orchestrator as Main Orchestrator
|
||||
participant State as State Manager
|
||||
participant AW as ActivityWatch API
|
||||
participant Fetcher as Event Fetcher
|
||||
participant Parser as Event Parser
|
||||
participant PG as PostgreSQL
|
||||
|
||||
Orchestrator->>State: Load last sync state
|
||||
State->>Orchestrator: Return last timestamp
|
||||
Orchestrator->>AW: List buckets
|
||||
AW->>Orchestrator: Return bucket list
|
||||
loop For each DLP bucket
|
||||
Orchestrator->>AW: Get events since last sync
|
||||
AW->>Fetcher: Return events
|
||||
Fetcher->>Parser: Parse event structure
|
||||
Parser->>Parser: Extract DLP data
|
||||
Parser->>Orchestrator: Return parsed events
|
||||
Orchestrator->>PG: Insert batch of events
|
||||
PG->>Orchestrator: Confirm insert
|
||||
end
|
||||
Orchestrator->>State: Save new sync state
|
||||
State->>PG: Persist state
|
||||
```
|
||||
|
||||
### Event Processing Pipeline
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Raw Event] --> B[Parse JSON]
|
||||
B --> C[Extract DLP Fields]
|
||||
C --> D[Normalize Timestamp]
|
||||
D --> E[Enrich with User/Host]
|
||||
E --> F[Validate Data]
|
||||
F --> G[Batch Prepare]
|
||||
G --> H[PostgreSQL Insert]
|
||||
```
|
||||
|
||||
## Ключевые функции
|
||||
|
||||
### Оркестрация
|
||||
- `aggregate_dlp_events_main()` - главная функция агрегации
|
||||
- `aggregate_dlp_events_list_buckets()` - получение списка buckets
|
||||
- `aggregate_dlp_events_get_start_time()` - определение начала периода
|
||||
|
||||
### Получение событий
|
||||
- `aggregate_dlp_events_fetch_bucket_events()` - получение событий из bucket
|
||||
- `aggregate_dlp_events_bucket_stream_type()` - определение типа потока
|
||||
- `aggregate_dlp_events_aw_get_json()` - HTTP запрос к AW API
|
||||
|
||||
### Парсинг
|
||||
- `aggregate_dlp_events_awevent()` - парсинг события AW
|
||||
- `aggregate_dlp_events_parse_timestamp()` - парсинг timestamp
|
||||
- `aggregate_dlp_events_parse_prefixes()` - парсинг префиксов bucket
|
||||
- `aggregate_dlp_events_event_row()` - формирование строки события
|
||||
|
||||
### Нормализация
|
||||
- `aggregate_dlp_events_normalize_base_url()` - нормализация URL AW API
|
||||
- `aggregate_dlp_events_format_aw_timestamp()` - форматирование timestamp
|
||||
- `aggregate_dlp_events_bool_as_int()` - конвертация boolean в int
|
||||
- `aggregate_dlp_events_first_string()` - получение первой строки
|
||||
|
||||
### Батчинг
|
||||
- `aggregate_dlp_events_bucket()` - обработка bucket
|
||||
- `aggregate_dlp_events_build_events_path()` - построение пути к событиям
|
||||
- `aggregate_dlp_events_build_parser()` - создание парсера
|
||||
|
||||
### Соединения
|
||||
- `aggregate_dlp_events_connect_sqlite()` - соединение с SQLite
|
||||
- `aggregate_dlp_events_psycopgconnection()` - соединение с PostgreSQL
|
||||
- `aggregate_dlp_events_psycopgconnection_cursor()` - курсор PostgreSQL
|
||||
- `aggregate_dlp_events_psycopgconnection_commit()` - коммит транзакции
|
||||
|
||||
### Запись
|
||||
- `aggregate_dlp_events_insert_events()` - вставка событий
|
||||
- `aggregate_dlp_events_insert_postgres_events()` - вставка в PostgreSQL
|
||||
- `aggregate_dlp_events_select_buckets()` - выборка buckets
|
||||
- `aggregate_dlp_events_ensure_schema()` - обеспечение схемы БД
|
||||
|
||||
### Управление состоянием
|
||||
- `aggregate_dlp_events_load_state()` - загрузка состояния
|
||||
- `aggregate_dlp_events_save_state()` - сохранение состояния
|
||||
- `aggregate_dlp_events_event_key()` - ключ события
|
||||
|
||||
### Утилиты
|
||||
- `aggregate_dlp_events_utc_now()` - текущее UTC время
|
||||
- `aggregate_dlp_events_ensure_postgres_schema()` - схема PostgreSQL
|
||||
|
||||
## Схема базы данных
|
||||
|
||||
### Таблица dlp_events
|
||||
```sql
|
||||
CREATE TABLE dlp_events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
event_type VARCHAR(50) NOT NULL,
|
||||
source VARCHAR(50) NOT NULL,
|
||||
rule_id VARCHAR(100),
|
||||
severity VARCHAR(20),
|
||||
user_id VARCHAR(100),
|
||||
host_id VARCHAR(100),
|
||||
data JSONB,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
INDEX idx_timestamp (timestamp),
|
||||
INDEX idx_user (user_id),
|
||||
INDEX idx_host (host_id),
|
||||
INDEX idx_severity (severity),
|
||||
INDEX idx_type (event_type)
|
||||
);
|
||||
```
|
||||
|
||||
### Таблица aggregation_state
|
||||
```sql
|
||||
CREATE TABLE aggregation_state (
|
||||
id SERIAL PRIMARY KEY,
|
||||
bucket_id VARCHAR(255) UNIQUE NOT NULL,
|
||||
last_synced_timestamp TIMESTAMP,
|
||||
last_synced_at TIMESTAMP DEFAULT NOW(),
|
||||
events_processed INTEGER DEFAULT 0
|
||||
);
|
||||
```
|
||||
|
||||
### Таблица dlp_statistics
|
||||
```sql
|
||||
CREATE TABLE dlp_statistics (
|
||||
id SERIAL PRIMARY KEY,
|
||||
date DATE NOT NULL,
|
||||
user_id VARCHAR(100),
|
||||
host_id VARCHAR(100),
|
||||
event_type VARCHAR(50),
|
||||
severity VARCHAR(20),
|
||||
incident_count INTEGER DEFAULT 0,
|
||||
UNIQUE(date, user_id, host_id, event_type, severity)
|
||||
);
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Config File
|
||||
```json
|
||||
{
|
||||
"aw_base_url": "http://aw-server:5600",
|
||||
"postgres_url": "postgresql://user:pass@localhost:5432/activitywatch",
|
||||
"batch_size": 1000,
|
||||
"sync_interval_minutes": 5,
|
||||
"bucket_prefixes": [
|
||||
"aw-watcher-dlp-endpoint",
|
||||
"aw-watcher-browser-domains",
|
||||
"aw-watcher-email-outbound"
|
||||
],
|
||||
"retention_days": 90
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
```bash
|
||||
AW_BASE_URL=http://aw-server:5600
|
||||
POSTGRES_URL=postgresql://aw:password@localhost:5432/activitywatch
|
||||
BATCH_SIZE=1000
|
||||
SYNC_INTERVAL=300
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
## События
|
||||
|
||||
### Input Event (from ActivityWatch)
|
||||
```json
|
||||
{
|
||||
"id": "event_id",
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"duration": 60.0,
|
||||
"data": {
|
||||
"type": "dlp_incident",
|
||||
"source": "clipboard",
|
||||
"rule_id": "credit_card_pattern",
|
||||
"severity": "high",
|
||||
"user": "user1",
|
||||
"host": "WORKSTATION01",
|
||||
"matched_text": "****-****-****-1234"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Output Event (in PostgreSQL)
|
||||
```sql
|
||||
INSERT INTO dlp_events (
|
||||
timestamp, event_type, source, rule_id,
|
||||
severity, user_id, host_id, data
|
||||
) VALUES (
|
||||
'2024-01-01 12:00:00',
|
||||
'dlp_incident',
|
||||
'clipboard',
|
||||
'credit_card_pattern',
|
||||
'high',
|
||||
'user1',
|
||||
'WORKSTATION01',
|
||||
'{"matched_text": "****-****-****-1234"}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
### Оптимизации
|
||||
- Batch вставки (по 1000 событий)
|
||||
- Connection pooling к PostgreSQL
|
||||
- Асинхронная обработка
|
||||
- Индексы на частых запросах
|
||||
|
||||
### Метрики производительности
|
||||
```python
|
||||
performance_metrics = {
|
||||
"events_per_second": 100,
|
||||
"batch_insert_time_ms": 50,
|
||||
"api_latency_ms": 20,
|
||||
"postgres_write_latency_ms": 30
|
||||
}
|
||||
```
|
||||
|
||||
### Мониторинг
|
||||
- Время обработки batch
|
||||
- Размер очереди событий
|
||||
- Ошибки соединения с PostgreSQL
|
||||
- Latency API запросов
|
||||
|
||||
## Обработка ошибок
|
||||
|
||||
### Retry Strategy
|
||||
```python
|
||||
retry_config = {
|
||||
"max_retries": 3,
|
||||
"backoff_seconds": [1, 5, 15],
|
||||
"retry_on": [
|
||||
"ConnectionError",
|
||||
"TimeoutError",
|
||||
"DatabaseError"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Dead Letter Queue
|
||||
```sql
|
||||
CREATE TABLE dlp_events_failed (
|
||||
id SERIAL PRIMARY KEY,
|
||||
raw_event JSONB,
|
||||
error_message TEXT,
|
||||
failed_at TIMESTAMP DEFAULT NOW(),
|
||||
retry_count INTEGER DEFAULT 0
|
||||
);
|
||||
```
|
||||
|
||||
## Планирование
|
||||
|
||||
### Cron Job
|
||||
```cron
|
||||
*/5 * * * * /usr/bin/python3 /path/to/aggregate_dlp_events.py
|
||||
```
|
||||
|
||||
### Systemd Service
|
||||
```ini
|
||||
[Unit]
|
||||
Description=ActivityWatch DLP Aggregation
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=aw-aggregator
|
||||
ExecStart=/usr/bin/python3 /path/to/aggregate_dlp_events.py
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Отчеты
|
||||
|
||||
### Daily Report
|
||||
```sql
|
||||
SELECT
|
||||
date,
|
||||
event_type,
|
||||
severity,
|
||||
COUNT(*) as incident_count
|
||||
FROM dlp_events
|
||||
WHERE date >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY date, event_type, severity
|
||||
ORDER BY date DESC, incident_count DESC;
|
||||
```
|
||||
|
||||
### User Summary
|
||||
```sql
|
||||
SELECT
|
||||
user_id,
|
||||
COUNT(*) as total_incidents,
|
||||
COUNT(CASE WHEN severity = 'high' THEN 1 END) as high_severity,
|
||||
COUNT(CASE WHEN severity = 'medium' THEN 1 END) as medium_severity
|
||||
FROM dlp_events
|
||||
WHERE timestamp >= CURRENT_DATE - INTERVAL '30 days'
|
||||
GROUP BY user_id
|
||||
ORDER BY total_incidents DESC;
|
||||
```
|
||||
|
||||
## Валидация данных
|
||||
|
||||
### Checks
|
||||
- Timestamp в допустимом диапазоне
|
||||
- Обязательные поля заполнены
|
||||
- Severity в списке допустимых значений
|
||||
- User/Host существуют в справочниках
|
||||
|
||||
### Data Quality
|
||||
```python
|
||||
validation_rules = {
|
||||
"timestamp": "required, past_date",
|
||||
"event_type": "required, in_list",
|
||||
"severity": "required, in_list",
|
||||
"user_id": "required, max_length=100",
|
||||
"host_id": "required, max_length=100"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,264 @@
|
||||
# DLP Endpoint Monitoring - Компонентная диаграмма
|
||||
|
||||
## Обзор
|
||||
Мониторинг конечных точек для обнаружения утечек данных через clipboard, печать и USB.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "DLP Endpoint Collector"
|
||||
A[Main Loop]
|
||||
B[Load DLP Policy]
|
||||
C[Deployment Config]
|
||||
D[Clipboard Monitor]
|
||||
E[Print Monitor]
|
||||
F[USB Monitor]
|
||||
G[Rule Engine]
|
||||
H[Enforcement Actions]
|
||||
I[Event Logging]
|
||||
J[Screenshot Capture]
|
||||
end
|
||||
|
||||
subgraph "External Dependencies"
|
||||
K[ActivityWatch API]
|
||||
L[DLP Policy JSON]
|
||||
M[Windows API]
|
||||
N[File System]
|
||||
end
|
||||
|
||||
A --> B
|
||||
A --> C
|
||||
B --> G
|
||||
C --> A
|
||||
|
||||
D --> G
|
||||
E --> G
|
||||
F --> G
|
||||
|
||||
G --> H
|
||||
G --> I
|
||||
G --> J
|
||||
|
||||
H --> M
|
||||
I --> N
|
||||
J --> N
|
||||
|
||||
I --> K
|
||||
A --> K
|
||||
|
||||
B --> L
|
||||
C --> L
|
||||
|
||||
style D fill:#ff6b6b
|
||||
style E fill:#ff6b6b
|
||||
style F fill:#ff6b6b
|
||||
style G fill:#4ecdc4
|
||||
style H fill:#ffe66d
|
||||
```
|
||||
|
||||
## Потоки данных
|
||||
|
||||
### Clipboard Monitoring Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant Clipboard as Clipboard API
|
||||
participant Collector as DLP Collector
|
||||
participant RuleEngine as Rule Engine
|
||||
participant AW as ActivityWatch
|
||||
participant FS as File System
|
||||
|
||||
User->>Clipboard: Copy data
|
||||
Clipboard->>Collector: Clipboard change event
|
||||
Collector->>RuleEngine: Evaluate against rules
|
||||
RuleEngine->>RuleEngine: Check patterns
|
||||
alt Pattern Match
|
||||
RuleEngine->>Collector: Trigger incident
|
||||
Collector->>FS: Capture screenshot
|
||||
Collector->>Collector: Apply enforcement
|
||||
Collector->>AW: Send DLP incident
|
||||
else No Match
|
||||
RuleEngine->>Collector: Allow
|
||||
end
|
||||
```
|
||||
|
||||
### Print Monitoring Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant App as Application
|
||||
participant PrintSpooler as Print Spooler
|
||||
participant Collector as DLP Collector
|
||||
participant RuleEngine as Rule Engine
|
||||
participant AW as ActivityWatch
|
||||
|
||||
App->>PrintSpooler: Print job
|
||||
PrintSpooler->>Collector: Print service event
|
||||
Collector->>Collector: Extract document info
|
||||
Collector->>RuleEngine: Evaluate printer rules
|
||||
alt Violation
|
||||
RuleEngine->>Collector: Block print job
|
||||
Collector->>PrintSpooler: Cancel job
|
||||
Collector->>AW: Log incident
|
||||
else Allowed
|
||||
Collector->>AW: Log print activity
|
||||
end
|
||||
```
|
||||
|
||||
### USB Monitoring Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User
|
||||
participant USB as USB Device
|
||||
participant Windows as Windows API
|
||||
participant Collector as DLP Collector
|
||||
participant RuleEngine as Rule Engine
|
||||
participant AW as ActivityWatch
|
||||
|
||||
User->>USB: Insert USB drive
|
||||
USB->>Windows: Device connect
|
||||
Windows->>Collector: USB device event
|
||||
Collector->>RuleEngine: Check USB rules
|
||||
alt Write Blocked
|
||||
RuleEngine->>Collector: Block write
|
||||
Collector->>Windows: Prevent write
|
||||
Collector->>AW: Log blocked attempt
|
||||
else Write Allowed
|
||||
Collector->>AW: Log file copy
|
||||
end
|
||||
```
|
||||
|
||||
## Ключевые функции
|
||||
|
||||
### Основные функции
|
||||
- `dlp_endpoint_signals_collector_main()` - главный цикл коллектора
|
||||
- `dlp_endpoint_signals_collector_load_dlppolicy()` - загрузка DLP правил
|
||||
- `dlp_endpoint_signals_collector_get_deploymentconfig()` - чтение конфигурации
|
||||
|
||||
### Мониторинг
|
||||
- `dlp_endpoint_signals_collector_evaluate_clipboardrules()` - проверка clipboard
|
||||
- `dlp_endpoint_signals_collector_evaluate_printrules()` - проверка печати
|
||||
- `dlp_endpoint_signals_collector_evaluate_usbrules()` - проверка USB
|
||||
|
||||
### Принудительные действия
|
||||
- `dlp_endpoint_signals_collector_invoke_clipboardenforcement()` - блокировка clipboard
|
||||
- `dlp_endpoint_signals_collector_invoke_printjobenforcement()` - блокировка печати
|
||||
- `dlp_endpoint_signals_collector_invoke_usbwriteblockenforcement()` - блокировка USB
|
||||
|
||||
### Логирование
|
||||
- `dlp_endpoint_signals_collector_write_endpointlog()` - запись логов коллектора
|
||||
- `dlp_endpoint_signals_collector_send_dlpincidentheartbeat()` - heartbeat инцидентов
|
||||
- `dlp_endpoint_signals_collector_capture_incidentscreenshot()` - захват скриншота
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### DLP Policy Structure
|
||||
```json
|
||||
{
|
||||
"clipboard_rules": [
|
||||
{
|
||||
"pattern": "\\b\\d{4}-\\d{4}-\\d{4}-\\d{4}\\b",
|
||||
"description": "Credit card numbers",
|
||||
"severity": "high",
|
||||
"action": "block"
|
||||
}
|
||||
],
|
||||
"print_rules": [
|
||||
{
|
||||
"printer_match": "*",
|
||||
"document_keywords": ["confidential", "secret"],
|
||||
"action": "block"
|
||||
}
|
||||
],
|
||||
"usb_rules": [
|
||||
{
|
||||
"device_id": "*",
|
||||
"action": "block_write"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Deployment Config
|
||||
```json
|
||||
{
|
||||
"aw_server_url": "http://aw-server:5600",
|
||||
"bucket_prefix": "aw-watcher-dlp-endpoint",
|
||||
"heartbeat_interval": 60,
|
||||
"screenshot_on_incident": true,
|
||||
"enforcement_enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
## События
|
||||
|
||||
### DLP Incident Event
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"type": "dlp_incident",
|
||||
"source": "clipboard",
|
||||
"rule_id": "credit_card_pattern",
|
||||
"severity": "high",
|
||||
"data": {
|
||||
"matched_text": "****-****-****-1234",
|
||||
"user": "user1",
|
||||
"host": "WORKSTATION01",
|
||||
"application": "chrome.exe",
|
||||
"screenshot": "path/to/screenshot.png"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Heartbeat Event
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"type": "heartbeat",
|
||||
"status": "running",
|
||||
"incidents_count": 5,
|
||||
"last_incident": "2024-01-01T11:55:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## Зависимости
|
||||
|
||||
### Windows API
|
||||
- Clipboard API
|
||||
- Print Spooler API
|
||||
- USB Device Notification API
|
||||
- Process API
|
||||
|
||||
### External Services
|
||||
- ActivityWatch HTTP API
|
||||
- File system (для скриншотов и логов)
|
||||
|
||||
## Развертывание
|
||||
|
||||
### Требования
|
||||
- Windows 10/11
|
||||
- PowerShell 5.1+
|
||||
- ActivityWatch installed
|
||||
- Административские права (для enforcement)
|
||||
|
||||
### Установка
|
||||
```powershell
|
||||
# Копирование коллектора
|
||||
Copy-Item dlp-endpoint-signals-collector.ps1 C:\ProgramData\AWatch-rus\
|
||||
|
||||
# Настройка scheduled task
|
||||
Register-ScheduledTask -TaskName "DLP Endpoint Collector" -Trigger $trigger -Action $action
|
||||
```
|
||||
|
||||
## Мониторинг
|
||||
|
||||
### Метрики
|
||||
- Количество инцидентов по типам (clipboard/print/USB)
|
||||
- Частота срабатываний правил
|
||||
- Успешность enforcement действий
|
||||
- Heartbeat статус
|
||||
|
||||
### Алерты
|
||||
- Коллектор не отправляет heartbeat > 5 минут
|
||||
- Высокая частота DLP инцидентов
|
||||
- Ошибки enforcement действий
|
||||
@@ -0,0 +1,338 @@
|
||||
# ActivityWatch-Russian - Обзор компонентов и связей
|
||||
|
||||
## Полная архитектура системы
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Windows Clients"
|
||||
subgraph "Workstation 1"
|
||||
W1A[DLP Endpoint Collector]
|
||||
W1B[Browser Domains Collector]
|
||||
W1C[Email Outbound Collector]
|
||||
W1D[Worktime Session Collector]
|
||||
end
|
||||
|
||||
subgraph "Workstation 2"
|
||||
W2A[DLP Endpoint Collector]
|
||||
W2B[Browser Domains Collector]
|
||||
W2C[Email Outbound Collector]
|
||||
end
|
||||
end
|
||||
|
||||
subgraph "Linux Server"
|
||||
S1[ActivityWatch Server]
|
||||
S2[PostgreSQL Database]
|
||||
S3[WebUI with RU Patches]
|
||||
end
|
||||
|
||||
subgraph "Integration Layer"
|
||||
I1[pfSense Poller]
|
||||
I2[DLP Aggregation Scripts]
|
||||
I3[Prometheus Exporter]
|
||||
end
|
||||
|
||||
subgraph "Monitoring Stack"
|
||||
M1[Prometheus]
|
||||
M2[Grafana]
|
||||
M3[SQL Exporter]
|
||||
end
|
||||
|
||||
subgraph "External Systems"
|
||||
E1[pfSense Firewall]
|
||||
E2[Domain Controller]
|
||||
end
|
||||
|
||||
W1A -->|Events| S1
|
||||
W1B -->|Events| S1
|
||||
W1C -->|Events| S1
|
||||
W1D -->|Events| S1
|
||||
W2A -->|Events| S1
|
||||
W2B -->|Events| S1
|
||||
W2C -->|Events| S1
|
||||
|
||||
S1 -->|Store| S2
|
||||
S1 -->|Serve| S3
|
||||
|
||||
I1 -->|Logs| E1
|
||||
I1 -->|Events| S1
|
||||
|
||||
I2 -->|Read/Write| S2
|
||||
I2 -->|Read| S1
|
||||
|
||||
I3 -->|Metrics| S1
|
||||
I3 -->|Scrape| M1
|
||||
|
||||
M1 -->|Query| I3
|
||||
M2 -->|Query| M1
|
||||
M2 -->|Query| S2
|
||||
M3 -->|Query| S2
|
||||
|
||||
E2 -->|Deploy| W1A
|
||||
E2 -->|Deploy| W1B
|
||||
E2 -->|Deploy| W1C
|
||||
E2 -->|Deploy| W2A
|
||||
E2 -->|Deploy| W2B
|
||||
E2 -->|Deploy| W2C
|
||||
|
||||
style S1 fill:#4ecdc4
|
||||
style S2 fill:#ff6b6b
|
||||
style M1 fill:#ffe66d
|
||||
style M2 fill:#95e1d3
|
||||
```
|
||||
|
||||
## Матрица связей компонентов
|
||||
|
||||
| Компонент | Тип | Подключается к | Протокол | Назначение |
|
||||
|-----------|-----|----------------|-----------|------------|
|
||||
| DLP Endpoint Collector | Windows Client | AW Server | HTTP API | Отправка DLP событий |
|
||||
| Browser Domains Collector | Windows Client | AW Server | HTTP API | Отправка браузерных событий |
|
||||
| Email Outbound Collector | Windows Client | AW Server | HTTP API | Отправка email событий |
|
||||
| Worktime Session Collector | Windows Client | AW Server | HTTP API | Отправка сессий |
|
||||
| ActivityWatch Server | Server | PostgreSQL | TCP | Хранение данных |
|
||||
| ActivityWatch Server | Server | WebUI | HTTP | Отдача интерфейса |
|
||||
| pfSense Poller | Integration | pfSense | HTTP API | Сбор логов firewall |
|
||||
| pfSense Poller | Integration | AW Server | HTTP API | Отправка событий |
|
||||
| DLP Aggregation | Integration | PostgreSQL | TCP | Обработка событий |
|
||||
| DLP Aggregation | Integration | AW Server | HTTP API | Чтение событий |
|
||||
| Prometheus Exporter | Integration | AW Server | HTTP API | Сбор метрик |
|
||||
| Prometheus Exporter | Integration | Prometheus | HTTP | Отдача метрик |
|
||||
| Prometheus | Monitoring | Exporter | HTTP | Scraping метрик |
|
||||
| Grafana | Monitoring | Prometheus | HTTP | Запросы метрик |
|
||||
| Grafana | Monitoring | PostgreSQL | TCP | Прямые запросы |
|
||||
| SQL Exporter | Monitoring | PostgreSQL | TCP | SQL запросы |
|
||||
|
||||
## Потоки данных по уровням
|
||||
|
||||
### Уровень 1: Сбор данных (Windows)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Windows Clients │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Workstation 1 Workstation 2 Workstation N │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
|
||||
│ │ DLP Collector│ │ DLP Collector│ │ DLP Coll.│ │
|
||||
│ │ Browser Coll.│ │ Browser Coll.│ │ Browser │ │
|
||||
│ │ Email Coll. │ │ Email Coll. │ │ Email │ │
|
||||
│ │ Worktime │ │ │ │ │ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ └──────────────────────┼───────────────────┘ │
|
||||
│ │ │
|
||||
└────────────────────────────────┼────────────────────────────┘
|
||||
│ HTTP API
|
||||
▼
|
||||
```
|
||||
|
||||
### Уровень 2: Хранение и обработка (Linux)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Linux Server Layer │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ AW Server │◄────── Events │
|
||||
│ │ (Rust) │ │
|
||||
│ └───────┬───────┘ │
|
||||
│ │ Store │
|
||||
│ ▼ │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ PostgreSQL │ │
|
||||
│ └───────┬───────┘ │
|
||||
│ │ │
|
||||
└────────────────────────────┼───────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
```
|
||||
|
||||
### Уровень 3: Интеграции
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Integration Layer │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ pfSense │ │ DLP Aggreg. │ │ Prometheus │ │
|
||||
│ │ Poller │ │ Scripts │ │ Exporter │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ pfSense FW │ │ PostgreSQL │ │ AW Server │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Уровень 4: Визуализация
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Monitoring & Visualization │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Prometheus │◄───┤ Exporter │ │ Grafana │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ └──────┬───────┘ └──────────────┘ └──────┬───────┘ │
|
||||
│ │ │ │
|
||||
│ │ Query │ Query │
|
||||
│ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Grafana │ │ PostgreSQL │ │
|
||||
│ │ Dashboards │ │ │ │
|
||||
│ └──────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Сценарии использования
|
||||
|
||||
### Сценарий 1: DLP инцидент
|
||||
```
|
||||
User copies sensitive data
|
||||
↓
|
||||
DLP Endpoint Collector detects
|
||||
↓
|
||||
Evaluates against rules
|
||||
↓
|
||||
Creates incident event
|
||||
↓
|
||||
Sends to AW Server
|
||||
↓
|
||||
Stored in PostgreSQL
|
||||
↓
|
||||
Aggregated by scripts
|
||||
↓
|
||||
Visible in Grafana DLP Dashboard
|
||||
```
|
||||
|
||||
### Сценарий 2: Мониторинг браузера
|
||||
```
|
||||
User visits website
|
||||
↓
|
||||
Browser Domains Collector detects
|
||||
↓
|
||||
Extracts domain
|
||||
↓
|
||||
Categorizes website
|
||||
↓
|
||||
Checks DLP rules
|
||||
↓
|
||||
Sends event to AW Server
|
||||
↓
|
||||
Visible in WebUI Dashboard
|
||||
```
|
||||
|
||||
### Сценарий 3: Метрики
|
||||
```
|
||||
Prometheus scrapes Exporter
|
||||
↓
|
||||
Exporter queries AW API
|
||||
↓
|
||||
Collects metrics
|
||||
↓
|
||||
Returns in Prometheus format
|
||||
↓
|
||||
Prometheus stores metrics
|
||||
↓
|
||||
Grafana visualizes in dashboards
|
||||
```
|
||||
|
||||
## Зависимости развертывания
|
||||
|
||||
### Минимальная конфигурация
|
||||
```
|
||||
1 Linux Server:
|
||||
- ActivityWatch Server
|
||||
- PostgreSQL
|
||||
- WebUI with patches
|
||||
|
||||
1+ Windows Workstations:
|
||||
- DLP Endpoint Collector
|
||||
- Browser Domains Collector
|
||||
- Email Outbound Collector
|
||||
```
|
||||
|
||||
### Полная конфигурация
|
||||
```
|
||||
1 Linux Server:
|
||||
- ActivityWatch Server
|
||||
- PostgreSQL
|
||||
- WebUI with patches
|
||||
- DLP Aggregation Scripts
|
||||
- Prometheus Exporter
|
||||
|
||||
1+ Windows Workstations:
|
||||
- DLP Endpoint Collector
|
||||
- Browser Domains Collector
|
||||
- Email Outbound Collector
|
||||
- Worktime Session Collector
|
||||
|
||||
1 pfSense Firewall:
|
||||
- pfSense Poller
|
||||
|
||||
1 Monitoring Server:
|
||||
- Prometheus
|
||||
- Grafana
|
||||
- SQL Exporter
|
||||
```
|
||||
|
||||
## Порты и протоколы
|
||||
|
||||
| Компонент | Порт | Протокол | Направление |
|
||||
|-----------|------|----------|-------------|
|
||||
| ActivityWatch API | 5600 | HTTP | Inbound |
|
||||
| ActivityWatch WebSocket | 5666 | WebSocket | Inbound |
|
||||
| PostgreSQL | 5432 | TCP | Inbound |
|
||||
| Prometheus | 9090 | HTTP | Inbound |
|
||||
| Grafana | 3000 | HTTP | Inbound |
|
||||
| Prometheus Exporter | 9398 | HTTP | Inbound |
|
||||
| pfSense API | 443 | HTTPS | Outbound |
|
||||
|
||||
## Резервное копирование
|
||||
|
||||
### PostgreSQL Backup
|
||||
```bash
|
||||
# Daily backup
|
||||
pg_dump activitywatch > backup_$(date +%Y%m%d).sql
|
||||
|
||||
# Restore
|
||||
psql activitywatch < backup_20240101.sql
|
||||
```
|
||||
|
||||
### AW Server Backup
|
||||
```bash
|
||||
# Backup SQLite databases (if used)
|
||||
cp /var/lib/activitywatch/*.db /backup/
|
||||
|
||||
# Backup configuration
|
||||
cp /etc/activitywatch/config.toml /backup/
|
||||
```
|
||||
|
||||
## Масштабирование
|
||||
|
||||
### Горизонтальное масштабирование
|
||||
- Добавление Windows workstation не требует изменений сервера
|
||||
- Каждый workstation автономно отправляет события
|
||||
- Server обрабатывает события от множества клиентов
|
||||
|
||||
### Вертикальное масштабирование
|
||||
- Увеличение ресурсов PostgreSQL для больших объемов данных
|
||||
- Разделение AW Server и PostgreSQL на разные машины
|
||||
- Добавление реплик PostgreSQL для высокой доступности
|
||||
|
||||
## Мониторинг системы
|
||||
|
||||
### Ключевые метрики
|
||||
- Количество активных хостов
|
||||
- Скорость поступления событий
|
||||
- Размер базы данных
|
||||
- Latency обработки событий
|
||||
- Статус коллекторов
|
||||
|
||||
### Алерты
|
||||
- Коллектор неактивен > 5 минут
|
||||
- Высокий процент DLP инцидентов
|
||||
- PostgreSQL connection pool exhausted
|
||||
- Диск > 80% заполнен
|
||||
- AW Server недоступен
|
||||
@@ -0,0 +1,373 @@
|
||||
# Prometheus Metrics Exporter - Компонентная диаграмма
|
||||
|
||||
## Обзор
|
||||
Экспорт метрик ActivityWatch в формате Prometheus для мониторинга и визуализации в Grafana.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Prometheus Exporter"
|
||||
A[HTTP Server]
|
||||
B[Metrics Collector]
|
||||
C[Bucket Fetcher]
|
||||
D[Event Counter]
|
||||
E[Host Tracker]
|
||||
F[Metrics Formatter]
|
||||
G[Registry]
|
||||
end
|
||||
|
||||
subgraph "Data Sources"
|
||||
H[ActivityWatch API]
|
||||
I[Buckets Data]
|
||||
J[Events Data]
|
||||
end
|
||||
|
||||
subgraph "Consumers"
|
||||
K[Prometheus]
|
||||
L[Grafana]
|
||||
end
|
||||
|
||||
A --> B
|
||||
B --> C
|
||||
B --> D
|
||||
B --> E
|
||||
C --> H
|
||||
C --> I
|
||||
D --> H
|
||||
D --> J
|
||||
E --> H
|
||||
F --> B
|
||||
F --> G
|
||||
G --> A
|
||||
|
||||
K --> A
|
||||
L --> K
|
||||
|
||||
style A fill:#ff6b6b
|
||||
style B fill:#4ecdc4
|
||||
style G fill:#ffe66d
|
||||
```
|
||||
|
||||
## Потоки данных
|
||||
|
||||
### Metrics Collection Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Prometheus as Prometheus
|
||||
participant Server as HTTP Server
|
||||
participant Collector as Metrics Collector
|
||||
participant AW as ActivityWatch API
|
||||
participant Registry as Metrics Registry
|
||||
|
||||
Prometheus->>Server: GET /metrics
|
||||
Server->>Collector: Trigger collection
|
||||
Collector->>AW: Get buckets list
|
||||
AW->>Collector: Return buckets
|
||||
loop For each bucket
|
||||
Collector->>AW: Get bucket info
|
||||
AW->>Collector: Return bucket data
|
||||
Collector->>Collector: Calculate metrics
|
||||
Collector->>Registry: Update metrics
|
||||
end
|
||||
Collector->>Server: Collection complete
|
||||
Server->>Registry: Format as Prometheus
|
||||
Registry->>Server: Return formatted metrics
|
||||
Server->>Prometheus: Return metrics text
|
||||
```
|
||||
|
||||
### Metrics Update Flow
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Scrape Request] --> B[Fetch Buckets]
|
||||
B --> C[Get Bucket Info]
|
||||
C --> D[Count Events]
|
||||
D --> E[Track Active Hosts]
|
||||
E --> F[Calculate Rates]
|
||||
F --> G[Update Registry]
|
||||
G --> H[Format Output]
|
||||
H --> I[Return to Prometheus]
|
||||
```
|
||||
|
||||
## Ключевые функции
|
||||
|
||||
### Инициализация
|
||||
- `aw_activitywatch_activitywatchexporter_init()` - инициализация exporter
|
||||
- `aw_activitywatch_activitywatchexporter()` - главный класс exporter
|
||||
- `aw_activitywatch_main()` - точка входа
|
||||
|
||||
### Сбор данных
|
||||
- `aw_activitywatch_activitywatchexporter_get_buckets()` - получение списка buckets
|
||||
- `aw_activitywatch_activitywatchexporter_get_bucket_info()` - информация о bucket
|
||||
- `aw_activitywatch_activitywatchexporter_get_bucket_events()` - события bucket
|
||||
- `aw_activitywatch_activitywatchexporter_collect_metrics()` - сбор всех метрик
|
||||
|
||||
### HTTP Server
|
||||
- Запуск HTTP сервера на порту 9398
|
||||
- Endpoint `/metrics` для Prometheus
|
||||
- Endpoint `/health` для health checks
|
||||
|
||||
### Метрики
|
||||
```python
|
||||
# Bucket metrics
|
||||
aw_bucket_events_total{bucket_id, host} - общее количество событий
|
||||
aw_bucket_events_last_timestamp{bucket_id} - timestamp последнего события
|
||||
|
||||
# Host metrics
|
||||
aw_host_active{host} - хост активен (1/0)
|
||||
aw_host_last_seen{host} - время последней активности
|
||||
|
||||
# Collector metrics
|
||||
aw_collector_heartbeat{host, collector_type} - heartbeat коллектора
|
||||
aw_collector_status{host, collector_type, status} - статус коллектора
|
||||
|
||||
# Aggregation metrics
|
||||
aw_aggregation_events_processed_total - обработано событий
|
||||
aw_aggregation_last_success_timestamp - последний успешный запуск
|
||||
|
||||
# Exporter metrics
|
||||
aw_exporter_scrape_duration_seconds - время обработки scrape
|
||||
aw_exporter_up - exporter доступен (1/0)
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Config
|
||||
```json
|
||||
{
|
||||
"aw_base_url": "http://aw-server:5600",
|
||||
"listen_address": "0.0.0.0",
|
||||
"listen_port": 9398,
|
||||
"scrape_interval_seconds": 60,
|
||||
"bucket_prefixes": [
|
||||
"aw-watcher-dlp-endpoint",
|
||||
"aw-watcher-browser-domains",
|
||||
"aw-watcher-email-outbound",
|
||||
"aw-watcher-window"
|
||||
],
|
||||
"cache_ttl_seconds": 30
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
```bash
|
||||
AW_BASE_URL=http://aw-server:5600
|
||||
EXPORTER_PORT=9398
|
||||
SCRAPE_INTERVAL=60
|
||||
LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
## Пример метрик
|
||||
|
||||
### Prometheus Format
|
||||
```
|
||||
# HELP aw_bucket_events_total Total number of events in bucket
|
||||
# TYPE aw_bucket_events_total gauge
|
||||
aw_bucket_events_total{bucket_id="aw-watcher-dlp-endpoint-WORKSTATION01",host="WORKSTATION01"} 1523.0
|
||||
aw_bucket_events_total{bucket_id="aw-watcher-browser-domains-WORKSTATION01",host="WORKSTATION01"} 3421.0
|
||||
|
||||
# HELP aw_host_active Host is active (1 or 0)
|
||||
# TYPE aw_host_active gauge
|
||||
aw_host_active{host="WORKSTATION01"} 1.0
|
||||
aw_host_active{host="WORKSTATION02"} 0.0
|
||||
|
||||
# HELP aw_host_last_seen Unix timestamp of last activity
|
||||
# TYPE aw_host_last_seen gauge
|
||||
aw_host_last_seen{host="WORKSTATION01"} 1704110400.0
|
||||
aw_host_last_seen{host="WORKSTATION02"} 1704100000.0
|
||||
|
||||
# HELP aw_exporter_scrape_duration_seconds Duration of scrape
|
||||
# TYPE aw_exporter_scrape_duration_seconds gauge
|
||||
aw_exporter_scrape_duration_seconds 0.523
|
||||
|
||||
# HELP aw_exporter_up Exporter is up (1 or 0)
|
||||
# TYPE aw_exporter_up gauge
|
||||
aw_exporter_up 1.0
|
||||
```
|
||||
|
||||
## Prometheus Configuration
|
||||
|
||||
### scrape_config
|
||||
```yaml
|
||||
scrape_configs:
|
||||
- job_name: 'activitywatch'
|
||||
static_configs:
|
||||
- targets: ['localhost:9398']
|
||||
scrape_interval: 60s
|
||||
scrape_timeout: 30s
|
||||
metrics_path: /metrics
|
||||
```
|
||||
|
||||
### Alerting Rules
|
||||
```yaml
|
||||
groups:
|
||||
- name: activitywatch_alerts
|
||||
rules:
|
||||
- alert: AWCollectorDown
|
||||
expr: aw_host_active == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "ActivityWatch collector down on {{ $labels.host }}"
|
||||
|
||||
- alert: AWExporterDown
|
||||
expr: aw_exporter_up == 0
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "ActivityWatch exporter is down"
|
||||
|
||||
- alert: AWHighEventRate
|
||||
expr: rate(aw_bucket_events_total[5m]) > 100
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "High event rate on {{ $labels.host }}"
|
||||
```
|
||||
|
||||
## Grafana Dashboard Queries
|
||||
|
||||
### Active Hosts
|
||||
```promql
|
||||
sum(aw_host_active) by (host)
|
||||
```
|
||||
|
||||
### Events per Bucket
|
||||
```promql
|
||||
aw_bucket_events_total
|
||||
```
|
||||
|
||||
### Event Rate
|
||||
```promql
|
||||
rate(aw_bucket_events_total[5m])
|
||||
```
|
||||
|
||||
### Host Last Seen
|
||||
```promql
|
||||
aw_host_last_seen
|
||||
```
|
||||
|
||||
### Collector Status
|
||||
```promql
|
||||
aw_collector_status{status="running"}
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
### Оптимизации
|
||||
- Кэширование ответов AW API
|
||||
- Batch запросы к buckets
|
||||
- Connection pooling
|
||||
- Асинхронная обработка
|
||||
|
||||
### Метрики производительности
|
||||
```python
|
||||
performance_metrics = {
|
||||
"scrape_duration_seconds": 0.5,
|
||||
"aw_api_latency_seconds": 0.2,
|
||||
"metrics_count": 50,
|
||||
"cache_hit_rate": 0.95
|
||||
}
|
||||
```
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Dockerfile
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
COPY collectors/aw_activitywatch.py .
|
||||
|
||||
EXPOSE 9398
|
||||
|
||||
CMD ["python", "aw_activitywatch.py"]
|
||||
```
|
||||
|
||||
### Docker Compose
|
||||
```yaml
|
||||
services:
|
||||
aw-exporter:
|
||||
build: ./sql-exporter
|
||||
ports:
|
||||
- "9398:9398"
|
||||
environment:
|
||||
- AW_BASE_URL=http://aw-server:5600
|
||||
- EXPORTER_PORT=9398
|
||||
depends_on:
|
||||
- aw-server
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
## Health Check
|
||||
|
||||
### Endpoint: /health
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"aw_server_reachable": true,
|
||||
"last_scrape_duration_seconds": 0.523,
|
||||
"metrics_count": 50
|
||||
}
|
||||
```
|
||||
|
||||
### Health Check Script
|
||||
```bash
|
||||
#!/bin/bash
|
||||
response=$(curl -s http://localhost:9398/health)
|
||||
status=$(echo $response | jq -r '.status')
|
||||
|
||||
if [ "$status" == "healthy" ]; then
|
||||
echo "Exporter is healthy"
|
||||
exit 0
|
||||
else
|
||||
echo "Exporter is unhealthy"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## Логирование
|
||||
|
||||
### Log Format
|
||||
```json
|
||||
{
|
||||
"timestamp": "2024-01-01T12:00:00Z",
|
||||
"level": "INFO",
|
||||
"message": "Metrics collection completed",
|
||||
"duration_seconds": 0.523,
|
||||
"buckets_processed": 10,
|
||||
"metrics_generated": 50
|
||||
}
|
||||
```
|
||||
|
||||
### Log Levels
|
||||
- DEBUG - детальная информация о сборе
|
||||
- INFO - нормальная работа
|
||||
- WARNING - не критичные проблемы
|
||||
- ERROR - ошибки при сборе метрик
|
||||
- CRITICAL - exporter недоступен
|
||||
|
||||
## Безопасность
|
||||
|
||||
### Рекомендации
|
||||
- Запускать за reverse proxy (nginx)
|
||||
- Ограничить доступ по IP
|
||||
- Использовать HTTPS в production
|
||||
- Не экспортировать чувствительные данные
|
||||
|
||||
### Nginx Config
|
||||
```nginx
|
||||
location /metrics {
|
||||
allow 10.0.0.0/8;
|
||||
deny all;
|
||||
proxy_pass http://localhost:9398/metrics;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,337 @@
|
||||
# WebUI Russian Localization Patches - Компонентная диаграмма
|
||||
|
||||
## Обзор
|
||||
Система патчей для русификации и адаптации ActivityWatch WebUI под российские требования.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Patch System"
|
||||
A[Patch Loader]
|
||||
B[DOM Injection]
|
||||
C[Style Injection]
|
||||
D[Navigation Patches]
|
||||
E[DLP Center Injection]
|
||||
F[Host Groups Center]
|
||||
G[Category Builder Patch]
|
||||
H[Text Translation]
|
||||
end
|
||||
|
||||
subgraph "ActivityWatch WebUI"
|
||||
I[Original WebUI]
|
||||
J[DOM Structure]
|
||||
K[Vue Components]
|
||||
L[Router]
|
||||
M[Axios API]
|
||||
end
|
||||
|
||||
subgraph "Patch Targets"
|
||||
N[Navigation Menu]
|
||||
O[Activity View]
|
||||
P[Category Builder]
|
||||
Q[Buckets View]
|
||||
R[Settings]
|
||||
end
|
||||
|
||||
A --> B
|
||||
A --> C
|
||||
A --> D
|
||||
A --> E
|
||||
A --> F
|
||||
A --> G
|
||||
A --> H
|
||||
|
||||
B --> J
|
||||
C --> J
|
||||
D --> L
|
||||
E --> K
|
||||
F --> K
|
||||
G --> K
|
||||
H --> K
|
||||
|
||||
D --> N
|
||||
E --> K
|
||||
F --> K
|
||||
G --> P
|
||||
H --> O
|
||||
|
||||
I --> J
|
||||
I --> K
|
||||
I --> L
|
||||
|
||||
style B fill:#ff6b6b
|
||||
style C fill:#ff6b6b
|
||||
style E fill:#4ecdc4
|
||||
style F fill:#4ecdc4
|
||||
style H fill:#ffe66d
|
||||
```
|
||||
|
||||
## Потоки данных
|
||||
|
||||
### Patch Application Flow
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant User as Browser
|
||||
participant WebUI as AW WebUI
|
||||
participant Loader as Patch Loader
|
||||
participant DOM as DOM Manipulator
|
||||
participant API as AW API
|
||||
|
||||
User->>WebUI: Load page
|
||||
WebUI->>Loader: Trigger patch application
|
||||
Loader->>Loader: Check route
|
||||
Loader->>DOM: Inject DLP center
|
||||
DOM->>DOM: Modify navigation
|
||||
DOM->>DOM: Add Russian styles
|
||||
Loader->>DOM: Translate text elements
|
||||
DOM->>DOM: Replace text with Russian
|
||||
Loader->>API: Fetch Russian data
|
||||
API->>Loader: Return translated data
|
||||
Loader->>DOM: Update UI with Russian
|
||||
DOM->>User: Render patched UI
|
||||
```
|
||||
|
||||
### DLP Center Injection Flow
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Route Check] --> B{Is DLP route?}
|
||||
B -->|Yes| C[Load DLP Center Component]
|
||||
B -->|No| D[Skip DLP injection]
|
||||
C --> E[Inject into DOM]
|
||||
E --> F[Bind Events]
|
||||
F --> G[Load DLP Data]
|
||||
G --> H[Render DLP UI]
|
||||
```
|
||||
|
||||
## Ключевые функции
|
||||
|
||||
### Загрузка патчей
|
||||
- `aw_ru_patch_applypatch()` - применение патча
|
||||
- `aw_ru_patch_scheduleapplypatch()` - отложенное применение
|
||||
- `aw_ru_patch_ensurehostgroupsdata()` - обеспечение данных групп хостов
|
||||
|
||||
### Инъекция компонентов
|
||||
- `aw_ru_patch_injectdlpnavigation()` - инъекция DLP навигации
|
||||
- `aw_ru_patch_injectdlpreviewcenter()` - инъекция центра просмотра DLP
|
||||
- `aw_ru_patch_injectdlpalertscenter()` - инъекция центра алертов
|
||||
- `aw_ru_patch_injecthostgroupscenter()` - инъекция центра групп хостов
|
||||
- `aw_ru_patch_injectpveauditcenter()` - инъекция центра аудита PVE
|
||||
|
||||
### Патчи навигации
|
||||
- `aw_ru_patch_findprimarynavlist()` - поиск главного меню
|
||||
- `aw_ru_patch_hidenoisenavigation()` - скрытие лишних пунктов
|
||||
- `aw_ru_patch_removebaddlplinks()` - удаление битых DLP ссылок
|
||||
- `aw_ru_patch_updatedlplinks()` - обновление DLP ссылок
|
||||
|
||||
### Патчи представлений
|
||||
- `aw_ru_patch_enforcesafeactivityviewforpvehost()` - безопасный вид для PVE
|
||||
- `aw_ru_patch_collapsereviewevents()` - сворачивание событий ревью
|
||||
- `aw_ru_patch_collapseruleevents()` - сворачивание событий правил
|
||||
- `aw_ru_patch_loadbucketevents()` - загрузка событий bucket
|
||||
|
||||
### Группы хостов
|
||||
- `aw_ru_patch_getdefaulthostgroupsconfig()` - конфигурация групп по умолчанию
|
||||
- `aw_ru_patch_matchhostgroup()` - сопоставление группы хоста
|
||||
- `aw_ru_patch_renderhostgroupcards()` - рендер карточек групп
|
||||
- `aw_ru_patch_hosthasbucketprefix()` - проверка префикса bucket
|
||||
|
||||
### Перевод
|
||||
- `aw_ru_patch_translateattributes()` - перевод атрибутов
|
||||
- `aw_ru_patch_normalizetext()` - нормализация текста
|
||||
- `aw_ru_patch_escapehtml()` - экранирование HTML
|
||||
|
||||
### Утилиты
|
||||
- `aw_ru_patch_replacetext()` - замена текста
|
||||
- `aw_ru_patch_walk()` - обход DOM
|
||||
- `aw_ru_patch_ishomeroute()` - проверка домашнего маршрута
|
||||
- `aw_ru_patch_ispvelikehost()` - проверка PVE хоста
|
||||
|
||||
## Структура патчей
|
||||
|
||||
### Патчи по маршрутам
|
||||
```javascript
|
||||
const routePatches = {
|
||||
'/': 'home-patch',
|
||||
'/activity': 'activity-patch',
|
||||
'/buckets': 'buckets-patch',
|
||||
'/category-builder': 'category-builder-patch',
|
||||
'/dlp/review': 'dlp-review-patch',
|
||||
'/dlp/rules': 'dlp-rules-patch',
|
||||
'/dlp/alerts': 'dlp-alerts-patch'
|
||||
};
|
||||
```
|
||||
|
||||
### Инъекции DLP компонентов
|
||||
```javascript
|
||||
const dlpInjections = {
|
||||
navigation: 'inject-dlp-nav',
|
||||
reviewCenter: 'inject-dlp-review',
|
||||
rulesManager: 'inject-dlp-rules',
|
||||
alertsCenter: 'inject-dlp-alerts'
|
||||
};
|
||||
```
|
||||
|
||||
### Русификация
|
||||
```javascript
|
||||
const translations = {
|
||||
'Activity': 'Активность',
|
||||
'Buckets': 'Бакеты',
|
||||
'Duration': 'Длительность',
|
||||
'Events': 'События',
|
||||
'Settings': 'Настройки'
|
||||
};
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Host Groups Config
|
||||
```json
|
||||
{
|
||||
"host_groups": [
|
||||
{
|
||||
"id": "workstations",
|
||||
"name": "Рабочие станции",
|
||||
"hosts": ["WORKSTATION01", "WORKSTATION02"],
|
||||
"color": "#4ecdc4"
|
||||
},
|
||||
{
|
||||
"id": "servers",
|
||||
"name": "Серверы",
|
||||
"hosts": ["SERVER01", "SERVER02"],
|
||||
"color": "#ff6b6b"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### DLP Settings
|
||||
```json
|
||||
{
|
||||
"dlp_enabled": true,
|
||||
"review_center_enabled": true,
|
||||
"alerts_center_enabled": true,
|
||||
"auto_refresh_interval": 30,
|
||||
"default_severity_filter": "all"
|
||||
}
|
||||
```
|
||||
|
||||
## Компоненты UI
|
||||
|
||||
### DLP Review Center
|
||||
- Список инцидентов
|
||||
- Фильтрация по типам
|
||||
- Детальный просмотр
|
||||
- Архивирование
|
||||
- Экспорт
|
||||
|
||||
### DLP Rules Manager
|
||||
- Список правил
|
||||
- Создание/редактирование
|
||||
- Включение/выключение
|
||||
- Тестирование правил
|
||||
- Импорт/экспорт
|
||||
|
||||
### DLP Alerts Center
|
||||
- Алерты в реальном времени
|
||||
- Группировка по серьезности
|
||||
- История алертов
|
||||
- Подписки на алерты
|
||||
|
||||
### Host Groups Center
|
||||
- Управление группами хостов
|
||||
- Визуализация по группам
|
||||
- Агрегированная статистика
|
||||
- Сравнение групп
|
||||
|
||||
## Интеграция с ActivityWatch
|
||||
|
||||
### API Endpoints
|
||||
```javascript
|
||||
// Получение DLP инцидентов
|
||||
GET /api/0/buckets/{bucket_id}/events?filters=dlp_incident
|
||||
|
||||
// Получение правил DLP
|
||||
GET /api/0/dlp/rules
|
||||
|
||||
// Сохранение инцидента
|
||||
POST /api/0/dlp/incident
|
||||
|
||||
// Получение групп хостов
|
||||
GET /api/0/host_groups
|
||||
```
|
||||
|
||||
### Bucket Prefixes
|
||||
```
|
||||
aw-watcher-dlp-endpoint-*
|
||||
aw-watcher-browser-domains-*
|
||||
aw-watcher-email-outbound-*
|
||||
```
|
||||
|
||||
## Стили
|
||||
|
||||
### Russian UI Styles
|
||||
```css
|
||||
.ru-font {
|
||||
font-family: 'Segoe UI', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.ru-nav-item {
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ru-dlp-card {
|
||||
border-left: 4px solid #ff6b6b;
|
||||
background: #fff5f5;
|
||||
}
|
||||
```
|
||||
|
||||
## Производительность
|
||||
|
||||
### Оптимизации
|
||||
- Lazy loading патчей
|
||||
- Кэширование переводов
|
||||
- Debouncing обновлений DOM
|
||||
- Virtual scrolling для больших списков
|
||||
|
||||
### Метрики
|
||||
- Время применения патча
|
||||
- Количество модификаций DOM
|
||||
- Частота перерисовки
|
||||
- Размер загружаемых переводов
|
||||
|
||||
## Отладка
|
||||
|
||||
### Логирование патчей
|
||||
```javascript
|
||||
console.log('[AW-RU-Patch] Applying patch:', patchName);
|
||||
console.log('[AW-RU-Patch] Route:', currentRoute);
|
||||
console.log('[AW-RU-Patch] Elements modified:', count);
|
||||
```
|
||||
|
||||
### Режим разработки
|
||||
```javascript
|
||||
const DEBUG_MODE = true;
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
window.AW_RU_PATCHES = {
|
||||
applied: [],
|
||||
skipped: [],
|
||||
errors: []
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Совместимость
|
||||
|
||||
### Поддерживаемые версии ActivityWatch
|
||||
- v0.12.x
|
||||
- v0.13.x
|
||||
- v0.14.x
|
||||
|
||||
### Поддерживаемые браузеры
|
||||
- Chrome 90+
|
||||
- Firefox 88+
|
||||
- Edge 90+
|
||||
- Safari 14+
|
||||
Reference in New Issue
Block a user