remove /api support

This commit is contained in:
2026-07-31 14:19:48 +08:00
parent 46c69607c5
commit 6330b99860
11 changed files with 75 additions and 67 deletions
@@ -22,7 +22,7 @@ test("panel model kernel renames, overwrites, lists both names, and deletes on t
}); });
const credentials = { const credentials = {
apiUrl: `http://127.0.0.1:${server.address().port}/api`, apiUrl: `http://127.0.0.1:${server.address().port}`,
adminToken: "test-token" adminToken: "test-token"
}; };
const handlers = createModelHandlers({ callServer }); const handlers = createModelHandlers({ callServer });
+2 -2
View File
@@ -49,14 +49,14 @@ $env:B_ADMIN_TOKEN="your-admin-token"
npm start npm start
``` ```
默认服务地址为 `http://127.0.0.1:3000`,健康检查为 `http://127.0.0.1:3000/health`。业务请求使用根路径`/api`。跨设备部署时,应使用服务器局域网 IP 或 HTTPS 域名,而不是 `127.0.0.1` 默认服务地址为 `http://127.0.0.1:3000`,健康检查为 `http://127.0.0.1:3000/health`。业务请求统一使用根路径。跨设备部署时,应使用服务器局域网 IP 或 HTTPS 域名,而不是 `127.0.0.1`
### ControlPanel ### ControlPanel
```powershell ```powershell
cd ControlPanel cd ControlPanel
npm install npm install
$env:REINLOOP_API_URL="http://server-address:3000/api" $env:REINLOOP_API_URL="http://server-address:3000"
$env:B_ADMIN_TOKEN="your-admin-token" $env:B_ADMIN_TOKEN="your-admin-token"
npm run gui npm run gui
``` ```
+1 -1
View File
@@ -11,7 +11,7 @@ base_url = os.environ.get(
).rstrip("/") ).rstrip("/")
server_api_url = os.environ.get( server_api_url = os.environ.get(
"REINLOOP_API_URL", "REINLOOP_API_URL",
f"{base_url}/api", base_url,
) )
# Compatibility alias used by existing modules. It points to the ReinLoop # Compatibility alias used by existing modules. It points to the ReinLoop
# Express server API, not a cloud-function endpoint. # Express server API, not a cloud-function endpoint.
+4 -4
View File
@@ -5,11 +5,11 @@
## 约定 ## 约定
- 业务服务端地址由 `REINLOOP_API_URL` 指定,未设置时使用 - 业务服务端地址由 `REINLOOP_API_URL` 指定,未设置时使用
`REINLOOP_SERVER_URL + /api`;默认地址为 `REINLOOP_SERVER_URL`;默认地址为
`https://ReinLoop.dominatedconvergence.com/api` `https://ReinLoop.dominatedconvergence.com`
- 新许可证的设备标识为 `<company_code>/<production_line_code>`,在代码中通过 - 新许可证的设备标识为 `<company_code>/<production_line_code>`,在代码中通过
`api.the_folder` 使用。旧许可证才回退到 `REINLOOP_DEVICE_ID` `api.the_folder` 使用。旧许可证才回退到 `REINLOOP_DEVICE_ID`
- 所有服务端业务请求均使用 `POST /api`,通过请求体的 `type` 字段分发。 - 所有服务端业务请求均使用 `POST /`,通过请求体的 `type` 字段分发。
- 公司、产线、许可证签发/撤销、模型删除、审核反馈及配置提交均为 - 公司、产线、许可证签发/撤销、模型删除、审核反馈及配置提交均为
ControlPanel 管理端能力,客户端不提供对应的管理接口。旧微信云函数和其管理脚本 ControlPanel 管理端能力,客户端不提供对应的管理接口。旧微信云函数和其管理脚本
已移除。 已移除。
@@ -148,7 +148,7 @@ RL 模式下,`ControlEngine` 使用模型根据流量、当前压力和压力
## 客户端服务端协议 ## 客户端服务端协议
所有业务请求都发送至 `POST /api`。业务成功响应应至少包含 `success: true` 所有业务请求都发送至 `POST /`。业务成功响应应至少包含 `success: true`
| `type` | 请求关键字段 | 用途 | | `type` | 请求关键字段 | 用途 |
| --- | --- | --- | | --- | --- | --- |
+1 -1
View File
@@ -276,7 +276,7 @@ def _api_url():
base_url = os.environ.get( base_url = os.environ.get(
"REINLOOP_SERVER_URL", "https://ReinLoop.dominatedconvergence.com" "REINLOOP_SERVER_URL", "https://ReinLoop.dominatedconvergence.com"
).rstrip("/") ).rstrip("/")
return os.environ.get("REINLOOP_API_URL", f"{base_url}/api") return os.environ.get("REINLOOP_API_URL", base_url)
def _offline_limit_seconds(): def _offline_limit_seconds():
+1 -1
View File
@@ -14,7 +14,7 @@ MODULE_PATH = Path(__file__).resolve().parents[1] / "core" / "data_collector.py"
def load_data_collector_module(): def load_data_collector_module():
api = types.ModuleType("api") api = types.ModuleType("api")
api.base_url = "https://cloud.example" api.base_url = "https://cloud.example"
api.data_record_url = "https://cloud.example/api" api.data_record_url = "https://cloud.example"
api.the_folder = "customer-a/line-1" api.the_folder = "customer-a/line-1"
requests = types.ModuleType("requests") requests = types.ModuleType("requests")
+2 -2
View File
@@ -30,12 +30,12 @@ class DeviceHeartbeatTests(unittest.TestCase):
requests_module.post = post requests_module.post = post
api_module = types.ModuleType("api") api_module = types.ModuleType("api")
api_module.data_record_url = "https://server.example/api" api_module.data_record_url = "https://server.example"
api_module.the_folder = "company/line" api_module.the_folder = "company/line"
with patch.dict(sys.modules, {"requests": requests_module, "api": api_module}): with patch.dict(sys.modules, {"requests": requests_module, "api": api_module}):
timestamp = HEARTBEAT.heartbeat_device(timeout=7) timestamp = HEARTBEAT.heartbeat_device(timeout=7)
self.assertEqual(timestamp, "2026-07-28T00:00:00.000Z") self.assertEqual(timestamp, "2026-07-28T00:00:00.000Z")
self.assertEqual(calls, [("https://server.example/api", { self.assertEqual(calls, [("https://server.example", {
"type": "deviceHeartbeat", "deviceId": "company/line" "type": "deviceHeartbeat", "deviceId": "company/line"
}, 7)]) }, 7)])
+2 -2
View File
@@ -2,7 +2,7 @@
## 通用约定 ## 通用约定
业务接口为 `POST /api`。请求与响应均为 JSON,响应包含 `success` 业务接口为 `POST /`。请求与响应均为 JSON,响应包含 `success`
标注为 Admin 的接口需要附加: 标注为 Admin 的接口需要附加:
@@ -16,7 +16,7 @@
| 方法 | 路径 | 用途 | | 方法 | 路径 | 用途 |
| --- | --- | --- | | --- | --- | --- |
| `POST` | `/api` | 主业务 API | | `POST` | `/` | 主业务 API |
| `POST` | `/upload/:token` | 根据上传凭证接收 multipart 文件 | | `POST` | `/upload/:token` | 根据上传凭证接收 multipart 文件 |
| `GET` | `/files/:fileID` | 下载已存储文件 | | `GET` | `/files/:fileID` | 下载已存储文件 |
| `GET` | `/health` | 服务存活检查 | | `GET` | `/health` | 服务存活检查 |
-1
View File
@@ -1111,7 +1111,6 @@ function createApp({
} }
}; };
app.post("/", apiHandler); app.post("/", apiHandler);
app.post("/api", apiHandler);
app.use((error, req, res, next) => { app.use((error, req, res, next) => {
console.error(error); console.error(error);
+1 -1
View File
@@ -39,7 +39,7 @@ async function main() {
purgeTimer.unref(); purgeTimer.unref();
app.listen(port, host, () => { app.listen(port, host, () => {
console.log(`ReinLoop server listening on http://${host}:${port}`); console.log(`ReinLoop server listening on http://${host}:${port}`);
console.log(`API endpoints: http://${host}:${port} and http://${host}:${port}/api`); console.log(`API endpoint: http://${host}:${port}`);
console.log(`Data directory: ${dataDirectory}`); console.log(`Data directory: ${dataDirectory}`);
console.log(`Metadata store: ${process.env.DATABASE_URL ? "PostgreSQL" : "local JSON"}`); console.log(`Metadata store: ${process.env.DATABASE_URL ? "PostgreSQL" : "local JSON"}`);
}); });
+11 -2
View File
@@ -15,7 +15,7 @@ let licensePrivateKey;
let licensePublicKeyPath; let licensePublicKeyPath;
async function post(payload) { async function post(payload) {
const response = await fetch(`${baseUrl}/api`, { const response = await fetch(baseUrl, {
method: "POST", method: "POST",
headers: { "content-type": "application/json" }, headers: { "content-type": "application/json" },
body: JSON.stringify(payload) body: JSON.stringify(payload)
@@ -59,7 +59,7 @@ test("health endpoint reports ready", async () => {
assert.deepEqual(await response.json(), { success: true, service: "reinloop-server" }); assert.deepEqual(await response.json(), { success: true, service: "reinloop-server" });
}); });
test("root endpoint accepts API requests without the /api suffix", async () => { test("root endpoint accepts API requests", async () => {
const response = await fetch(baseUrl, { const response = await fetch(baseUrl, {
method: "POST", method: "POST",
headers: { "content-type": "application/json" }, headers: { "content-type": "application/json" },
@@ -69,6 +69,15 @@ test("root endpoint accepts API requests without the /api suffix", async () => {
assert.deepEqual(await response.json(), { success: true, companies: [] }); assert.deepEqual(await response.json(), { success: true, companies: [] });
}); });
test("legacy /api endpoint is unavailable", async () => {
const response = await fetch(`${baseUrl}/api`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ type: "listOrganizations", adminToken: "test-token" })
});
assert.equal(response.status, 404);
});
test("legacy data_record endpoint and uploadUserInfo type are unavailable", async () => { test("legacy data_record endpoint and uploadUserInfo type are unavailable", async () => {
const legacyRoute = await fetch(`${baseUrl}/data_record`, { const legacyRoute = await fetch(`${baseUrl}/data_record`, {
method: "POST", method: "POST",