remove /api support
This commit is contained in:
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
## 通用约定
|
||||
|
||||
业务接口为 `POST /api`。请求与响应均为 JSON,响应包含 `success`。
|
||||
业务接口为 `POST /`。请求与响应均为 JSON,响应包含 `success`。
|
||||
|
||||
标注为 Admin 的接口需要附加:
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
| 方法 | 路径 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api` | 主业务 API |
|
||||
| `POST` | `/` | 主业务 API |
|
||||
| `POST` | `/upload/:token` | 根据上传凭证接收 multipart 文件 |
|
||||
| `GET` | `/files/:fileID` | 下载已存储文件 |
|
||||
| `GET` | `/health` | 服务存活检查 |
|
||||
|
||||
@@ -1111,7 +1111,6 @@ function createApp({
|
||||
}
|
||||
};
|
||||
app.post("/", apiHandler);
|
||||
app.post("/api", apiHandler);
|
||||
|
||||
app.use((error, req, res, next) => {
|
||||
console.error(error);
|
||||
|
||||
+50
-50
@@ -1,51 +1,51 @@
|
||||
const path = require("node:path");
|
||||
const fs = require("node:fs");
|
||||
const { createApp } = require("./app");
|
||||
const { JsonStore } = require("./store");
|
||||
const { PostgresStore } = require("./postgres-store");
|
||||
|
||||
const host = process.env.HOST || "127.0.0.1";
|
||||
const port = Number(process.env.PORT || 3000);
|
||||
const identificationPurgeIntervalMs = Number(process.env.IDENTIFICATION_PURGE_INTERVAL_MS || 60 * 60 * 1000);
|
||||
const dataDirectory = path.resolve(process.env.DATA_DIR || path.join(__dirname, "..", "data"));
|
||||
|
||||
async function main() {
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("PORT 必须是有效端口号");
|
||||
const jsonDatabasePath = path.join(dataDirectory, "database.json");
|
||||
const hasExistingJsonStore = await fs.promises.access(jsonDatabasePath).then(() => true, () => false);
|
||||
if (process.env.NODE_ENV === "production" && !process.env.DATABASE_URL && !hasExistingJsonStore) {
|
||||
throw new Error("生产环境必须配置 DATABASE_URL");
|
||||
}
|
||||
if (process.env.NODE_ENV === "production" && !process.env.DATABASE_URL) {
|
||||
console.warn("警告: 正在使用既有 JSON 数据库;请尽快迁移到 PostgreSQL");
|
||||
}
|
||||
const filesDirectory = path.join(dataDirectory, "files");
|
||||
await fs.promises.mkdir(filesDirectory, { recursive: true });
|
||||
const store = process.env.DATABASE_URL
|
||||
? new PostgresStore(process.env.DATABASE_URL, filesDirectory)
|
||||
: new JsonStore(dataDirectory);
|
||||
await store.initialize();
|
||||
const app = createApp({ store });
|
||||
const purgeIdentificationFiles = async () => {
|
||||
try {
|
||||
const purged = await app.locals.purgeExpiredIdentificationFiles();
|
||||
if (purged) console.info(`[retention] purged ${purged} expired identification file(s)`);
|
||||
} catch (error) {
|
||||
console.error("[retention] identification file purge failed", error);
|
||||
}
|
||||
};
|
||||
await purgeIdentificationFiles();
|
||||
const purgeTimer = setInterval(purgeIdentificationFiles, identificationPurgeIntervalMs);
|
||||
purgeTimer.unref();
|
||||
app.listen(port, host, () => {
|
||||
console.log(`ReinLoop server listening on http://${host}:${port}`);
|
||||
console.log(`API endpoints: http://${host}:${port} and http://${host}:${port}/api`);
|
||||
console.log(`Data directory: ${dataDirectory}`);
|
||||
console.log(`Metadata store: ${process.env.DATABASE_URL ? "PostgreSQL" : "local JSON"}`);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error.message);
|
||||
process.exitCode = 1;
|
||||
const path = require("node:path");
|
||||
const fs = require("node:fs");
|
||||
const { createApp } = require("./app");
|
||||
const { JsonStore } = require("./store");
|
||||
const { PostgresStore } = require("./postgres-store");
|
||||
|
||||
const host = process.env.HOST || "127.0.0.1";
|
||||
const port = Number(process.env.PORT || 3000);
|
||||
const identificationPurgeIntervalMs = Number(process.env.IDENTIFICATION_PURGE_INTERVAL_MS || 60 * 60 * 1000);
|
||||
const dataDirectory = path.resolve(process.env.DATA_DIR || path.join(__dirname, "..", "data"));
|
||||
|
||||
async function main() {
|
||||
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("PORT 必须是有效端口号");
|
||||
const jsonDatabasePath = path.join(dataDirectory, "database.json");
|
||||
const hasExistingJsonStore = await fs.promises.access(jsonDatabasePath).then(() => true, () => false);
|
||||
if (process.env.NODE_ENV === "production" && !process.env.DATABASE_URL && !hasExistingJsonStore) {
|
||||
throw new Error("生产环境必须配置 DATABASE_URL");
|
||||
}
|
||||
if (process.env.NODE_ENV === "production" && !process.env.DATABASE_URL) {
|
||||
console.warn("警告: 正在使用既有 JSON 数据库;请尽快迁移到 PostgreSQL");
|
||||
}
|
||||
const filesDirectory = path.join(dataDirectory, "files");
|
||||
await fs.promises.mkdir(filesDirectory, { recursive: true });
|
||||
const store = process.env.DATABASE_URL
|
||||
? new PostgresStore(process.env.DATABASE_URL, filesDirectory)
|
||||
: new JsonStore(dataDirectory);
|
||||
await store.initialize();
|
||||
const app = createApp({ store });
|
||||
const purgeIdentificationFiles = async () => {
|
||||
try {
|
||||
const purged = await app.locals.purgeExpiredIdentificationFiles();
|
||||
if (purged) console.info(`[retention] purged ${purged} expired identification file(s)`);
|
||||
} catch (error) {
|
||||
console.error("[retention] identification file purge failed", error);
|
||||
}
|
||||
};
|
||||
await purgeIdentificationFiles();
|
||||
const purgeTimer = setInterval(purgeIdentificationFiles, identificationPurgeIntervalMs);
|
||||
purgeTimer.unref();
|
||||
app.listen(port, host, () => {
|
||||
console.log(`ReinLoop server listening on http://${host}:${port}`);
|
||||
console.log(`API endpoint: http://${host}:${port}`);
|
||||
console.log(`Data directory: ${dataDirectory}`);
|
||||
console.log(`Metadata store: ${process.env.DATABASE_URL ? "PostgreSQL" : "local JSON"}`);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error.message);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -15,7 +15,7 @@ let licensePrivateKey;
|
||||
let licensePublicKeyPath;
|
||||
|
||||
async function post(payload) {
|
||||
const response = await fetch(`${baseUrl}/api`, {
|
||||
const response = await fetch(baseUrl, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
@@ -59,7 +59,7 @@ test("health endpoint reports ready", async () => {
|
||||
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, {
|
||||
method: "POST",
|
||||
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: [] });
|
||||
});
|
||||
|
||||
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 () => {
|
||||
const legacyRoute = await fetch(`${baseUrl}/data_record`, {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user