Repository navigation
Developer Known Pitfalls
Reference. Implementation and current repository policy take precedence if this page differs.
Status: supplemental traps only. For current authority boundaries and transitional runtime paths, start with Architecture Overview.
Common traps, gotchas, and "don't touch this" areas that have caused problems in the past.
Every admin query must filter by WHERE tenant_id = <session.tenant_id>. The tenant_id is the sole authorization boundary. Never accept tenant_id from user input.
Bad:
# Missing tenant_id filter — leaks data across tenants
query = db.query(ArchiveMessage).filter(ArchiveMessage.sender == sender)Good:
query = db.query(ArchiveMessage).filter(
ArchiveMessage.sender == sender,
ArchiveMessage.tenant_id == tenant_id, # from session
)This endpoint is called by WeCom servers (server-to-server). If you add Depends(get_current_user) here, the entire archive event callback will break silently.
The resolve_safe_media_path() function in media_storage.py is the only valid way to resolve media file paths. Never construct file paths directly from media_files.local_path — that field comes from the database and could be compromised.
The SDK (app/sdk/wecom_sdk.py) is wrapped via ctypes. It:
- Must be initialized before every call (
init_sdk()/destroy_sdk()). - Has a per-sdk-instance lock.
- Is only available on Linux (production).
- Cannot be tested in CI without the actual
.sofile.
Both the archive worker and media download worker use fcntl.flock on lock files. Never remove or change these locks without understanding the concurrency model. If you add a new worker, it must use its own lock file path.
AUTH_MODE=wecom (default) and AUTH_MODE=password are mutually exclusive. If you test the password endpoint while AUTH_MODE=wecom, you get a 404. This is intentional.
-
WECOM_OAUTH_SECRETis global OAuth configuration; archive credentials are encrypted tenant-scoped configuration and are not read fromWECOM_ARCHIVE_SECRET. -
ADMIN_DOMAINmust be the domain only (no protocol, no port, no path). -
MEDIA_STORAGE_PROVIDER=localis the new RND-185 selector.STORAGE_BACKEND=localis the backward-compatible alias.
Migrations must be idempotent where possible. If you add a migration that:
- Creates a unique index, handle the case where duplicate data already exists.
- Adds a NOT NULL column, provide a default or backfill first.
The archive worker runs sync then decrypt. If only sync runs (decrypt fails), messages exist in the DB with decrypt_status='pending'. They're invisible in the review console until decrypted.
- The media download worker is a single unified pipeline covering image/voice/video/file/emotion (RND-147 image, RND-199 voice/video/file/emotion).
- The timer uses
--since-hours 72to limit candidates — old messages may have expired WeCom download windows. - The reconciliation timer uses
--retry; durable retry eligibility is bounded by the configured attempt budget and backoff. Do not replace it with ad-hoc retries.
| Area | Risk | Why |
|---|---|---|
| Conversation aggregation logic | High |
_collect_staff_ids() heuristic — changes here affect what accounts appear in the console |
| Timeline message dedup | High |
mergeMessagesByMsgid() in JS — incorrect merge leads to duplicate or missing messages |
| Tenant bootstrap script | Medium |
bootstrap_default_tenant.py must be run exactly once; re-running may cause conflicts |
| Auth state token cleanup | Medium | OAuth state tokens are in-memory with 5-min TTL — not suitable for multi-process deployments |
| i18n locale switching | Low | Client-side only (localStorage) — server-rendered HTML always starts in zh-CN |
| Reachability audit query | Medium | Can be slow on large archives — the /api/messages query with reachability join is not optimized for extreme scale |
- Check
journalctl -u wecom-archive-worker.servicefor worker failures. - Check
journalctl -u wecom-archive-media-download.servicefor media download failures. - The
/healthendpoint is the first thing to check when deployment seems broken. - Use
alembic upgrade head --sqlto preview migration SQL before applying. - Run
python scripts/download_wecom_media_once.py --count-onlyto see media download progress without downloading (unified pipeline: image/voice/video/file/emotion; use--typesto narrow).
Maintained in the Wiki as a developer reference.