refactor: imports and reorganize domain structure

- Updated import statements across multiple modules to use relative imports for better encapsulation.
- Consolidated and organized the `__init__.py` files in various domain packages to expose necessary classes and functions.
- Improved code readability and maintainability by grouping related imports and removing unused ones.
- Ensured consistent import patterns across the domain, enhancing the overall structure of the codebase.
This commit is contained in:
shiyu
2026-01-09 17:28:10 +08:00
parent a727e77341
commit 6b2ada0b42
69 changed files with 420 additions and 169 deletions
+2 -2
View File
@@ -15,7 +15,7 @@ from domain.agent import api as agent
from domain.virtual_fs import api as virtual_fs from domain.virtual_fs import api as virtual_fs
from domain.virtual_fs.mapping import s3_api, webdav_api from domain.virtual_fs.mapping import s3_api, webdav_api
from domain.virtual_fs.search import search_api from domain.virtual_fs.search import search_api
from domain.audit import router as audit from domain.audit import api as audit
def include_routers(app: FastAPI): def include_routers(app: FastAPI):
@@ -37,4 +37,4 @@ def include_routers(app: FastAPI):
app.include_router(s3_api.router) app.include_router(s3_api.router)
app.include_router(offline_downloads.router) app.include_router(offline_downloads.router)
app.include_router(email.router) app.include_router(email.router)
app.include_router(audit) app.include_router(audit.router)
+1 -1
View File
@@ -1,6 +1,6 @@
from tortoise import Tortoise from tortoise import Tortoise
from domain.adapters.registry import runtime_registry from domain.adapters import runtime_registry
TORTOISE_ORM = { TORTOISE_ORM = {
"connections": {"default": "sqlite://data/db/db.sqlite3"}, "connections": {"default": "sqlite://data/db/db.sqlite3"},
+7
View File
@@ -0,0 +1,7 @@
"""
domain:业务域层
约定:跨包只从各子包 `__init__.py` 导入公开 API。
"""
__all__: list[str] = []
+23
View File
@@ -1 +1,24 @@
from .providers import BaseAdapter
from .registry import (
RuntimeRegistry,
discover_adapters,
get_config_schema,
get_config_schemas,
normalize_adapter_type,
runtime_registry,
)
from .service import AdapterService
from .types import AdapterCreate, AdapterOut
__all__ = [
"BaseAdapter",
"RuntimeRegistry",
"discover_adapters",
"get_config_schema",
"get_config_schemas",
"normalize_adapter_type",
"runtime_registry",
"AdapterService",
"AdapterCreate",
"AdapterOut",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Depends, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.adapters.service import AdapterService from domain.auth import User, get_current_active_user
from domain.adapters.types import AdapterCreate from .service import AdapterService
from domain.auth.service import get_current_active_user from .types import AdapterCreate
from domain.auth.types import User
router = APIRouter(prefix="/api/adapters", tags=["adapters"]) router = APIRouter(prefix="/api/adapters", tags=["adapters"])
+2 -2
View File
@@ -4,7 +4,7 @@ from importlib import import_module
from typing import Callable, Dict from typing import Callable, Dict
from models import StorageAdapter from models import StorageAdapter
from domain.adapters.providers.base import BaseAdapter from .providers.base import BaseAdapter
AdapterFactory = Callable[[StorageAdapter], BaseAdapter] AdapterFactory = Callable[[StorageAdapter], BaseAdapter]
@@ -21,7 +21,7 @@ def normalize_adapter_type(value: str | None) -> str | None:
def discover_adapters(): def discover_adapters():
"""扫描 domain.adapters.providers 包, 自动注册适配器类型、工厂与配置 schema。""" """扫描 domain.adapters.providers 包, 自动注册适配器类型、工厂与配置 schema。"""
from domain.adapters import providers as adapters_pkg from . import providers as adapters_pkg
TYPE_MAP.clear() TYPE_MAP.clear()
CONFIG_SCHEMAS.clear() CONFIG_SCHEMAS.clear()
+3 -3
View File
@@ -2,13 +2,13 @@ from typing import Optional
from fastapi import HTTPException from fastapi import HTTPException
from domain.adapters.registry import ( from domain.auth import User
from .registry import (
get_config_schemas, get_config_schemas,
normalize_adapter_type, normalize_adapter_type,
runtime_registry, runtime_registry,
) )
from domain.adapters.types import AdapterCreate, AdapterOut from .types import AdapterCreate, AdapterOut
from domain.auth.types import User
from models import StorageAdapter from models import StorageAdapter
+8 -3
View File
@@ -1,4 +1,9 @@
from .api import router from .service import AgentService
from .types import AgentChatContext, AgentChatRequest, PendingToolCall
__all__ = ["router"]
__all__ = [
"AgentService",
"AgentChatContext",
"AgentChatRequest",
"PendingToolCall",
]
+3 -4
View File
@@ -4,11 +4,10 @@ from fastapi import APIRouter, Depends, Request
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from api.response import success from api.response import success
from domain.agent.service import AgentService
from domain.agent.types import AgentChatRequest
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import AgentService
from .types import AgentChatRequest
router = APIRouter(prefix="/api/agent", tags=["agent"]) router = APIRouter(prefix="/api/agent", tags=["agent"])
+30 -8
View File
@@ -6,11 +6,10 @@ from typing import Any, Dict, List, Optional, Tuple
import httpx import httpx
from fastapi import HTTPException from fastapi import HTTPException
from domain.agent.tools import get_tool, openai_tools, tool_result_to_content from domain.ai import AIProviderService, MissingModelError, chat_completion, chat_completion_stream
from domain.agent.types import AgentChatRequest, PendingToolCall from domain.auth import User
from domain.ai.inference import MissingModelError, chat_completion, chat_completion_stream from .tools import get_tool, openai_tools, tool_result_to_content
from domain.ai.service import AIProviderService from .types import AgentChatRequest, PendingToolCall
from domain.auth.types import User
def _normalize_path(p: Optional[str]) -> Optional[str]: def _normalize_path(p: Optional[str]) -> Optional[str]:
@@ -136,6 +135,11 @@ def _sse(event: str, data: Any) -> bytes:
return f"event: {event}\ndata: {payload}\n\n".encode("utf-8") return f"event: {event}\ndata: {payload}\n\n".encode("utf-8")
def _format_exc(exc: BaseException) -> str:
text = str(exc)
return text if text else exc.__class__.__name__
class AgentService: class AgentService:
@classmethod @classmethod
async def chat(cls, req: AgentChatRequest, user: Optional[User]) -> Dict[str, Any]: async def chat(cls, req: AgentChatRequest, user: Optional[User]) -> Dict[str, Any]:
@@ -376,11 +380,11 @@ class AgentService:
if isinstance(msg, dict): if isinstance(msg, dict):
assistant_message = msg assistant_message = msg
except MissingModelError as exc: except MissingModelError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc raise HTTPException(status_code=400, detail=_format_exc(exc)) from exc
except httpx.HTTPStatusError as exc: except httpx.HTTPStatusError as exc:
raise HTTPException(status_code=502, detail=f"对话请求失败: {exc}") from exc raise HTTPException(status_code=502, detail=f"对话请求失败: {_format_exc(exc)}") from exc
except httpx.RequestError as exc: except httpx.RequestError as exc:
raise HTTPException(status_code=502, detail=f"对话请求异常: {exc}") from exc raise HTTPException(status_code=502, detail=f"对话请求异常: {_format_exc(exc)}") from exc
if not assistant_message: if not assistant_message:
assistant_message = {"role": "assistant", "content": ""} assistant_message = {"role": "assistant", "content": ""}
@@ -446,3 +450,21 @@ class AgentService:
except asyncio.CancelledError: except asyncio.CancelledError:
return return
except HTTPException as exc:
detail = exc.detail
content = detail if isinstance(detail, str) else str(detail)
if not content.strip():
content = f"请求失败({exc.status_code})"
new_messages.append({"role": "assistant", "content": content})
payload: Dict[str, Any] = {"messages": new_messages}
if pending:
payload["pending_tool_calls"] = [p.model_dump() for p in pending]
yield _sse("done", payload)
return
except Exception as exc: # noqa: BLE001
new_messages.append({"role": "assistant", "content": f"服务端异常: {_format_exc(exc)}"})
payload: Dict[str, Any] = {"messages": new_messages}
if pending:
payload["pending_tool_calls"] = [p.model_dump() for p in pending]
yield _sse("done", payload)
return
+3 -4
View File
@@ -2,10 +2,9 @@ import json
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, List, Optional from typing import Any, Awaitable, Callable, Dict, List, Optional
from domain.processors.service import ProcessorService from domain.processors import ProcessDirectoryRequest, ProcessRequest, ProcessorService
from domain.processors.types import ProcessDirectoryRequest, ProcessRequest from domain.virtual_fs import VirtualFSService
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs.search import VirtualFSSearchService
from domain.virtual_fs.search.search_service import VirtualFSSearchService
@dataclass(frozen=True) @dataclass(frozen=True)
+39 -6
View File
@@ -1,28 +1,61 @@
from .api import router_ai, router_vector_db from .inference import (
MissingModelError,
chat_completion,
chat_completion_stream,
describe_image_base64,
get_text_embedding,
provider_service,
rerank_texts,
)
from .service import ( from .service import (
AIProviderService, AIProviderService,
FILE_COLLECTION_NAME,
VECTOR_COLLECTION_NAME,
DEFAULT_VECTOR_DIMENSION,
VectorDBConfigManager, VectorDBConfigManager,
VectorDBService, VectorDBService,
DEFAULT_VECTOR_DIMENSION,
ABILITIES,
normalize_capabilities,
) )
from .types import ( from .types import (
ABILITIES,
AIDefaultsUpdate, AIDefaultsUpdate,
AIModelCreate, AIModelCreate,
AIModelUpdate, AIModelUpdate,
AIProviderCreate, AIProviderCreate,
AIProviderUpdate, AIProviderUpdate,
VectorDBConfigPayload, VectorDBConfigPayload,
normalize_capabilities,
)
from .vector_providers import (
BaseVectorProvider,
MilvusLiteProvider,
MilvusServerProvider,
QdrantProvider,
get_provider_class,
get_provider_entry,
list_providers,
) )
__all__ = [ __all__ = [
"router_ai", "MissingModelError",
"router_vector_db", "chat_completion",
"chat_completion_stream",
"describe_image_base64",
"get_text_embedding",
"provider_service",
"rerank_texts",
"AIProviderService", "AIProviderService",
"VectorDBService", "VectorDBService",
"VectorDBConfigManager", "VectorDBConfigManager",
"DEFAULT_VECTOR_DIMENSION", "DEFAULT_VECTOR_DIMENSION",
"VECTOR_COLLECTION_NAME",
"FILE_COLLECTION_NAME",
"BaseVectorProvider",
"MilvusLiteProvider",
"MilvusServerProvider",
"QdrantProvider",
"list_providers",
"get_provider_entry",
"get_provider_class",
"ABILITIES", "ABILITIES",
"normalize_capabilities", "normalize_capabilities",
"AIDefaultsUpdate", "AIDefaultsUpdate",
+4 -5
View File
@@ -5,8 +5,9 @@ from fastapi import APIRouter, Depends, HTTPException, Path, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.ai.service import AIProviderService, VectorDBConfigManager, VectorDBService from domain.auth import User, get_current_active_user
from domain.ai.types import ( from .service import AIProviderService, VectorDBConfigManager, VectorDBService
from .types import (
AIDefaultsUpdate, AIDefaultsUpdate,
AIModelCreate, AIModelCreate,
AIModelUpdate, AIModelUpdate,
@@ -14,9 +15,7 @@ from domain.ai.types import (
AIProviderUpdate, AIProviderUpdate,
VectorDBConfigPayload, VectorDBConfigPayload,
) )
from domain.ai.vector_providers import get_provider_class, get_provider_entry, list_providers from .vector_providers import get_provider_class, get_provider_entry, list_providers
from domain.auth.service import get_current_active_user
from domain.auth.types import User
router_ai = APIRouter(prefix="/api/ai", tags=["ai"]) router_ai = APIRouter(prefix="/api/ai", tags=["ai"])
router_vector_db = APIRouter(prefix="/api/vector-db", tags=["vector-db"]) router_vector_db = APIRouter(prefix="/api/vector-db", tags=["vector-db"])
+1 -1
View File
@@ -4,7 +4,7 @@ import httpx
from typing import Any, AsyncIterator, Dict, List, Sequence, Tuple from typing import Any, AsyncIterator, Dict, List, Sequence, Tuple
from models.database import AIModel, AIProvider from models.database import AIModel, AIProvider
from domain.ai.service import AIProviderService from .service import AIProviderService
provider_service = AIProviderService provider_service = AIProviderService
+1 -1
View File
@@ -7,7 +7,7 @@ import httpx
from tortoise.exceptions import DoesNotExist from tortoise.exceptions import DoesNotExist
from tortoise.transactions import in_transaction from tortoise.transactions import in_transaction
from domain.config.service import ConfigService from domain.config import ConfigService
from models.database import AIDefaultModel, AIModel, AIProvider from models.database import AIDefaultModel, AIModel, AIProvider
from .types import ABILITIES, normalize_capabilities from .types import ABILITIES, normalize_capabilities
+3 -4
View File
@@ -1,5 +1,4 @@
from domain.audit.decorator import audit from .decorator import audit
from domain.audit.types import AuditAction from .types import AuditAction
from domain.audit.api import router
__all__ = ["audit", "AuditAction", "router"] __all__ = ["audit", "AuditAction"]
+3 -4
View File
@@ -4,10 +4,9 @@ from typing import Annotated, Optional
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from api import response from api import response
from domain.audit.service import AuditService from domain.auth import User, get_current_active_user
from domain.audit.types import AuditAction from .service import AuditService
from domain.auth.service import get_current_active_user from .types import AuditAction
from domain.auth.types import User
CurrentUser = Annotated[User, Depends(get_current_active_user)] CurrentUser = Annotated[User, Depends(get_current_active_user)]
+4 -4
View File
@@ -7,11 +7,11 @@ import jwt
from fastapi import Request from fastapi import Request
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from domain.audit.service import AuditService from domain.auth import ALGORITHM
from domain.audit.types import AuditAction from domain.config import ConfigService
from domain.auth.service import ALGORITHM
from domain.config.service import ConfigService
from models.database import UserAccount from models.database import UserAccount
from .service import AuditService
from .types import AuditAction
def _extract_request(bound_args: Mapping[str, Any]) -> Request | None: def _extract_request(bound_args: Mapping[str, Any]) -> Request | None:
+1 -1
View File
@@ -2,7 +2,7 @@ from typing import Any, Dict, Optional
from models.database import AuditLog from models.database import AuditLog
from domain.audit.types import AuditAction from .types import AuditAction
class AuditService: class AuditService:
+49
View File
@@ -0,0 +1,49 @@
from .service import (
ALGORITHM,
AuthService,
authenticate_user_db,
create_access_token,
get_current_active_user,
get_current_user,
get_password_hash,
has_users,
register_user,
request_password_reset,
reset_password_with_token,
verify_password,
verify_password_reset_token,
)
from .types import (
PasswordResetConfirm,
PasswordResetRequest,
RegisterRequest,
Token,
TokenData,
UpdateMeRequest,
User,
UserInDB,
)
__all__ = [
"ALGORITHM",
"AuthService",
"authenticate_user_db",
"create_access_token",
"get_current_active_user",
"get_current_user",
"get_password_hash",
"has_users",
"register_user",
"request_password_reset",
"reset_password_with_token",
"verify_password",
"verify_password_reset_token",
"PasswordResetConfirm",
"PasswordResetRequest",
"RegisterRequest",
"Token",
"TokenData",
"UpdateMeRequest",
"User",
"UserInDB",
]
+2 -2
View File
@@ -5,8 +5,8 @@ from fastapi.security import OAuth2PasswordRequestForm
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import AuthService, get_current_active_user from .service import AuthService, get_current_active_user
from domain.auth.types import ( from .types import (
PasswordResetConfirm, PasswordResetConfirm,
PasswordResetRequest, PasswordResetRequest,
RegisterRequest, RegisterRequest,
+4 -4
View File
@@ -11,7 +11,9 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from domain.auth.types import ( from domain.config import ConfigService
from models.database import UserAccount
from .types import (
PasswordResetConfirm, PasswordResetConfirm,
PasswordResetRequest, PasswordResetRequest,
RegisterRequest, RegisterRequest,
@@ -21,8 +23,6 @@ from domain.auth.types import (
User, User,
UserInDB, UserInDB,
) )
from models.database import UserAccount
from domain.config.service import ConfigService
ALGORITHM = "HS256" ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 365 ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 365
@@ -324,7 +324,7 @@ class AuthService:
@classmethod @classmethod
async def _send_password_reset_email(cls, user: UserAccount, token: str) -> None: async def _send_password_reset_email(cls, user: UserAccount, token: str) -> None:
from domain.email.service import EmailService from domain.email import EmailService
app_domain = await ConfigService.get("APP_DOMAIN", None) app_domain = await ConfigService.get("APP_DOMAIN", None)
base_url = (app_domain or "http://localhost:5173").rstrip("/") base_url = (app_domain or "http://localhost:5173").rstrip("/")
+6
View File
@@ -1 +1,7 @@
from .service import BackupService
from .types import BackupData
__all__ = [
"BackupService",
"BackupData",
]
+2 -2
View File
@@ -4,8 +4,8 @@ from fastapi import APIRouter, Depends, File, Request, UploadFile
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import get_current_active_user
from domain.backup.service import BackupService from .service import BackupService
router = APIRouter( router = APIRouter(
prefix="/api/backup", prefix="/api/backup",
+2 -2
View File
@@ -4,8 +4,8 @@ from datetime import datetime
from fastapi import HTTPException from fastapi import HTTPException
from tortoise.transactions import in_transaction from tortoise.transactions import in_transaction
from domain.backup.types import BackupData from domain.config import VERSION
from domain.config.service import VERSION from .types import BackupData
from models.database import ( from models.database import (
AIDefaultModel, AIDefaultModel,
AIModel, AIModel,
+10
View File
@@ -0,0 +1,10 @@
from .service import ConfigService, VERSION
from .types import ConfigItem, LatestVersionInfo, SystemStatus
__all__ = [
"ConfigService",
"VERSION",
"ConfigItem",
"LatestVersionInfo",
"SystemStatus",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Depends, Form, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import ConfigService
from domain.config.service import ConfigService from .types import ConfigItem
from domain.config.types import ConfigItem
router = APIRouter(prefix="/api/config", tags=["config"]) router = APIRouter(prefix="/api/config", tags=["config"])
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Any, Dict, Optional
import httpx import httpx
from dotenv import load_dotenv from dotenv import load_dotenv
from domain.config.types import LatestVersionInfo, SystemStatus from .types import LatestVersionInfo, SystemStatus
from models.database import Configuration, UserAccount from models.database import Configuration, UserAccount
load_dotenv(dotenv_path=".env") load_dotenv(dotenv_path=".env")
+20
View File
@@ -0,0 +1,20 @@
from .service import EmailService, EmailTemplateRenderer
from .types import (
EmailConfig,
EmailSecurity,
EmailSendPayload,
EmailTemplatePreviewPayload,
EmailTemplateUpdate,
EmailTestRequest,
)
__all__ = [
"EmailService",
"EmailTemplateRenderer",
"EmailConfig",
"EmailSecurity",
"EmailSendPayload",
"EmailTemplatePreviewPayload",
"EmailTemplateUpdate",
"EmailTestRequest",
]
+3 -4
View File
@@ -2,10 +2,9 @@ from fastapi import APIRouter, Depends, HTTPException, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import EmailService, EmailTemplateRenderer
from domain.email.service import EmailService, EmailTemplateRenderer from .types import (
from domain.email.types import (
EmailTemplatePreviewPayload, EmailTemplatePreviewPayload,
EmailTemplateUpdate, EmailTemplateUpdate,
EmailTestRequest, EmailTestRequest,
+4 -4
View File
@@ -7,8 +7,8 @@ from pathlib import Path
from string import Template from string import Template
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from domain.config.service import ConfigService from domain.config import ConfigService
from domain.email.types import EmailConfig, EmailSecurity, EmailSendPayload from .types import EmailConfig, EmailSecurity, EmailSendPayload
class EmailTemplateRenderer: class EmailTemplateRenderer:
@@ -104,7 +104,7 @@ class EmailService:
template: str, template: str,
context: Optional[Dict[str, Any]] = None, context: Optional[Dict[str, Any]] = None,
): ):
from domain.tasks.task_queue import TaskProgress, task_queue_service from domain.tasks import TaskProgress, task_queue_service
payload = EmailSendPayload( payload = EmailSendPayload(
recipients=recipients, recipients=recipients,
@@ -126,7 +126,7 @@ class EmailService:
@classmethod @classmethod
async def send_from_task(cls, task_id: str, data: Dict[str, Any]): async def send_from_task(cls, task_id: str, data: Dict[str, Any]):
from domain.tasks.task_queue import TaskProgress, task_queue_service from domain.tasks import TaskProgress, task_queue_service
payload = EmailSendPayload(**data) payload = EmailSendPayload(**data)
+7
View File
@@ -0,0 +1,7 @@
from .service import OfflineDownloadService
from .types import OfflineDownloadCreate
__all__ = [
"OfflineDownloadService",
"OfflineDownloadCreate",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Depends, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import OfflineDownloadService
from domain.offline_downloads.service import OfflineDownloadService from .types import OfflineDownloadCreate
from domain.offline_downloads.types import OfflineDownloadCreate
CurrentUser = Annotated[User, Depends(get_current_active_user)] CurrentUser = Annotated[User, Depends(get_current_active_user)]
+4 -5
View File
@@ -7,11 +7,10 @@ import aiofiles
import aiohttp import aiohttp
from fastapi import Depends, HTTPException from fastapi import Depends, HTTPException
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from domain.tasks import Task, TaskProgress, task_queue_service
from domain.offline_downloads.types import OfflineDownloadCreate from domain.virtual_fs import VirtualFSService
from domain.virtual_fs.service import VirtualFSService from .types import OfflineDownloadCreate
from domain.tasks.task_queue import Task, TaskProgress, task_queue_service
class OfflineDownloadService: class OfflineDownloadService:
+3 -3
View File
@@ -4,9 +4,9 @@ Foxel 插件系统
提供 .foxpkg 插件包的安装、管理和运行时加载功能。 提供 .foxpkg 插件包的安装、管理和运行时加载功能。
""" """
from domain.plugins.loader import PluginLoader, PluginLoadError from .loader import PluginLoadError, PluginLoader
from domain.plugins.service import PluginService from .service import PluginService
from domain.plugins.startup import init_plugins, load_installed_plugins from .startup import init_plugins, load_installed_plugins
__all__ = [ __all__ = [
"PluginLoader", "PluginLoader",
+2 -2
View File
@@ -8,8 +8,8 @@ from fastapi import APIRouter, File, Request, UploadFile
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.plugins.service import PluginService from .service import PluginService
from domain.plugins.types import ( from .types import (
PluginInstallResult, PluginInstallResult,
PluginOut, PluginOut,
) )
+3 -3
View File
@@ -20,7 +20,7 @@ from typing import Any, Dict, List, Optional, Tuple
from fastapi import APIRouter from fastapi import APIRouter
from domain.plugins.types import ( from .types import (
ManifestProcessorConfig, ManifestProcessorConfig,
ManifestRouteConfig, ManifestRouteConfig,
PluginManifest, PluginManifest,
@@ -344,7 +344,7 @@ class PluginLoader:
supported_exts = getattr(module, "SUPPORTED_EXTS", []) supported_exts = getattr(module, "SUPPORTED_EXTS", [])
# 注册到处理器注册表 # 注册到处理器注册表
from domain.processors.registry import CONFIG_SCHEMAS, TYPE_MAP from domain.processors import CONFIG_SCHEMAS, TYPE_MAP
processor_type = processor_config.type processor_type = processor_config.type
TYPE_MAP[processor_type] = factory TYPE_MAP[processor_type] = factory
@@ -401,7 +401,7 @@ class PluginLoader:
""" """
# 卸载处理器 # 卸载处理器
if manifest and manifest.backend and manifest.backend.processors: if manifest and manifest.backend and manifest.backend.processors:
from domain.processors.registry import CONFIG_SCHEMAS, TYPE_MAP from domain.processors import CONFIG_SCHEMAS, TYPE_MAP
for proc_config in manifest.backend.processors: for proc_config in manifest.backend.processors:
proc_type = proc_config.type proc_type = proc_config.type
+2 -2
View File
@@ -12,8 +12,8 @@ from typing import List, Optional, Union
from fastapi import HTTPException from fastapi import HTTPException
from domain.plugins.loader import PluginLoadError, PluginLoader from .loader import PluginLoadError, PluginLoader
from domain.plugins.types import ( from .types import (
PluginInstallResult, PluginInstallResult,
PluginManifest, PluginManifest,
PluginOut, PluginOut,
+2 -3
View File
@@ -7,8 +7,8 @@
import logging import logging
from typing import TYPE_CHECKING, List, Tuple from typing import TYPE_CHECKING, List, Tuple
from domain.plugins.loader import PluginLoadError, PluginLoader from .loader import PluginLoadError, PluginLoader
from domain.plugins.types import PluginManifest from .types import PluginManifest
if TYPE_CHECKING: if TYPE_CHECKING:
from fastapi import FastAPI from fastapi import FastAPI
@@ -113,4 +113,3 @@ async def init_plugins(app: "FastAPI") -> None:
logger.warning(f" - {error}") logger.warning(f" - {error}")
else: else:
logger.info(f"插件加载完成,共 {loaded_count} 个插件") logger.info(f"插件加载完成,共 {loaded_count} 个插件")
+35
View File
@@ -0,0 +1,35 @@
from .base import BaseProcessor
from .registry import (
CONFIG_SCHEMAS,
TYPE_MAP,
get_config_schema,
get_config_schemas,
get_last_discovery_errors,
get_module_path,
reload_processors,
)
from .service import (
ProcessorService,
get_processor,
list_processors,
reload_processor_modules,
)
from .types import ProcessDirectoryRequest, ProcessRequest, UpdateSourceRequest
__all__ = [
"BaseProcessor",
"CONFIG_SCHEMAS",
"TYPE_MAP",
"get_config_schema",
"get_config_schemas",
"get_last_discovery_errors",
"get_module_path",
"reload_processors",
"ProcessorService",
"get_processor",
"list_processors",
"reload_processor_modules",
"ProcessDirectoryRequest",
"ProcessRequest",
"UpdateSourceRequest",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Body, Depends, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import ProcessorService
from domain.processors.service import ProcessorService from .types import (
from domain.processors.types import (
ProcessDirectoryRequest, ProcessDirectoryRequest,
ProcessRequest, ProcessRequest,
UpdateSourceRequest, UpdateSourceRequest,
+6 -4
View File
@@ -8,12 +8,14 @@ from fastapi.responses import Response
from PIL import Image from PIL import Image
from ..base import BaseProcessor from ..base import BaseProcessor
from domain.ai.inference import describe_image_base64, get_text_embedding, provider_service from domain.ai import (
from domain.ai.service import (
VectorDBService,
DEFAULT_VECTOR_DIMENSION, DEFAULT_VECTOR_DIMENSION,
VECTOR_COLLECTION_NAME,
FILE_COLLECTION_NAME, FILE_COLLECTION_NAME,
VECTOR_COLLECTION_NAME,
VectorDBService,
describe_image_base64,
get_text_embedding,
provider_service,
) )
+2 -2
View File
@@ -5,7 +5,7 @@ from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Callable, Dict, Optional from typing import Callable, Dict, Optional
from domain.processors.base import BaseProcessor from .base import BaseProcessor
ProcessorFactory = Callable[[], BaseProcessor] ProcessorFactory = Callable[[], BaseProcessor]
TYPE_MAP: Dict[str, ProcessorFactory] = {} TYPE_MAP: Dict[str, ProcessorFactory] = {}
@@ -16,7 +16,7 @@ LAST_DISCOVERY_ERRORS: list[str] = []
def discover_processors(force_reload: bool = False) -> list[str]: def discover_processors(force_reload: bool = False) -> list[str]:
"""扫描并缓存可用的处理器模块。""" """扫描并缓存可用的处理器模块。"""
from domain.processors import builtin as processors_pkg from . import builtin as processors_pkg
TYPE_MAP.clear() TYPE_MAP.clear()
CONFIG_SCHEMAS.clear() CONFIG_SCHEMAS.clear()
+4 -4
View File
@@ -3,20 +3,20 @@ from typing import List, Tuple
from fastapi import HTTPException from fastapi import HTTPException
from fastapi.concurrency import run_in_threadpool from fastapi.concurrency import run_in_threadpool
from domain.processors.registry import ( from domain.tasks import task_queue_service
from domain.virtual_fs import VirtualFSService
from .registry import (
get, get,
get_config_schema, get_config_schema,
get_config_schemas, get_config_schemas,
get_module_path, get_module_path,
reload_processors, reload_processors,
) )
from domain.processors.types import ( from .types import (
ProcessDirectoryRequest, ProcessDirectoryRequest,
ProcessRequest, ProcessRequest,
UpdateSourceRequest, UpdateSourceRequest,
) )
from domain.virtual_fs.service import VirtualFSService
from domain.tasks.task_queue import task_queue_service
class ProcessorService: class ProcessorService:
+1
View File
@@ -0,0 +1 @@
__all__: list[str] = []
+10
View File
@@ -0,0 +1,10 @@
from .service import ShareService
from .types import ShareCreate, ShareInfo, ShareInfoWithPassword, SharePassword
__all__ = [
"ShareService",
"ShareCreate",
"ShareInfo",
"ShareInfoWithPassword",
"SharePassword",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Depends, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import ShareService
from domain.share.service import ShareService from .types import (
from domain.share.types import (
ShareCreate, ShareCreate,
ShareInfo, ShareInfo,
ShareInfoWithPassword, ShareInfoWithPassword,
+1 -1
View File
@@ -7,7 +7,7 @@ import bcrypt
from fastapi import HTTPException, status from fastapi import HTTPException, status
from fastapi.responses import Response from fastapi.responses import Response
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs import VirtualFSService
from models.database import ShareLink, UserAccount from models.database import ShareLink, UserAccount
+24
View File
@@ -0,0 +1,24 @@
from .service import TaskService
from .task_queue import Task, TaskProgress, TaskStatus, task_queue_service
from .types import (
AutomationTaskBase,
AutomationTaskCreate,
AutomationTaskRead,
AutomationTaskUpdate,
TaskQueueSettings,
TaskQueueSettingsResponse,
)
__all__ = [
"TaskService",
"Task",
"TaskProgress",
"TaskStatus",
"task_queue_service",
"AutomationTaskBase",
"AutomationTaskCreate",
"AutomationTaskRead",
"AutomationTaskUpdate",
"TaskQueueSettings",
"TaskQueueSettingsResponse",
]
+3 -3
View File
@@ -2,9 +2,9 @@ from fastapi import APIRouter, Depends, Request
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import get_current_active_user
from domain.tasks.service import TaskService from .service import TaskService
from domain.tasks.types import ( from .types import (
AutomationTaskCreate, AutomationTaskCreate,
AutomationTaskUpdate, AutomationTaskUpdate,
TaskQueueSettings, TaskQueueSettings,
+4 -5
View File
@@ -3,17 +3,16 @@ from typing import Annotated, Any, Dict, Optional
from fastapi import Depends, HTTPException from fastapi import Depends, HTTPException
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from domain.config import ConfigService
from domain.config.service import ConfigService from .task_queue import task_queue_service
from domain.tasks.types import ( from .types import (
AutomationTaskCreate, AutomationTaskCreate,
AutomationTaskUpdate, AutomationTaskUpdate,
TaskQueueSettings, TaskQueueSettings,
TaskQueueSettingsResponse, TaskQueueSettingsResponse,
) )
from models.database import AutomationTask from models.database import AutomationTask
from domain.tasks.task_queue import task_queue_service
class TaskService: class TaskService:
+6 -6
View File
@@ -74,7 +74,7 @@ class TaskQueueService:
try: try:
# Local import to avoid circular dependency during module load. # Local import to avoid circular dependency during module load.
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs import VirtualFSService
if task.name == "process_file": if task.name == "process_file":
params = task.task_info params = task.task_info
@@ -88,7 +88,7 @@ class TaskQueueService:
task.result = result task.result = result
elif task.name == "automation_task" or self._is_processor_task(task.name): elif task.name == "automation_task" or self._is_processor_task(task.name):
from models.database import AutomationTask from models.database import AutomationTask
from domain.processors.service import get_processor from domain.processors import get_processor
params = task.task_info params = task.task_info
auto_task = await AutomationTask.get(id=params["task_id"]) auto_task = await AutomationTask.get(id=params["task_id"])
@@ -116,7 +116,7 @@ class TaskQueueService:
await VirtualFSService.write_file(save_to, result) await VirtualFSService.write_file(save_to, result)
task.result = "Automation task completed" task.result = "Automation task completed"
elif task.name == "offline_http_download": elif task.name == "offline_http_download":
from domain.offline_downloads.service import OfflineDownloadService from domain.offline_downloads import OfflineDownloadService
result_path = await OfflineDownloadService.run_http_download(task) result_path = await OfflineDownloadService.run_http_download(task)
task.result = {"path": result_path} task.result = {"path": result_path}
@@ -124,7 +124,7 @@ class TaskQueueService:
result = await VirtualFSService.run_cross_mount_transfer_task(task) result = await VirtualFSService.run_cross_mount_transfer_task(task)
task.result = result task.result = result
elif task.name == "send_email": elif task.name == "send_email":
from domain.email.service import EmailService from domain.email import EmailService
await EmailService.send_from_task(task.id, task.task_info) await EmailService.send_from_task(task.id, task.task_info)
task.result = "Email sent" task.result = "Email sent"
else: else:
@@ -141,7 +141,7 @@ class TaskQueueService:
def _is_processor_task(self, task_name: str) -> bool: def _is_processor_task(self, task_name: str) -> bool:
try: try:
from domain.processors.service import get_processor from domain.processors import get_processor
return get_processor(task_name) is not None return get_processor(task_name) is not None
except Exception: except Exception:
@@ -180,7 +180,7 @@ class TaskQueueService:
async def start_worker(self, concurrency: int | None = None): async def start_worker(self, concurrency: int | None = None):
if concurrency is None: if concurrency is None:
from domain.config.service import ConfigService from domain.config import ConfigService
stored_value = await ConfigService.get("TASK_QUEUE_CONCURRENCY", self._concurrency) stored_value = await ConfigService.get("TASK_QUEUE_CONCURRENCY", self._concurrency)
try: try:
+11
View File
@@ -0,0 +1,11 @@
from .service import VirtualFSService
from .types import DirListing, MkdirRequest, MoveRequest, SearchResultItem, VfsEntry
__all__ = [
"VirtualFSService",
"DirListing",
"MkdirRequest",
"MoveRequest",
"SearchResultItem",
"VfsEntry",
]
+3 -4
View File
@@ -4,10 +4,9 @@ from fastapi import APIRouter, Depends, File, Query, Request, UploadFile
from api.response import success from api.response import success
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .service import VirtualFSService
from domain.virtual_fs.service import VirtualFSService from .types import MkdirRequest, MoveRequest
from domain.virtual_fs.types import MkdirRequest, MoveRequest
router = APIRouter(prefix="/api/fs", tags=["virtual-fs"]) router = APIRouter(prefix="/api/fs", tags=["virtual-fs"])
+2 -2
View File
@@ -4,8 +4,8 @@ from typing import Any, AsyncIterator, Union
from fastapi import HTTPException from fastapi import HTTPException
from fastapi.responses import Response from fastapi.responses import Response
from domain.tasks.service import TaskService from domain.tasks import TaskService
from domain.virtual_fs.thumbnail import is_raw_filename, raw_bytes_to_jpeg from .thumbnail import is_raw_filename, raw_bytes_to_jpeg
from .listing import VirtualFSListingMixin from .listing import VirtualFSListingMixin
+3 -3
View File
@@ -3,9 +3,9 @@ from typing import Any, Dict, List, Tuple
from fastapi import HTTPException from fastapi import HTTPException
from api.response import page from api.response import page
from domain.adapters.registry import runtime_registry from domain.adapters import runtime_registry
from domain.ai.service import VectorDBService, VECTOR_COLLECTION_NAME, FILE_COLLECTION_NAME from domain.ai import FILE_COLLECTION_NAME, VECTOR_COLLECTION_NAME, VectorDBService
from domain.virtual_fs.thumbnail import is_image_filename, is_video_filename from .thumbnail import is_image_filename, is_video_filename
from models import StorageAdapter from models import StorageAdapter
from .resolver import VirtualFSResolverMixin from .resolver import VirtualFSResolverMixin
+1
View File
@@ -0,0 +1 @@
__all__: list[str] = []
+2 -2
View File
@@ -15,8 +15,8 @@ from fastapi import APIRouter, Request, Response
from fastapi import HTTPException from fastapi import HTTPException
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.config.service import ConfigService from domain.config import ConfigService
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs import VirtualFSService
router = APIRouter(prefix="/s3", tags=["s3"]) router = APIRouter(prefix="/s3", tags=["s3"])
+3 -4
View File
@@ -9,10 +9,9 @@ from fastapi import APIRouter, Request, Response, HTTPException, Depends
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from domain.audit import AuditAction, audit from domain.audit import AuditAction, audit
from domain.auth.service import AuthService from domain.auth import AuthService, User, UserInDB
from domain.auth.types import User, UserInDB from domain.config import ConfigService
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs import VirtualFSService
from domain.config.service import ConfigService
_WEBDAV_ENABLED_KEY = "WEBDAV_MAPPING_ENABLED" _WEBDAV_ENABLED_KEY = "WEBDAV_MAPPING_ENABLED"
+1 -1
View File
@@ -16,7 +16,7 @@ class VirtualFSProcessingMixin(VirtualFSTransferMixin):
save_to: str | None = None, save_to: str | None = None,
overwrite: bool = False, overwrite: bool = False,
) -> Any: ) -> Any:
from domain.processors.service import get_processor from domain.processors import get_processor
processor = get_processor(processor_type) processor = get_processor(processor_type)
if not processor: if not processor:
+1 -1
View File
@@ -3,7 +3,7 @@ from typing import Tuple
from fastapi import HTTPException from fastapi import HTTPException
from fastapi.responses import Response from fastapi.responses import Response
from domain.adapters.registry import runtime_registry from domain.adapters import runtime_registry
from models import StorageAdapter from models import StorageAdapter
from .common import VirtualFSCommonMixin from .common import VirtualFSCommonMixin
+2 -2
View File
@@ -4,8 +4,8 @@ import re
from fastapi import HTTPException, UploadFile from fastapi import HTTPException, UploadFile
from fastapi.responses import Response from fastapi.responses import Response
from domain.config.service import ConfigService from domain.config import ConfigService
from domain.virtual_fs.thumbnail import ( from .thumbnail import (
get_or_create_thumb, get_or_create_thumb,
is_image_filename, is_image_filename,
is_raw_filename, is_raw_filename,
+3
View File
@@ -0,0 +1,3 @@
from .search_service import VirtualFSSearchService
__all__ = ["VirtualFSSearchService"]
+2 -3
View File
@@ -1,9 +1,8 @@
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from api.response import success from api.response import success
from domain.auth.service import get_current_active_user from domain.auth import User, get_current_active_user
from domain.auth.types import User from .search_service import VirtualFSSearchService
from domain.virtual_fs.search.search_service import VirtualFSSearchService
router = APIRouter(prefix="/api/fs/search", tags=["search"]) router = APIRouter(prefix="/api/fs/search", tags=["search"])
+2 -3
View File
@@ -1,8 +1,7 @@
from typing import Any, Dict, List, Tuple from typing import Any, Dict, List, Tuple
from domain.virtual_fs.types import SearchResultItem from domain.ai import FILE_COLLECTION_NAME, VECTOR_COLLECTION_NAME, VectorDBService, get_text_embedding
from domain.ai.inference import get_text_embedding from ..types import SearchResultItem
from domain.ai.service import VectorDBService, VECTOR_COLLECTION_NAME, FILE_COLLECTION_NAME
def _normalize_result(raw: Dict[str, Any], source: str, fallback_score: float = 0.0) -> SearchResultItem: def _normalize_result(raw: Dict[str, Any], source: str, fallback_score: float = 0.0) -> SearchResultItem:
+1 -1
View File
@@ -5,7 +5,7 @@ import time
from fastapi import HTTPException from fastapi import HTTPException
from domain.config.service import ConfigService from domain.config import ConfigService
from .processing import VirtualFSProcessingMixin from .processing import VirtualFSProcessingMixin
+2 -2
View File
@@ -273,7 +273,7 @@ class VirtualFSTransferMixin(VirtualFSFileOpsMixin):
"overwrite": overwrite, "overwrite": overwrite,
} }
from domain.tasks.task_queue import task_queue_service from domain.tasks import task_queue_service
task = await task_queue_service.add_task("cross_mount_transfer", payload) task = await task_queue_service.add_task("cross_mount_transfer", payload)
return { return {
@@ -286,7 +286,7 @@ class VirtualFSTransferMixin(VirtualFSFileOpsMixin):
@classmethod @classmethod
async def run_cross_mount_transfer_task(cls, task: "Task") -> Dict[str, Any]: async def run_cross_mount_transfer_task(cls, task: "Task") -> Dict[str, Any]:
from domain.tasks.task_queue import task_queue_service from domain.tasks import task_queue_service
params = task.task_info or {} params = task.task_info or {}
operation = params.get("operation") operation = params.get("operation")
+4 -4
View File
@@ -2,8 +2,8 @@ import os
from pathlib import Path from pathlib import Path
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from domain.config.service import ConfigService, VERSION from domain.adapters import runtime_registry
from domain.adapters.registry import runtime_registry from domain.config import ConfigService, VERSION
from db.session import close_db, init_db from db.session import close_db, init_db
from api.routers import include_routers from api.routers import include_routers
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
@@ -19,7 +19,7 @@ from middleware.exception_handler import (
) )
import httpx import httpx
from dotenv import load_dotenv from dotenv import load_dotenv
from domain.tasks.task_queue import task_queue_service from domain.tasks import task_queue_service
load_dotenv() load_dotenv()
@@ -59,7 +59,7 @@ async def lifespan(app: FastAPI):
await task_queue_service.start_worker() await task_queue_service.start_worker()
# 加载已安装的插件 # 加载已安装的插件
from domain.plugins.startup import init_plugins from domain.plugins import init_plugins
await init_plugins(app) await init_plugins(app)
# 在所有路由加载完成后,挂载静态文件服务(放在最后以避免覆盖 API 路由) # 在所有路由加载完成后,挂载静态文件服务(放在最后以避免覆盖 API 路由)
+2 -2
View File
@@ -13,8 +13,8 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path: if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT)) sys.path.insert(0, str(PROJECT_ROOT))
from domain.auth.service import get_password_hash from domain.config import VERSION
from domain.config.service import VERSION from domain.auth import get_password_hash
def _project_root() -> Path: def _project_root() -> Path: