# Jlocal 路由机制

## 概述

Jlocal 基于 FastAPI 实现路由系统，采用**静态路由 + 动态路由**双层架构：

```
请求 → FastAPI
         ↓
    ┌────┴────┐
    ↓         ↓
静态路由    动态路由
(api/*)   (whitelist)
    ↓         ↓
本地处理   白名单判断
```

## 路由注册顺序

```python
# main.py

def create_app():
    # 1. 自动注册 api 目录下的静态路由
    include_routers_from_package(app, "jingrow.api")
    
    # 2. 手动注册动态路由（最后注册，避免冲突）
    app.include_router(router)  # whitelist.py 的 router
```

**重要**：注册顺序决定路由匹配优先级，静态路由优先于动态路由。

## 一、静态路由（api 目录）

### 自动注册机制

`jingrow/api/` 目录下的模块会被自动扫描注册：

```python
# utils/router_auto_register.py

def include_routers_from_package(app: FastAPI, package: str):
    pkg = importlib.import_module(package)
    modules = list(pkgutil.iter_modules(pkg.__path__, package + "."))
    
    for module_info in modules:
        mod = importlib.import_module(module_info.name)
        router = getattr(mod, "router", None)
        if router is not None:
            app.include_router(router)
```

### 模块规范

每个路由模块必须：

1. 定义 `router = APIRouter()` 变量
2. 使用明确的路由路径（避免通配符冲突）

```python
# api/auth_api.py 示例

router = APIRouter()

@router.post("/api/action/login")
async def login_route(login_data: LoginRequest):
    # 本地处理登录逻辑
    result = login(login_data.username, login_data.password)
    return create_response_with_cookie(result, result.get("session_cookie"))
```

## 二、动态路由（whitelist）

### 路由定义

```python
# services/whitelist.py

router = APIRouter()  # 无前缀

@router.api_route("/{module_path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def handle_request(request: Request, module_path: str):
    # 验证格式：必须包含点（如 jingrow.sessions.get_boot_info）
    if '.' not in module_path:
        raise HTTPException(status_code=404, detail="Not Found")
    
    return await _process_whitelist_call(request, module_path)
```

### 处理流程

```
请求: /jingrow.sessions.get_boot_info
           ↓
    动态导入模块
           ↓
    检查白名单装饰器
       ↙        ↘
     有          无
      ↓           ↓
   本地执行     返回 404
```

### 白名单装饰器

```python
# 在任意模块中使用

import jingrow

@jingrow.whitelist()
def local_method(arg1, arg2):
    """需要登录才能调用"""
    return {"result": "data"}

@jingrow.whitelist(allow_guest=True)
def public_method():
    """允许访客调用"""
    return {"result": "public"}

@jingrow.whitelist(methods=["POST"])
def post_only_method():
    """只允许 POST 请求"""
    return {"result": "created"}
```

## 四、开发指南

### 添加白名单函数

1. 在任意模块中定义函数
2. 添加 `@jingrow.whitelist()` 装饰器
3. 确保模块被导入（在 main.py 或其他入口导入）

```python
# services/my_service.py

import jingrow

@jingrow.whitelist()
def my_rpc_method(param1: str):
    return {"result": param1}
```

### 路由冲突处理

1. 静态路由优先于动态路由
2. 精确匹配优先于通配符
3. 后注册的路由不会覆盖先注册的

## 五、注意事项

1. **不要在 whitelist.py 中添加业务逻辑**，它只负责路由分发
2. **本地特有功能使用 api 目录**，自动注册
3. **需要动态 RPC 调用使用白名单装饰器**
4. **路由路径要明确**，避免通配符冲突

## 六、SaaS 端转发

对于 SaaS 端已有的 API，需要在本地添加代理路由：

### 方式一：在 api 目录添加代理路由

```python
# api/data.py - 代理 /api/data/* 到 SaaS 端

from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse
import requests
from jingrow.config import Config

router = APIRouter(prefix="/api/data")

@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
async def proxy_data(request: Request, path: str):
    url = f"{Config.jingrow_server_url}/api/data/{path}"
    # 转发请求到 SaaS 端
    resp = requests.get(url, headers={...}, timeout=30)
    return JSONResponse(content=resp.json(), status_code=resp.status_code)
```



### 常见需要转发的路由

| 路由 | 说明 |
|------|------|
| `/api/data/*` | CRUD 数据操作 |
| `/api/action/jingrow.sessions.*` | 会话相关 |
| `/api/action/jingrow.client.*` | 客户端数据获取 |
| `/api/action/jingrow.realtime.*` | 实时通信 |