Feature/dev - #1272
Conversation
…l unreachable eastmoney, improve search provider selection - Add litellm/LiteLLM to DEFAULT_QUIET_LOGGERS (reduced log from 748 to 218 lines) - Pass pre-fetched realtime_quote into get_fundamental_context to avoid redundant network call - DNS probe eastmoney hosts at EfinanceFetcher/AkshareFetcher init; skip chip/board APIs when unreachable - Add prefer_provider for announcements (Anspire) and earnings (Tavily) dimensions to replace ineffective SerpAPI/SearXNG for Chinese content - Add docs/deploy-webui.md: Web service deployment and startup manual
Add src/migration.py with ensure_schema_current() that tracks schema versions in _schema_version table and applies v1 migration (user_id columns on 5 tables). Hook into DatabaseManager.__init__ after create_all.
- Replace is_auth_enabled()/verify_session() with has_users()/verify_session_user() - Add EXEMPT_PREFIXES for /api/v1/share/* public share links - Add /api/v1/auth/register to exempt paths - Extract user_id from session and store in request.state.user_id - Add test_auth_middleware.py with 8 test cases covering all branches - Update test_auth_api.py middleware tests to patch has_users/verify_session_user
…load, enrich timeline
… quickstart - main.py: catch ModuleNotFoundError on third-party imports and print a bilingual "pip install -r requirements.txt" message with exit code 1, instead of surfacing a raw Python traceback to first-time contributors. - NotFoundPage: display the attempted path (pathname + search) so users can tell a typo from a dead link. - README (zh / en / cht): add a "Minimum viable config" callout above the ~225-row secrets table, showing that STOCK_LIST + one AI key + one push channel is enough to get running. Everything else is optional. - CHANGELOG: three [Unreleased] entries in the flat format. Verification: - python -m py_compile main.py: OK - apps/dsa-web: npm run build OK (4.9s, 3211 modules). NotFoundPage lints clean. Two pre-existing lint errors in KLineChart.tsx and SidebarNav.tsx are unrelated to this change.
…tab layout unification
…suffix, Chinese names)
…ruth)
- Call normalize_stock_identity(stock_code) in POST /api/v1/watchlist;
canonical (code, name) from STOCK_NAME_MAP/akshare replaces any user-submitted name.
- Return HTTP 400 with {"error": "stock.identity_not_found"} for unresolvable codes,
using raise HTTPException with a dict detail — compatible with the existing
global http_exception_handler that spreads dict details to the top level.
- Add http_client fixture + 2 HTTP-level tests to tests/test_watchlist.py.
Wire normalize_stock_identity into POST /analyze so every code is resolved to a canonical (code, name) pair before task submission. Unknown codes that pass format validation but cannot be resolved (neither in STOCK_NAME_MAP nor akshare) now return 400 with error=stock.identity_not_found. Behavior change: stock_name passed to task queue is now the canonical name from STOCK_NAME_MAP rather than request.stock_name. Existing tests that asserted stock_name=None for resolvable codes (600519->贵州茅台, HK00700->腾讯控股) are updated to assert the canonical name. Tests that use codes outside STOCK_NAME_MAP (AAPL.US, 688783) now mock normalize_stock_identity to avoid network dependency and keep the focus on the routing/queue-submission semantics. MAX_BATCH_SIZE check moved to precede normalization so DoS protection triggers before any per-code identity lookups.
… persists canonical
…ASE_PATH
- vite.config.ts: base reads from VITE_BASE_PATH env var, defaults to '/'
- App.tsx: Router basename uses import.meta.env.BASE_URL (vite-injected)
- Default build behavior unchanged (root path); sub-path deploys use:
VITE_BASE_PATH=/your/path/ npm run build
The hardcoded '/stocks.index.json' broke autocomplete when the app is deployed under a sub-path (e.g. /agents/daily-stock/) — the request hit the host root and got back unrelated HTML, which JSON.parse silently failed on, leaving the search index empty. Prepend import.meta.env.BASE_URL so the URL resolves to: - '/stocks.index.json' for root deploys (default, unchanged) - '/agents/daily-stock/stocks.index.json' under sub-path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🤖 自动审查报告
📁 修改的文件
... 还有 10 个文件 🧠 AI 代码审查意见结论: Not Ready 必要性: 基于 PR 描述无法确认。PR 描述中的 "Background And Problem" 为空。然而,从代码改动内容看,本次 PR 引入了多用户认证、用户数据隔离、用户配置管理等一系列核心系统功能,这是非常重要的特性。 关联性: 不通过。PR 描述中的 "Issue Link" 为空,且未说明无 Issue 的原因和验收标准。这是必填项。 类型: 建议为 描述完整性: 不完整。缺失项包括:
关于验证充分性:
风险级别: 高。
必改项 (阻断项):
建议项:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34d7eb386a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| except Exception: | ||
| return False |
There was a problem hiding this comment.
Fail closed when user lookup errors in auth middleware
If the has_users() database query throws (e.g. transient DB outage, migration mismatch), this returns False, and dispatch() then skips authentication for every /api/v1/* route. That turns backend failures into an authorization bypass, exposing protected APIs exactly when infrastructure is unstable. The middleware should deny by default (or return 503) when user existence cannot be determined.
Useful? React with 👍 / 👎.
| _saved_env: Dict[str, Optional[str]] = {} | ||
|
|
||
|
|
||
| def _inject_user_settings(settings: Dict[str, str]) -> None: | ||
| """Temporarily inject user settings into os.environ.""" | ||
| global _saved_env | ||
| _saved_env = {} | ||
| for k, v in settings.items(): |
There was a problem hiding this comment.
Remove shared env snapshot across concurrent user runs
The scheduled per-user analysis path runs in parallel threads (src/scheduler.py dispatches one thread per due user), but _inject_user_settings() stores original env values in a single module-global _saved_env. Concurrent executions overwrite each other’s snapshot, so one user's restore step can corrupt another user's runtime config, causing cross-user model/API credentials and analysis settings to leak into the wrong job.
Useful? React with 👍 / 👎.
PR Type
Background And Problem
请描述当前问题、影响范围与触发场景。
(EN) Describe the problem, its impact, and what triggers it.
Scope Of Change
请列出本 PR 修改的模块和文件范围。
(EN) List the modules and files changed in this PR.
Issue Link
必须填写以下之一 / Fill in one of:
Fixes #<issue_number>Refs #<issue_number>Verification Commands And Results
请填写你实际执行过的命令和关键结果(不要只写"已测试")。
(EN) Paste the commands you actually ran and their key output (don't just write "tested"):
关键输出/结论 / Key output & conclusion:
Compatibility And Risk
请说明兼容性影响、潜在风险(如无请写
None)。(EN) Describe compatibility impact and potential risks (write
Noneif not applicable).(EN) If this PR changes third-party model/API compatibility, request parameters, routing prefixes, or provider fallback behavior, include an official source link or announcement and clarify whether the rule is permanent, runtime-specific, or a temporary compatibility workaround.)
(EN) If this PR depends on a specific runtime or pinned dependency window (for example a LiteLLM version range, OpenAI-compatible routing, or YAML alias behavior), state the compatibility window you verified and which code paths were covered.)
(EN) If this PR touches runtime config save/cleanup/migration/backfill logic, explicitly describe whether existing config is rewritten, cleared, migrated, or left intact, and how users can restore the previous behavior.)
Rollback Plan
请至少写一句可执行的回滚方案(必填)。
(EN) Provide at least one actionable rollback step (required).
revert this PR),并说明是否需要额外回滚配置或数据迁移。(EN) For compatibility fixes, include the minimal rollback path (for example
revert this PR) and whether any additional config or data rollback is required.)EXTRACT_PROMPT Change (if applicable)
若本 PR 修改了
src/services/image_stock_extractor.py中的EXTRACT_PROMPT,请在此处粘贴完整变更后的 prompt。If this PR changes
EXTRACT_PROMPTinsrc/services/image_stock_extractor.py, paste the full updated prompt here:展开 / Expand: Full EXTRACT_PROMPT
Checklist
docs/CHANGELOG.md;README.md仅在首页级信息变化时更新,细节优先写入docs/*.md/ If user-visible changes are included, relevant docs anddocs/CHANGELOG.mdare updated;README.mdis updated only for homepage-level changes, with details kept indocs/*.md