新增本地认证与管理接口服务

This commit is contained in:
cat-shark
2026-06-20 10:07:44 +08:00
parent a13b804c7a
commit cf82d606aa
10 changed files with 1057 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
const fs = require("node:fs");
const path = require("node:path");
const { dataDir, defaultUser, defaultSystemConfig } = require("./config.cjs");
const { addYears, nowIso, passwordHash } = require("./security.cjs");
const dbPath = path.join(dataDir, "users.json");
let memoryDb = null;
function createInitialDb() {
const expiresAt = addYears(new Date(), 100).toISOString();
const user = {
id: 1,
username: defaultUser.username,
email: defaultUser.email,
password: passwordHash(defaultUser.password),
role: "admin",
status: "active",
points: 999999,
is_super_admin: 1,
owner_admin_id: null,
invite_code: "LOCALADMIN",
max_devices: 99,
device_fingerprint: "",
created_at: nowIso(),
updated_at: nowIso(),
last_login_at: null,
last_login_device: null,
current_token: "",
cloud_digital_human_disabled: 0,
};
return {
nextUserId: 2,
nextSubscriptionId: 2,
nextUserLogId: 1,
nextGenerationLogId: 1,
users: [user],
subscriptions: [
{
id: 1,
user_id: 1,
subscription_type: "local",
expires_at: expiresAt,
trial_started_at: nowIso(),
created_at: nowIso(),
updated_at: nowIso(),
},
],
generationLogs: [],
userLogs: [],
devices: [],
systemConfigs: { ...defaultSystemConfig },
};
}
function normalizeSystemConfigs(value) {
if (Array.isArray(value)) {
return value.reduce((configs, item) => {
if (item?.config_key) configs[item.config_key] = String(item.config_value ?? "");
return configs;
}, { ...defaultSystemConfig });
}
return { ...defaultSystemConfig, ...(value || {}) };
}
function normalizeDb(db) {
const initial = createInitialDb();
const normalized = { ...initial, ...(db || {}) };
normalized.users = Array.isArray(normalized.users) ? normalized.users : initial.users;
normalized.subscriptions = Array.isArray(normalized.subscriptions) ? normalized.subscriptions : initial.subscriptions;
normalized.generationLogs = Array.isArray(normalized.generationLogs) ? normalized.generationLogs : [];
normalized.userLogs = Array.isArray(normalized.userLogs) ? normalized.userLogs : [];
normalized.devices = Array.isArray(normalized.devices) ? normalized.devices : [];
normalized.systemConfigs = normalizeSystemConfigs(normalized.systemConfigs);
for (const user of normalized.users) {
user.role = user.role || "user";
user.status = user.status || "active";
user.points = Number(user.points || 0);
user.max_devices = Number(user.max_devices || 1);
user.is_super_admin = Number(user.is_super_admin || 0);
user.cloud_digital_human_disabled = Number(user.cloud_digital_human_disabled || 0);
user.created_at = user.created_at || nowIso();
user.updated_at = user.updated_at || nowIso();
}
normalized.nextUserId = Math.max(
Number(normalized.nextUserId || 1),
...normalized.users.map(user => Number(user.id || 0) + 1),
);
normalized.nextSubscriptionId = Math.max(
Number(normalized.nextSubscriptionId || 1),
...normalized.subscriptions.map(item => Number(item.id || 0) + 1),
);
normalized.nextUserLogId = Math.max(
Number(normalized.nextUserLogId || 1),
...normalized.userLogs.map(item => Number(item.id || 0) + 1),
);
normalized.nextGenerationLogId = Math.max(
Number(normalized.nextGenerationLogId || 1),
...normalized.generationLogs.map(item => Number(item.id || 0) + 1),
);
return normalized;
}
function ensureDb() {
if (memoryDb) return;
try {
fs.mkdirSync(dataDir, { recursive: true });
if (!fs.existsSync(dbPath)) {
fs.writeFileSync(dbPath, JSON.stringify(createInitialDb(), null, 2), "utf8");
}
} catch (error) {
memoryDb = createInitialDb();
console.warn(`[local-auth] cannot write ${dbPath}; using in-memory database: ${error.message}`);
}
}
function loadDb() {
ensureDb();
if (memoryDb) return memoryDb;
return normalizeDb(JSON.parse(fs.readFileSync(dbPath, "utf8")));
}
function saveDb(db) {
const normalized = normalizeDb(db);
if (memoryDb) {
memoryDb = normalized;
return;
}
fs.writeFileSync(dbPath, JSON.stringify(normalized, null, 2), "utf8");
}
function getSubscription(db, userId) {
const existing = db.subscriptions.find(item => Number(item.user_id) === Number(userId));
if (existing) return existing;
const subscription = {
id: db.nextSubscriptionId++,
user_id: Number(userId),
subscription_type: "local",
expires_at: addYears(new Date(), 100).toISOString(),
trial_started_at: nowIso(),
created_at: nowIso(),
updated_at: nowIso(),
};
db.subscriptions.push(subscription);
return subscription;
}
function addUserLog(db, userId, action, message, extra = {}) {
db.userLogs.push({
id: db.nextUserLogId++,
user_id: Number(userId || 0) || null,
action,
message,
device_fingerprint: extra.device_fingerprint || null,
ip_address: extra.ip_address || "127.0.0.1",
created_at: nowIso(),
});
}
function addGenerationLog(db, userId, type, detail, extra = {}) {
db.generationLogs.push({
id: db.nextGenerationLogId++,
user_id: Number(userId || 0) || null,
type: type || "unknown",
detail: typeof detail === "string" ? detail : JSON.stringify(detail || {}),
device_fingerprint: extra.device_fingerprint || null,
ip_address: extra.ip_address || "127.0.0.1",
created_at: nowIso(),
});
}
module.exports = {
dbPath,
ensureDb,
loadDb,
saveDb,
getSubscription,
addUserLog,
addGenerationLog,
};