diff --git a/CLAUDE.md b/CLAUDE.md index 58912c3..958eb55 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -154,7 +154,7 @@ When reopening the project in Claude Code: - `src/parquet_manager.py` - Parquet conversion engine - `connectors/jira/file_lock.py` - Advisory file locking - `connectors/jira/incremental_transform.py` - Jira monthly Parquet transform -- `server/ws_gateway/` - WebSocket notification gateway +- `services/ws_gateway/` - WebSocket notification gateway ## Git Commits & Pull Requests diff --git a/dev_docs/desktop-app.md b/dev_docs/desktop-app.md index 2f5f84a..b3599c7 100644 --- a/dev_docs/desktop-app.md +++ b/dev_docs/desktop-app.md @@ -21,7 +21,7 @@ notify-runner (cron, every 5min) ``` Notifications are delivered in parallel to both Telegram and the desktop app. -The WebSocket gateway (`server/ws_gateway/`) runs as a separate systemd service alongside the existing Telegram bot. +The WebSocket gateway (`services/ws_gateway/`) runs as a separate systemd service alongside the existing Telegram bot. ## Requirements @@ -90,7 +90,7 @@ Client -> Server: {"type":"pong"} ## Server Components -### WebSocket Gateway (`server/ws_gateway/`) +### WebSocket Gateway (`services/ws_gateway/`) - `gateway.py` - main asyncio+aiohttp server with two listeners: - TCP WebSocket on `127.0.0.1:8765` (proxied by nginx) @@ -121,7 +121,7 @@ Unlinking removes the entry from `desktop_users.json` but does not invalidate th ### Notification Dispatch -Both `server/bin/notify-runner` and `server/telegram_bot/bot.py` dispatch notifications to the WebSocket gateway alongside Telegram delivery. The webapp API (`POST /api/desktop/scripts/run`) also dispatches via `server/telegram_bot/dispatch.py`. The dispatch is fire-and-forget - if the gateway is not running, it silently skips. +Both `server/bin/notify-runner` and `services/telegram_bot/bot.py` dispatch notifications to the WebSocket gateway alongside Telegram delivery. The webapp API (`POST /api/desktop/scripts/run`) also dispatches via `services/telegram_bot/dispatch.py`. The dispatch is fire-and-forget - if the gateway is not running, it silently skips. Script execution (from webapp API and Telegram bot) uses the `notify-scripts` helper: ```bash diff --git a/dev_docs/plan-corporate-memory.md b/dev_docs/plan-corporate-memory.md index 52316bc..4eb65f8 100644 --- a/dev_docs/plan-corporate-memory.md +++ b/dev_docs/plan-corporate-memory.md @@ -193,7 +193,7 @@ def deduplicate_and_merge(new_items: list, username: str): ### 1. Server-side Collector -**`server/corporate_memory/collector.py`** - Main collection logic: +**`services/corporate_memory/collector.py`** - Main collection logic: - `collect_all()` - iteruje přes /home/*/CLAUDE.local.md - `should_process_user(username)` - kontrola hash změn (šetří tokeny) - `extract_knowledge(content)` - volá HAIKU API @@ -202,7 +202,7 @@ def deduplicate_and_merge(new_items: list, username: str): - `save_knowledge(data)` - atomic JSON write - `update_user_hash(username, hash)` - uloží hash pro příští run -**`server/corporate_memory/prompts.py`** - HAIKU prompts: +**`services/corporate_memory/prompts.py`** - HAIKU prompts: - Prompt pro extrakci znalostí - Prompt pro AI filtering citlivých dat - Prompt pro merge podobných znalostí (optional) @@ -301,9 +301,9 @@ rsync -avz data-analyst:~/.claude_rules/ .claude/rules/ 2>/dev/null || \ ## Implementation Phases ### Phase 1: Server Infrastructure -1. `server/corporate_memory/` Python modul +1. `services/corporate_memory/` Python modul 2. `server/bin/collect-knowledge` wrapper -3. `server/corporate-memory.service` + `.timer` +3. `services/corporate_memory/systemd/corporate-memory.service` + `.timer` 4. Update `server/deploy.sh` 5. Update sudoers @@ -336,7 +336,7 @@ rsync -avz data-analyst:~/.claude_rules/ .claude/rules/ 2>/dev/null || \ - **JSON I/O**: `webapp/telegram_service.py` - atomic writes with tempfile - **Dashboard widget**: `webapp/templates/dashboard.html` - KPI card pattern - **Sub-page**: `webapp/templates/catalog.html` - route + template pattern -- **Systemd service**: `server/notify-bot.service` - timer pattern +- **Systemd service**: `services/telegram_bot/systemd/notify-bot.service` - timer pattern - **Deploy**: `server/deploy.sh` - directory setup, script installation ## Verification diff --git a/dev_docs/security.md b/dev_docs/security.md index 2ec35f4..4ec70ee 100644 --- a/dev_docs/security.md +++ b/dev_docs/security.md @@ -342,7 +342,7 @@ Given that notifications go to both Telegram and the desktop app, and there is n #### Recommendations -1. **Immediate**: Change socket permissions to `0660` or `0770` in the bot code (`server/telegram_bot/bot.py`) or systemd service file. The socket is currently set to `0666` by an `ExecStartPost` or in code -- update to restrict to `data-ops` group. +1. **Immediate**: Change socket permissions to `0660` or `0770` in the bot code (`services/telegram_bot/bot.py`) or systemd service file. The socket is currently set to `0666` by an `ExecStartPost` or in code -- update to restrict to `data-ops` group. 2. **Better**: Add `SO_PEERCRED` validation in the bot's HTTP handler to verify the caller's UID and ensure they can only send notifications for their own username. @@ -413,7 +413,7 @@ The `username` parameter comes from webapp request data or Telegram bot callback #### Code Analysis -In `server/telegram_bot/runner.py` (line 30): +In `services/telegram_bot/runner.py` (line 30): ```python result = subprocess.run( ["/usr/bin/sudo", "-u", username, NOTIFY_SCRIPTS_BIN, "run", script_name], @@ -638,7 +638,7 @@ All analyst home directories are `750`, but deploy's home is `755` (world-readab **Severity:** MEDIUM **Source:** Codex second opinion -The WebSocket gateway (`server/ws_gateway/gateway.py`) validates JWT tokens but does not: +The WebSocket gateway (`services/ws_gateway/gateway.py`) validates JWT tokens but does not: - Check the `Origin` header of WebSocket connections - Implement replay protection (a captured auth message could be replayed within token validity) - Bind tokens to specific connections or IP addresses @@ -746,8 +746,8 @@ The following findings were identified by the OpenAI Codex second opinion review - `server/sudoers-deploy`, `server/sudoers-webapp` (privilege escalation) - `server/deploy.sh` (CI/CD pipeline) - `server/bin/notify-runner`, `server/bin/notify-scripts` (notification pipeline) - - `server/telegram_bot/` (bot service, dispatch, runner) - - `server/ws_gateway/` (WebSocket gateway, JWT auth) + - `services/telegram_bot/` (bot service, dispatch, runner) + - `services/ws_gateway/` (WebSocket gateway, JWT auth) - `webapp/desktop_auth.py`, `webapp/user_service.py` (auth flows) - `.github/workflows/deploy.yml` (CI/CD configuration) diff --git a/dev_docs/server.md b/dev_docs/server.md index 9e75302..62c4d1c 100644 --- a/dev_docs/server.md +++ b/dev_docs/server.md @@ -911,7 +911,7 @@ Gunicorn runs with a restricted PATH (only `/opt/data-analyst/.venv/bin`). There - `/usr/local/bin/add-analyst` - `/usr/local/bin/notify-scripts` -This is handled in `webapp/user_service.py` and `server/telegram_bot/runner.py`. +This is handled in `webapp/user_service.py` and `services/telegram_bot/runner.py`. ### Username Generation @@ -1103,8 +1103,8 @@ sudo -u /usr/local/bin/notify-scripts sync-status The `sync-status` command reads the mtime of `~/server/` directory. This is updated by `sync_data.sh` via `touch ~/server/` at the end of each sync. Each user has their own `~/server/` directory (containing symlinks to shared `/data/`), so timestamps are per-user. **Callers:** -- `server/telegram_bot/status.py` - `/status` command and script list API -- `server/telegram_bot/runner.py` - on-demand script execution (Telegram "Run" button, webapp API) +- `services/telegram_bot/status.py` - `/status` command and script list API +- `services/telegram_bot/runner.py` - on-demand script execution (Telegram "Run" button, webapp API) - `webapp/account_service.py` - Account card "Last Sync" display **Sudoers rules:** diff --git a/dev_docs/telegram_bot.md b/dev_docs/telegram_bot.md index d5319ec..2b9430f 100644 --- a/dev_docs/telegram_bot.md +++ b/dev_docs/telegram_bot.md @@ -31,7 +31,7 @@ Technical documentation for the notification engine (Phase 3, Issue #41). ### 1. Telegram Bot Service -**Source:** `server/telegram_bot/` +**Source:** `services/telegram_bot/` | File | Purpose | |------|---------| @@ -53,7 +53,7 @@ Technical documentation for the notification engine (Phase 3, Issue #41). - `POST /send_photo` - send photo with caption (`user`, `photo_path`, `caption`) - `GET /health` - health check -**Systemd service:** `server/notify-bot.service` +**Systemd service:** `services/telegram_bot/systemd/notify-bot.service` - User: `deploy`, Group: `data-ops` - EnvironmentFile: `/opt/data-analyst/.env` (contains `TELEGRAM_BOT_TOKEN`) - Restarts automatically on failure @@ -170,7 +170,7 @@ Scripts output JSON to stdout: |--------|-------------| | `server/bin/notify-scripts` | `/usr/local/bin/notify-scripts` | | `server/bin/notify-runner` | `/usr/local/bin/notify-runner` | -| `server/notify-bot.service` | `/etc/systemd/system/notify-bot.service` | +| `services/telegram_bot/systemd/notify-bot.service` | `/etc/systemd/system/notify-bot.service` | | `examples/notifications/*.py` | `/data/docs/examples/notifications/` | | `docs/notifications.md` | `/data/docs/notifications.md` | diff --git a/server/sudoers-deploy b/server/sudoers-deploy index f94e56e..f8d225f 100644 --- a/server/sudoers-deploy +++ b/server/sudoers-deploy @@ -61,7 +61,7 @@ deploy ALL=(ALL) NOPASSWD: /usr/bin/chown deploy\:data-ops /data/notifications deploy ALL=(ALL) NOPASSWD: /usr/bin/chmod 2770 /data/notifications # Allow deploy user to manage notify-bot service -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/notify-bot.service /etc/systemd/system/notify-bot.service +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/telegram_bot/systemd/notify-bot.service /etc/systemd/system/notify-bot.service deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart notify-bot deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl start notify-bot @@ -74,7 +74,7 @@ deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active notify-bot deploy ALL=(%dataread) NOPASSWD: /usr/local/bin/notify-scripts # Allow deploy user to manage ws-gateway service -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/ws-gateway.service /etc/systemd/system/ws-gateway.service +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/ws_gateway/systemd/ws-gateway.service /etc/systemd/system/ws-gateway.service deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart ws-gateway deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl start ws-gateway deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop ws-gateway @@ -105,8 +105,8 @@ deploy ALL=(ALL) NOPASSWD: /usr/bin/chmod 2770 /data/auth deploy ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /data/corporate-memory deploy ALL=(ALL) NOPASSWD: /usr/bin/chown deploy\:data-ops /data/corporate-memory deploy ALL=(ALL) NOPASSWD: /usr/bin/chmod 2770 /data/corporate-memory -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/corporate-memory.service /etc/systemd/system/corporate-memory.service -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/corporate-memory.timer /etc/systemd/system/corporate-memory.timer +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/corporate_memory/systemd/corporate-memory.service /etc/systemd/system/corporate-memory.service +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/corporate_memory/systemd/corporate-memory.timer /etc/systemd/system/corporate-memory.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable corporate-memory.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl start corporate-memory.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop corporate-memory.timer @@ -124,8 +124,8 @@ deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-enabled jira-sla-poll.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /data/user_sessions deploy ALL=(ALL) NOPASSWD: /usr/bin/chown root\:data-ops /data/user_sessions deploy ALL=(ALL) NOPASSWD: /usr/bin/chmod 2770 /data/user_sessions -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/session-collector.service /etc/systemd/system/session-collector.service -deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/server/session-collector.timer /etc/systemd/system/session-collector.timer +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/session_collector/systemd/session-collector.service /etc/systemd/system/session-collector.service +deploy ALL=(ALL) NOPASSWD: /usr/bin/cp /opt/data-analyst/repo/services/session_collector/systemd/session-collector.timer /etc/systemd/system/session-collector.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable session-collector.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl start session-collector.timer deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop session-collector.timer