## Summary epic r04 - begin refactor ## Change Type - [x] Cowork feature - [ ] Bug fix - [ ] Core AI contribution - [ ] Test / hardening - [ ] Performance - [ ] Documentation ## Related Work Cowork Task: Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets Core AI Issue: Core Task: Related PR: ## Scope What is intentionally included? What is intentionally NOT included? ## Validation - [ ] Unit tests - [ ] Integration tests - [ ] Manual verification - [ ] Regression check Commands / evidence: ## Security Impact Permission / credential / network / customer data impact: ## Compatibility - [ ] No breaking change - [ ] Breaking change documented ## Reviewer Notes Anything Cowork reviewers should pay attention to. --------- Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com> Co-authored-by: Huong Le Thi Thien <huongltt35@fpt.com> Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com> Co-authored-by: Vu Dam Tuan <vudt15@fpt.com> Co-authored-by: Hiep Ha Van <hiephv3@fpt.com> Co-authored-by: Lam Hoang Van <lamhv7@fpt.com> Reviewed-on: #7 Co-authored-by: Duy Le Huu <duylh19@fpt.com>
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""Composition Root — R08-T10.
|
||||
|
||||
Một chỗ duy nhất quyết định app chạy bằng những mảnh nào. Trước đây quyết định
|
||||
đó nằm rải trong ``app.py::run``, lẫn với việc dựng cửa sổ; tách ra để đổi một
|
||||
mảnh (ví dụ thay kho bí mật) không phải đụng vào mã giao diện.
|
||||
|
||||
Đây cũng là chỗ hoàn tất R02: từ đây app chạy bằng :class:`JsonConfigRepository`
|
||||
chứ không còn ``config.py::AppConfig``. Hai thứ đổi thật sự:
|
||||
|
||||
* ghi cấu hình qua ``AtomicJsonFile`` — mất điện giữa lúc lưu không làm hỏng file
|
||||
* API key nằm trong kho bí mật của hệ điều hành, không nằm trong ``config.json``
|
||||
|
||||
Máy không có kho bí mật (Linux headless, CI, hoặc keyring hỏng) vẫn chạy bình
|
||||
thường: repository nhận ``secrets=None`` và đọc khoá thẳng từ file như cũ. Thà
|
||||
để khoá trong file còn hơn xoá đi rồi người dùng mất khoá mà không hiểu vì sao.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from ...infrastructure.config.json_config_repository import JsonConfigRepository
|
||||
from ...infrastructure.secrets.keyring_adapter import KeyringAdapter
|
||||
from ...state import AppContext
|
||||
|
||||
|
||||
def build_secret_store():
|
||||
"""Kho bí mật của hệ điều hành, hoặc None nếu máy này không có.
|
||||
|
||||
``KeyringAdapter`` không bao giờ ném lỗi — nó tự báo ``available``. Trả về
|
||||
None thay vì một adapter chết để chỗ gọi khỏi phải đoán.
|
||||
"""
|
||||
store = KeyringAdapter()
|
||||
return store if store.available else None
|
||||
|
||||
|
||||
def build_config(path: Path | None = None) -> JsonConfigRepository:
|
||||
"""Dựng repository cấu hình đã nối sẵn kho bí mật của hệ điều hành.
|
||||
|
||||
Đây là Composition Root: chỗ DUY NHẤT quyết định app chạy bằng mảnh nào.
|
||||
"""
|
||||
return JsonConfigRepository.load(path, secrets=build_secret_store())
|
||||
|
||||
|
||||
def build_context(path: Path | None = None) -> AppContext:
|
||||
"""Dựng AppContext hoàn chỉnh — điểm vào cho ``app.run()`` và cho checker."""
|
||||
return AppContext(build_config(path))
|
||||
Reference in New Issue
Block a user