add notice after config

This commit is contained in:
2026-07-31 10:20:33 +08:00
parent 692d8dd18a
commit d45acaf50f
12 changed files with 2721 additions and 2277 deletions
+1122 -1037
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -41,6 +41,8 @@ class PostgresStore {
const identificationFiles = await queryable.query("SELECT file_id, device_id, file_name, media_type, upload_time, size_bytes, status, processed_at, expires_at FROM identification_files");
const feedback = await queryable.query("SELECT device_id, run_id, file_name, status, result, update_time FROM identification_feedback");
const requests = await queryable.query("SELECT device_id, request_id, status, created_at_ms, expires_at_ms, config_file_id, config_file_name, uploaded_at_ms, update_time FROM volume_config_requests");
const volumeConfigs = await queryable.query("SELECT device_id, file_id, file_name, request_id, updated_at_ms FROM volume_configs");
const panelNotifications = await queryable.query("SELECT notification_id, device_id, type, title, message, request_id, file_id, created_at_ms FROM panel_notifications");
return {
...structuredClone(EMPTY_DATABASE),
companies: companies.rows.map((row) => ({ id: row.id, name: row.name, code: row.code, createdAt: asIso(row.created_at) })),
@@ -51,7 +53,9 @@ class PostgresStore {
panelInbox: panelInbox.rows.map((row) => ({ fileID: row.file_id, deviceId: row.device_id, fileName: row.file_name, mediaType: row.media_type, uploadTime: asIso(row.upload_time) })),
identificationFiles: identificationFiles.rows.map((row) => ({ fileID: row.file_id, deviceId: row.device_id, fileName: row.file_name, mediaType: row.media_type, uploadTime: asIso(row.upload_time), size: Number(row.size_bytes), status: row.status, processedAt: row.processed_at && asIso(row.processed_at), expiresAt: row.expires_at && asIso(row.expires_at) })),
identificationFeedback: feedback.rows.map((row) => ({ deviceId: row.device_id, runId: row.run_id, fileName: row.file_name, status: row.status, result: row.result, updateTime: asIso(row.update_time) })),
volumeConfigRequests: requests.rows.map((row) => ({ deviceId: row.device_id, requestId: row.request_id, status: row.status, createdAtMs: Number(row.created_at_ms), expiresAtMs: Number(row.expires_at_ms), configFileID: row.config_file_id, configFileName: row.config_file_name, uploadedAtMs: row.uploaded_at_ms && Number(row.uploaded_at_ms), updateTime: asIso(row.update_time) }))
volumeConfigRequests: requests.rows.map((row) => ({ deviceId: row.device_id, requestId: row.request_id, status: row.status, createdAtMs: Number(row.created_at_ms), expiresAtMs: Number(row.expires_at_ms), configFileID: row.config_file_id, configFileName: row.config_file_name, uploadedAtMs: row.uploaded_at_ms && Number(row.uploaded_at_ms), updateTime: asIso(row.update_time) })),
volumeConfigs: volumeConfigs.rows.map((row) => ({ deviceId: row.device_id, fileID: row.file_id, fileName: row.file_name, requestId: row.request_id, updatedAtMs: Number(row.updated_at_ms) })),
panelNotifications: panelNotifications.rows.map((row) => ({ notificationId: row.notification_id, deviceId: row.device_id, type: row.type, title: row.title, message: row.message, requestId: row.request_id, fileID: row.file_id, createdAtMs: Number(row.created_at_ms) }))
};
}
@@ -74,7 +78,7 @@ class PostgresStore {
}
async writeDatabase(client, database) {
await client.query("DELETE FROM volume_config_requests; DELETE FROM identification_feedback; DELETE FROM panel_inbox; DELETE FROM identification_files; DELETE FROM function_configs; DELETE FROM file_records; DELETE FROM licenses; DELETE FROM production_lines; DELETE FROM companies;");
await client.query("DELETE FROM panel_notifications; DELETE FROM volume_configs; DELETE FROM volume_config_requests; DELETE FROM identification_feedback; DELETE FROM panel_inbox; DELETE FROM identification_files; DELETE FROM function_configs; DELETE FROM file_records; DELETE FROM licenses; DELETE FROM production_lines; DELETE FROM companies;");
for (const item of database.companies) await client.query("INSERT INTO companies (id, name, code, created_at) VALUES ($1, $2, $3, $4)", [item.id, item.name, item.code, item.createdAt]);
for (const item of database.productionLines) await client.query("INSERT INTO production_lines (id, company_id, name, code, device_id, created_at, last_seen_at) VALUES ($1, $2, $3, $4, $5, $6, $7)", [item.id, item.companyId, item.name, item.code, item.deviceId, item.createdAt, item.lastSeenAt]);
for (const item of database.licenses) await client.query("INSERT INTO licenses (license_id, company_id, production_line_id, device_id, customer, issued_at, expiry_at, features, license, status, created_at, revoked_at, revocation_reason) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)", [item.licenseId, item.companyId, item.productionLineId, item.deviceId, item.customer, item.issuedAt, item.expiryAt, item.features, item.license, item.status, item.createdAt, item.revokedAt, item.revocationReason]);
@@ -84,6 +88,8 @@ class PostgresStore {
for (const item of database.identificationFiles) await client.query("INSERT INTO identification_files (file_id, device_id, file_name, media_type, upload_time, size_bytes, status, processed_at, expires_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", [item.fileID, item.deviceId, item.fileName, item.mediaType, item.uploadTime, item.size, item.status, item.processedAt, item.expiresAt]);
for (const item of database.identificationFeedback) await client.query("INSERT INTO identification_feedback (device_id, run_id, file_name, status, result, update_time) VALUES ($1,$2,$3,$4,$5,$6)", [item.deviceId, item.runId, item.fileName, item.status, item.result, item.updateTime]);
for (const item of database.volumeConfigRequests) await client.query("INSERT INTO volume_config_requests (device_id, request_id, status, created_at_ms, expires_at_ms, config_file_id, config_file_name, uploaded_at_ms, update_time) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)", [item.deviceId, item.requestId, item.status, item.createdAtMs, item.expiresAtMs, item.configFileID, item.configFileName, item.uploadedAtMs, item.updateTime]);
for (const item of database.volumeConfigs) await client.query("INSERT INTO volume_configs (device_id, file_id, file_name, request_id, updated_at_ms) VALUES ($1,$2,$3,$4,$5)", [item.deviceId, item.fileID, item.fileName, item.requestId, item.updatedAtMs]);
for (const item of database.panelNotifications) await client.query("INSERT INTO panel_notifications (notification_id, device_id, type, title, message, request_id, file_id, created_at_ms) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)", [item.notificationId, item.deviceId, item.type, item.title, item.message, item.requestId, item.fileID, item.createdAtMs]);
}
createId(prefix) {
+81 -79
View File
@@ -1,80 +1,82 @@
const fs = require("node:fs");
const path = require("node:path");
const { randomUUID } = require("node:crypto");
const EMPTY_DATABASE = {
fileRecords: [],
functionConfigs: [],
panelInbox: [],
identificationFiles: [],
identificationFeedback: [],
volumeConfigRequests: [],
userInfo: [],
companies: [],
productionLines: [],
licenses: []
};
class JsonStore {
constructor(dataDirectory) {
this.dataDirectory = dataDirectory;
this.filesDirectory = path.join(dataDirectory, "files");
this.modelsDirectory = path.join(dataDirectory, "models");
this.databasePath = path.join(dataDirectory, "database.json");
this.writeQueue = Promise.resolve();
}
async initialize() {
await fs.promises.mkdir(this.filesDirectory, { recursive: true });
try {
await fs.promises.access(this.databasePath);
} catch {
await this.writeDatabase(structuredClone(EMPTY_DATABASE));
}
}
async read() {
const content = await fs.promises.readFile(this.databasePath, "utf8");
return { ...structuredClone(EMPTY_DATABASE), ...JSON.parse(content) };
}
async update(mutator) {
const operation = this.writeQueue.then(async () => {
const database = await this.read();
const result = await mutator(database);
await this.writeDatabase(database);
return result;
});
this.writeQueue = operation.catch(() => undefined);
return operation;
}
async writeDatabase(database) {
await fs.promises.mkdir(this.dataDirectory, { recursive: true });
const temporaryPath = `${this.databasePath}.${process.pid}.tmp`;
await fs.promises.writeFile(
temporaryPath,
`${JSON.stringify(database, null, 2)}\n`,
"utf8"
);
await fs.promises.rename(temporaryPath, this.databasePath);
}
createId(prefix) {
return `${prefix}_${randomUUID()}`;
}
resolveStoredFile(fileID) {
if (typeof fileID !== "string") return null;
const isModel = fileID.startsWith("model://");
if (!isModel && !fileID.startsWith("local://")) return null;
const rootDirectory = isModel ? this.modelsDirectory : this.filesDirectory;
const relativePath = fileID.slice(isModel ? "model://".length : "local://".length).replace(/\\/g, "/");
const absolutePath = path.resolve(rootDirectory, relativePath);
const relativeToRoot = path.relative(rootDirectory, absolutePath);
if (relativeToRoot.startsWith("..") || path.isAbsolute(relativeToRoot)) return null;
return absolutePath;
}
}
const fs = require("node:fs");
const path = require("node:path");
const { randomUUID } = require("node:crypto");
const EMPTY_DATABASE = {
fileRecords: [],
functionConfigs: [],
panelInbox: [],
identificationFiles: [],
identificationFeedback: [],
volumeConfigRequests: [],
volumeConfigs: [],
panelNotifications: [],
userInfo: [],
companies: [],
productionLines: [],
licenses: []
};
class JsonStore {
constructor(dataDirectory) {
this.dataDirectory = dataDirectory;
this.filesDirectory = path.join(dataDirectory, "files");
this.modelsDirectory = path.join(dataDirectory, "models");
this.databasePath = path.join(dataDirectory, "database.json");
this.writeQueue = Promise.resolve();
}
async initialize() {
await fs.promises.mkdir(this.filesDirectory, { recursive: true });
try {
await fs.promises.access(this.databasePath);
} catch {
await this.writeDatabase(structuredClone(EMPTY_DATABASE));
}
}
async read() {
const content = await fs.promises.readFile(this.databasePath, "utf8");
return { ...structuredClone(EMPTY_DATABASE), ...JSON.parse(content) };
}
async update(mutator) {
const operation = this.writeQueue.then(async () => {
const database = await this.read();
const result = await mutator(database);
await this.writeDatabase(database);
return result;
});
this.writeQueue = operation.catch(() => undefined);
return operation;
}
async writeDatabase(database) {
await fs.promises.mkdir(this.dataDirectory, { recursive: true });
const temporaryPath = `${this.databasePath}.${process.pid}.tmp`;
await fs.promises.writeFile(
temporaryPath,
`${JSON.stringify(database, null, 2)}\n`,
"utf8"
);
await fs.promises.rename(temporaryPath, this.databasePath);
}
createId(prefix) {
return `${prefix}_${randomUUID()}`;
}
resolveStoredFile(fileID) {
if (typeof fileID !== "string") return null;
const isModel = fileID.startsWith("model://");
if (!isModel && !fileID.startsWith("local://")) return null;
const rootDirectory = isModel ? this.modelsDirectory : this.filesDirectory;
const relativePath = fileID.slice(isModel ? "model://".length : "local://".length).replace(/\\/g, "/");
const absolutePath = path.resolve(rootDirectory, relativePath);
const relativeToRoot = path.relative(rootDirectory, absolutePath);
if (relativeToRoot.startsWith("..") || path.isAbsolute(relativeToRoot)) return null;
return absolutePath;
}
}
module.exports = { EMPTY_DATABASE, JsonStore };