diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9e7f50f..cd2057d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -32,10 +32,10 @@ jobs: - name: Run tests env: - PYTHONPATH: src + PYTHONPATH: . run: | pytest tests/ -v - name: Security check with Bandit run: | - bandit -r src -ll + bandit -r duty_teller -ll diff --git a/.gitignore b/.gitignore index 6c3bfbf..030ade7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ venv/ .venv/ +*.egg-info/ *.pyc *.pyo data/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 38a5cd2..38bbd64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,4 +36,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Input validation and initData hash verification for Miniapp access. - Optional CORS and init_data_max_age; use env for secrets. -[0.1.0]: https://github.com/your-org/duty-teller/releases/tag/v0.1.0 +[0.1.0]: https://github.com/your-org/duty-teller/releases/tag/v0.1.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aba078c..3829a5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,9 +10,9 @@ 2. **Create a virtual environment** ```bash - python -m venv venv - source venv/bin/activate # Linux/macOS - # or: venv\Scripts\activate # Windows + python -m venv .venv + source .venv/bin/activate # Linux/macOS + # or: .venv\Scripts\activate # Windows ``` 3. **Install dependencies** diff --git a/README.md b/README.md index fcb2224..a179812 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ A minimal Telegram bot boilerplate using [python-telegram-bot](https://github.co 2. **Create a virtual environment (recommended)** ```bash - python -m venv venv - source venv/bin/activate # Linux/macOS - # or: venv\Scripts\activate # Windows + python -m venv .venv + source .venv/bin/activate # Linux/macOS + # or: .venv\Scripts\activate # Windows ``` 3. **Install dependencies** @@ -144,7 +144,7 @@ pytest Tests cover `api/telegram_auth` (validate_init_data, auth_date expiry), `config` (is_admin, can_access_miniapp), and the API (date validation, 403/200 with mocked auth, plus an E2E auth test without auth mocks). -**CI (Gitea Actions):** Lint (ruff), tests (pytest), security (bandit). If the workflow uses `PYTHONPATH: src` or `bandit -r src`, update it to match the repo layout (no `src/`). +**CI (Gitea Actions):** Lint (ruff), tests (pytest), security (bandit). Run from the repo root with `PYTHONPATH=.` if needed. ## Contributing diff --git a/docs/import-format.md b/docs/import-format.md index 129785a..44140f7 100644 --- a/docs/import-format.md +++ b/docs/import-format.md @@ -1,6 +1,6 @@ # Duty-schedule import format -The **duty-schedule** format is used by the `/import_duty_schedule` command. Only users in `ADMIN_USERNAMES` or `ADMIN_PHONES` can import. +The **duty-schedule** format is used by the `/import_duty_schedule` command. Only users in `ADMIN_USERNAMES` or `ADMIN_PHONES`, or users with the **admin** role in the database (assigned via `/set_role`), can import. ## Import flow diff --git a/docs/runbook.md b/docs/runbook.md index 3fd5163..5cc2455 100644 --- a/docs/runbook.md +++ b/docs/runbook.md @@ -31,8 +31,8 @@ On container start, `entrypoint.sh` runs Alembic migrations then starts the app ## Health check -- **HTTP:** The FastAPI app serves the API and static webapp. A simple way to verify it is up is to open the interactive API docs: **`GET /docs`** (e.g. `http://localhost:8080/docs`). If that page loads, the server is running. -- There is no dedicated `/health` endpoint; use `/docs` or a lightweight API call (e.g. `GET /api/duties?from=...&to=...` with valid auth) as needed. +- **HTTP:** The FastAPI app exposes **`GET /health`**, which returns `{"status": "ok"}` with 200 when the app is up. Use it for readiness checks and in Docker `HEALTHCHECK`. +- Alternatively, open the interactive API docs at **`GET /docs`** (e.g. `http://localhost:8080/docs`) or call a lightweight API endpoint with valid auth. ## Logs @@ -63,8 +63,8 @@ On container start, `entrypoint.sh` runs Alembic migrations then starts the app ### User not in allowlist (403) -- **Cause:** Telegram user’s username is not in `ALLOWED_USERNAMES` or `ADMIN_USERNAMES`, and (if using phone) their phone (set via `/set_phone`) is not in `ALLOWED_PHONES` or `ADMIN_PHONES`. -- **Check:** [configuration.md](configuration.md) for `ALLOWED_USERNAMES`, `ADMIN_USERNAMES`, `ALLOWED_PHONES`, `ADMIN_PHONES`. Add the user or ask them to set phone and add it to the allowlist. +- **Cause:** Miniapp access is determined by **roles in the database** (see `can_access_miniapp_for_telegram_user` in the repository). If the user has no role in the DB, the app falls back to `ADMIN_USERNAMES` and `ADMIN_PHONES`. A 403 means the user has neither a role granting access nor a match in the admin allowlist. +- **Check:** Assign a role with `/set_role @username user` or `admin` (admin-only command), or add the user to `ADMIN_USERNAMES` / their phone to `ADMIN_PHONES` in [configuration.md](configuration.md). Ensure at least one admin exists via env so they can use `/set_role`. ## Database and migrations diff --git a/duty_teller/config.py b/duty_teller/config.py index 5281a3a..bcf0297 100644 --- a/duty_teller/config.py +++ b/duty_teller/config.py @@ -147,7 +147,10 @@ def is_admin(username: str) -> bool: def can_access_miniapp(username: str) -> bool: - """Check if username is allowed to open the calendar Miniapp. + """Check if username is allowed to open the calendar Miniapp (env allowlist only). + + Legacy: Miniapp access in production is determined by repository.can_access_miniapp_for_telegram_user + (DB roles + fallback to ADMIN_*). This function is kept for tests and backward compatibility. Args: username: Telegram username (with or without @; case-insensitive). @@ -160,7 +163,10 @@ def can_access_miniapp(username: str) -> bool: def can_access_miniapp_by_phone(phone: str | None) -> bool: - """Check if phone (set via /set_phone) is allowed to open the Miniapp. + """Check if phone (set via /set_phone) is allowed to open the Miniapp (env allowlist only). + + Legacy: Miniapp access in production is determined by repository.can_access_miniapp_for_telegram_user + (DB roles + fallback to ADMIN_*). This function is kept for tests and backward compatibility. Args: phone: Raw phone string or None. diff --git a/mkdocs.yml b/mkdocs.yml index 8e8e4f3..1cf686c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,6 +2,7 @@ # Build: mkdocs build. Preview: mkdocs serve. site_name: Duty Teller site_description: Telegram bot for team duty shift calendar and group reminder +# Set to your repository URL when publishing (placeholder below). site_url: https://github.com/your-org/duty-teller docs_dir: docs diff --git a/pyproject.toml b/pyproject.toml index 3a4819a..d39df54 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,6 +54,3 @@ asyncio_mode = "auto" filterwarnings = [ "ignore::DeprecationWarning:pytest_asyncio.plugin", ] - -[tool.pylint.messages_control] -disable = ["C0114", "C0115", "C0116"]