187 lines
5.9 KiB
JavaScript
187 lines
5.9 KiB
JavaScript
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,
|
|
};
|