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_OUTPUT_DIR=data/output
|
||||||
OPENOCR_UPLOAD_DIR=data/uploads
|
OPENOCR_UPLOAD_DIR=data/uploads
|
||||||
OPENOCR_USE_GPU=false
|
OPENOCR_USE_GPU=false
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ docker compose up -d --build # 重新构建并启动
|
|||||||
|
|
||||||
| 变量 | 默认值 | 说明 |
|
| 变量 | 默认值 | 说明 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
|
| `OPENOCR_ROOT_PATH` | *(空)* | 反向代理子路径,如 `/openocr` |
|
||||||
| `OPENOCR_OUTPUT_DIR` | `data/output` | 解析结果输出目录 |
|
| `OPENOCR_OUTPUT_DIR` | `data/output` | 解析结果输出目录 |
|
||||||
| `OPENOCR_UPLOAD_DIR` | `data/uploads` | 上传文件临时目录 |
|
| `OPENOCR_UPLOAD_DIR` | `data/uploads` | 上传文件临时目录 |
|
||||||
| `OPENOCR_USE_GPU` | `false` | 是否使用 GPU |
|
| `OPENOCR_USE_GPU` | `false` | 是否使用 GPU |
|
||||||
@@ -191,6 +192,35 @@ docker compose up -d --build # 重新构建并启动
|
|||||||
└── .env.example
|
└── .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
|
- `.env`、`data/`、模型缓存目录已在 `.gitignore` 中排除,不会进入 Git
|
||||||
|
|||||||
+17
-10
@@ -36,7 +36,7 @@ def create_app() -> FastAPI:
|
|||||||
settings.output_dir.mkdir(parents=True, exist_ok=True)
|
settings.output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
settings.upload_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(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=settings.cors_origin_list,
|
allow_origins=settings.cors_origin_list,
|
||||||
@@ -51,7 +51,7 @@ def create_app() -> FastAPI:
|
|||||||
|
|
||||||
@app.get("/", include_in_schema=False)
|
@app.get("/", include_in_schema=False)
|
||||||
async def root() -> RedirectResponse:
|
async def root() -> RedirectResponse:
|
||||||
return RedirectResponse(url="/docs")
|
return RedirectResponse(url=settings.url_path("/docs"))
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
async def health() -> dict[str, str]:
|
async def health() -> dict[str, str]:
|
||||||
@@ -71,9 +71,15 @@ def create_app() -> FastAPI:
|
|||||||
items.append(
|
items.append(
|
||||||
HistoryItem(
|
HistoryItem(
|
||||||
id=item_dir.name,
|
id=item_dir.name,
|
||||||
markdown_url=f"/output/{item_dir.name}/{markdown_file.name}",
|
markdown_url=settings.url_path(
|
||||||
json_url=f"/output/{item_dir.name}/{json_file.name}" if json_file else None,
|
f"/output/{item_dir.name}/{markdown_file.name}"
|
||||||
source_url=f"/output/{item_dir.name}/{source_file.name}" if source_file else None,
|
),
|
||||||
|
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,
|
updated_at=markdown_file.stat().st_mtime,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -104,8 +110,8 @@ def create_app() -> FastAPI:
|
|||||||
id=doc_id,
|
id=doc_id,
|
||||||
status="success",
|
status="success",
|
||||||
message="Existing result reused. Set force=true to re-run.",
|
message="Existing result reused. Set force=true to re-run.",
|
||||||
markdown_url=f"/output/{doc_id}/{markdown_file.name}",
|
markdown_url=settings.url_path(f"/output/{doc_id}/{markdown_file.name}"),
|
||||||
json_url=f"/output/{doc_id}/{doc_id}.json",
|
json_url=settings.url_path(f"/output/{doc_id}/{doc_id}.json"),
|
||||||
)
|
)
|
||||||
|
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -130,9 +136,9 @@ def create_app() -> FastAPI:
|
|||||||
id=doc_id,
|
id=doc_id,
|
||||||
status="success",
|
status="success",
|
||||||
message=f"Parsed successfully in {elapsed}s.",
|
message=f"Parsed successfully in {elapsed}s.",
|
||||||
markdown_url=f"/output/{doc_id}/{doc_id}.md",
|
markdown_url=settings.url_path(f"/output/{doc_id}/{doc_id}.md"),
|
||||||
json_url=f"/output/{doc_id}/{doc_id}.json",
|
json_url=settings.url_path(f"/output/{doc_id}/{doc_id}.json"),
|
||||||
source_url=f"/output/{doc_id}/{source_path.name}",
|
source_url=settings.url_path(f"/output/{doc_id}/{source_path.name}"),
|
||||||
)
|
)
|
||||||
|
|
||||||
app.mount("/output", StaticFiles(directory=settings.output_dir), name="output")
|
app.mount("/output", StaticFiles(directory=settings.output_dir), name="output")
|
||||||
@@ -149,6 +155,7 @@ def main() -> None:
|
|||||||
port=8000,
|
port=8000,
|
||||||
reload=False,
|
reload=False,
|
||||||
log_level=settings.log_level,
|
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")
|
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
||||||
|
|
||||||
app_name: str = "OpenOCR Markdown Service"
|
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")
|
output_dir: Path = Field(default=Path("data/output"), alias="OPENOCR_OUTPUT_DIR")
|
||||||
upload_dir: Path = Field(default=Path("data/uploads"), alias="OPENOCR_UPLOAD_DIR")
|
upload_dir: Path = Field(default=Path("data/uploads"), alias="OPENOCR_UPLOAD_DIR")
|
||||||
use_gpu: bool = Field(default=False, alias="OPENOCR_USE_GPU")
|
use_gpu: bool = Field(default=False, alias="OPENOCR_USE_GPU")
|
||||||
@@ -29,5 +30,12 @@ class Settings(BaseSettings):
|
|||||||
return ["*"]
|
return ["*"]
|
||||||
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
|
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()
|
settings = Settings()
|
||||||
|
|||||||
Reference in New Issue
Block a user