新增本地认证与管理接口服务
This commit is contained in:
@@ -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,
|
||||
};
|
||||
Reference in New Issue
Block a user