NODEDC_LAUNCHER/scripts/seed-live-control-plane.mjs

244 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { existsSync, readFileSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, isAbsolute, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)));
const serverStorageRoot = resolveStorageRoot(projectRoot);
const dataPath = join(serverStorageRoot, "launcher-data.json");
const legacyPublicDataPath = join(projectRoot, "public", "storage", "launcher-data.json");
const now = new Date().toISOString();
const existingData = existsSync(dataPath) ? readJson(dataPath) : readJson(legacyPublicDataPath);
const services = Array.isArray(existingData.services) ? existingData.services : [];
const existingUsersByEmail = new Map(
(Array.isArray(existingData.users) ? existingData.users : []).map((user) => [String(user.email || "").toLowerCase(), user])
);
const dcTouchAuthentikUserId = existingUsersByEmail.get("dcctouch@gmail.com")?.authentikUserId ?? null;
const silverPsihAuthentikUserId = existingUsersByEmail.get("silver_psih@yahoo.com")?.authentikUserId ?? null;
const liveData = {
...existingData,
clients: [
{
id: "client_romashka",
type: "company",
name: "DCTOUCH",
legalName: "ООО ДИСИТАЧ",
status: "active",
contractStartsAt: "2026-05-04T00:00:00.000Z",
contractEndsAt: null,
paidUntil: null,
demoEndsAt: null,
contactName: "DC Touch",
contactEmail: "dcctouch@gmail.com",
notes: "Live-клиент NODE.DC для первичной проверки control-plane, SSO и доступа к сервисам.",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
],
users: [
{
id: "user_root",
authentikUserId: dcTouchAuthentikUserId,
name: "DC Touch",
email: "dcctouch@gmail.com",
phone: null,
position: "NODE.DC Super Admin",
notes: "Главный супер-администратор NODE.DC. Authentik-пользователь уже создан в dev-контуре.",
avatarUrl: null,
globalStatus: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
{
id: "user_silver_psih",
authentikUserId: silverPsihAuthentikUserId,
name: "Silver Psy",
email: "silver_psih@yahoo.com",
phone: null,
position: "Manager",
notes: "Живой пользователь из Plane. Требует создания/синхронизации в Authentik через Launcher flow.",
avatarUrl: null,
globalStatus: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
],
memberships: [
{
id: "mem_dc_touch_dctouch",
clientId: "client_romashka",
userId: "user_root",
role: "client_owner",
status: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
{
id: "mem_silver_psih_dctouch",
clientId: "client_romashka",
userId: "user_silver_psih",
role: "member",
status: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
],
groups: [
{
id: "group_dctouch_admins",
clientId: "client_romashka",
name: "Администраторы",
description: "Администраторы клиента и владельцы платформенного доступа.",
memberIds: ["user_root"],
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
{
id: "group_dctouch_managers",
clientId: "client_romashka",
name: "Менеджеры",
description: "Рабочая группа менеджеров с доступом к операционному контуру.",
memberIds: ["user_silver_psih"],
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
],
grants: [
{
id: "grant_dctouch_task_admins",
serviceId: "service_task_manager",
targetType: "group",
targetId: "group_dctouch_admins",
appRole: "admin",
status: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
{
id: "grant_dctouch_task_managers",
serviceId: "service_task_manager",
targetType: "group",
targetId: "group_dctouch_managers",
appRole: "member",
status: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
{
id: "grant_dctouch_nodedc_admins",
serviceId: "service_nodedc",
targetType: "group",
targetId: "group_dctouch_admins",
appRole: "admin",
status: "active",
createdAt: "2026-05-04T00:00:00.000Z",
updatedAt: now,
},
],
exceptions: [],
invites: [],
syncStatuses: [
{
id: "sync_dctouch_client_authentik",
objectId: "client_romashka",
objectName: "DCTOUCH",
objectType: "client",
target: "authentik",
state: "synced",
lastSyncAt: now,
error: null,
updatedAt: now,
},
{
id: "sync_dc_touch_authentik",
objectId: "user_root",
objectName: "dcctouch@gmail.com",
objectType: "user",
target: "authentik",
state: dcTouchAuthentikUserId ? "synced" : "pending",
lastSyncAt: dcTouchAuthentikUserId ? now : null,
error: dcTouchAuthentikUserId ? null : "Пользователь есть в Authentik, но Launcher seed ещё не содержит Authentik UUID.",
updatedAt: now,
},
{
id: "sync_silver_psih_authentik",
objectId: "user_silver_psih",
objectName: "silver_psih@yahoo.com",
objectType: "user",
target: "authentik",
state: silverPsihAuthentikUserId ? "synced" : "pending",
lastSyncAt: silverPsihAuthentikUserId ? now : null,
error: silverPsihAuthentikUserId
? null
: "Пользователь найден в Plane, но ещё не создан в Authentik через Launcher invite/sync flow.",
updatedAt: now,
},
{
id: "sync_dctouch_groups_authentik",
objectId: "client_romashka:groups",
objectName: "DCTOUCH groups",
objectType: "group",
target: "authentik",
state: "pending",
lastSyncAt: null,
error: null,
updatedAt: now,
},
{
id: "sync_task_manager_authentik",
objectId: "service_task_manager",
objectName: "OPERATIONAL CORE",
objectType: "service",
target: "authentik",
state: "synced",
lastSyncAt: now,
error: null,
updatedAt: now,
},
],
auditEvents: [
{
id: "audit_live_seed_control_plane",
at: now,
actorUserId: "system",
actorName: "NODE.DC seed",
action: "Применён live seed control-plane",
objectType: "control_plane",
objectName: "Launcher users and access",
clientId: "client_romashka",
result: "success",
details: "Demo-участники удалены из runtime storage. Оставлены dcctouch@gmail.com и silver_psih@yahoo.com.",
},
],
services,
};
await writeJson(dataPath, liveData);
console.log(`Seeded ${liveData.users.length} users, ${liveData.clients.length} client, ${liveData.groups.length} groups.`);
function readJson(path) {
if (!existsSync(path)) {
return {};
}
return JSON.parse(readFileSync(path, "utf8"));
}
async function writeJson(path, data) {
await mkdir(dirname(path), { recursive: true });
await writeFile(path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
}
function resolveStorageRoot(projectRoot) {
const configuredRoot = process.env.NODEDC_LAUNCHER_STORAGE_DIR;
if (configuredRoot && configuredRoot.trim()) {
return isAbsolute(configuredRoot) ? configuredRoot : resolve(projectRoot, configuredRoot);
}
return join(projectRoot, "server", "storage");
}