add docs
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# Reverse-proxy subpath, e.g. /openocr when served at https://example.com/openocr/
|
||||
OPENOCR_ROOT_PATH=
|
||||
OPENOCR_OUTPUT_DIR=data/output
|
||||
OPENOCR_UPLOAD_DIR=data/uploads
|
||||
OPENOCR_USE_GPU=false
|
||||
|
||||
@@ -163,6 +163,7 @@ docker compose up -d --build # 重新构建并启动
|
||||
|
||||
| 变量 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `OPENOCR_ROOT_PATH` | *(空)* | 反向代理子路径,如 `/openocr` |
|
||||
| `OPENOCR_OUTPUT_DIR` | `data/output` | 解析结果输出目录 |
|
||||
| `OPENOCR_UPLOAD_DIR` | `data/uploads` | 上传文件临时目录 |
|
||||
| `OPENOCR_USE_GPU` | `false` | 是否使用 GPU |
|
||||
@@ -191,6 +192,35 @@ docker compose up -d --build # 重新构建并启动
|
||||
└── .env.example
|
||||
```
|
||||
|
||||
## 反向代理(Nginx 子路径)
|
||||
|
||||
若通过 `https://jsuse.com/openocr/` 这类**子路径**访问(而非直接 `:8000`),必须在 `.env` 中设置:
|
||||
|
||||
```env
|
||||
OPENOCR_ROOT_PATH=/openocr
|
||||
```
|
||||
|
||||
否则 `/health` 可能正常,但 `/docs` 会报 **Failed to load API definition**——因为 Swagger 会去根路径请求 `/openapi.json`,而不是 `/openocr/openapi.json`。
|
||||
|
||||
Nginx 参考配置:
|
||||
|
||||
```nginx
|
||||
location /openocr/ {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
|
||||
修改后重启容器:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- `.env`、`data/`、模型缓存目录已在 `.gitignore` 中排除,不会进入 Git
|
||||
|
||||
+17
-10
@@ -36,7 +36,7 @@ def create_app() -> FastAPI:
|
||||
settings.output_dir.mkdir(parents=True, exist_ok=True)
|
||||
settings.upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
app = FastAPI(title=settings.app_name)
|
||||
app = FastAPI(title=settings.app_name, root_path=settings.root_path or "")
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origin_list,
|
||||
@@ -51,7 +51,7 @@ def create_app() -> FastAPI:
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def root() -> RedirectResponse:
|
||||
return RedirectResponse(url="/docs")
|
||||
return RedirectResponse(url=settings.url_path("/docs"))
|
||||
|
||||
@app.get("/health")
|
||||
async def health() -> dict[str, str]:
|
||||
@@ -71,9 +71,15 @@ def create_app() -> FastAPI:
|
||||
items.append(
|
||||
HistoryItem(
|
||||
id=item_dir.name,
|
||||
markdown_url=f"/output/{item_dir.name}/{markdown_file.name}",
|
||||
json_url=f"/output/{item_dir.name}/{json_file.name}" if json_file else None,
|
||||
source_url=f"/output/{item_dir.name}/{source_file.name}" if source_file else None,
|
||||
markdown_url=settings.url_path(
|
||||
f"/output/{item_dir.name}/{markdown_file.name}"
|
||||
),
|
||||
json_url=settings.url_path(f"/output/{item_dir.name}/{json_file.name}")
|
||||
if json_file
|
||||
else None,
|
||||
source_url=settings.url_path(f"/output/{item_dir.name}/{source_file.name}")
|
||||
if source_file
|
||||
else None,
|
||||
updated_at=markdown_file.stat().st_mtime,
|
||||
)
|
||||
)
|
||||
@@ -104,8 +110,8 @@ def create_app() -> FastAPI:
|
||||
id=doc_id,
|
||||
status="success",
|
||||
message="Existing result reused. Set force=true to re-run.",
|
||||
markdown_url=f"/output/{doc_id}/{markdown_file.name}",
|
||||
json_url=f"/output/{doc_id}/{doc_id}.json",
|
||||
markdown_url=settings.url_path(f"/output/{doc_id}/{markdown_file.name}"),
|
||||
json_url=settings.url_path(f"/output/{doc_id}/{doc_id}.json"),
|
||||
)
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -130,9 +136,9 @@ def create_app() -> FastAPI:
|
||||
id=doc_id,
|
||||
status="success",
|
||||
message=f"Parsed successfully in {elapsed}s.",
|
||||
markdown_url=f"/output/{doc_id}/{doc_id}.md",
|
||||
json_url=f"/output/{doc_id}/{doc_id}.json",
|
||||
source_url=f"/output/{doc_id}/{source_path.name}",
|
||||
markdown_url=settings.url_path(f"/output/{doc_id}/{doc_id}.md"),
|
||||
json_url=settings.url_path(f"/output/{doc_id}/{doc_id}.json"),
|
||||
source_url=settings.url_path(f"/output/{doc_id}/{source_path.name}"),
|
||||
)
|
||||
|
||||
app.mount("/output", StaticFiles(directory=settings.output_dir), name="output")
|
||||
@@ -149,6 +155,7 @@ def main() -> None:
|
||||
port=8000,
|
||||
reload=False,
|
||||
log_level=settings.log_level,
|
||||
root_path=settings.root_path or "",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||||
|
||||
app_name: str = "OpenOCR Markdown Service"
|
||||
root_path: str = Field(default="", alias="OPENOCR_ROOT_PATH")
|
||||
output_dir: Path = Field(default=Path("data/output"), alias="OPENOCR_OUTPUT_DIR")
|
||||
upload_dir: Path = Field(default=Path("data/uploads"), alias="OPENOCR_UPLOAD_DIR")
|
||||
use_gpu: bool = Field(default=False, alias="OPENOCR_USE_GPU")
|
||||
@@ -29,5 +30,12 @@ class Settings(BaseSettings):
|
||||
return ["*"]
|
||||
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
|
||||
|
||||
def url_path(self, path: str) -> str:
|
||||
"""Build a public URL path, honoring reverse-proxy root prefix."""
|
||||
prefix = self.root_path.rstrip("/")
|
||||
if not prefix:
|
||||
return path
|
||||
return f"{prefix}{path}"
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
Reference in New Issue
Block a user