ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: синхронизация профиля из Tasker

This commit is contained in:
DCCONSTRUCTIONS 2026-05-12 17:57:57 +03:00
parent 06a6160a46
commit 95225280e7
1 changed files with 69 additions and 0 deletions

View File

@ -535,6 +535,44 @@ app.post("/api/internal/tasker/invite-requests/cancel", asyncRoute(async (req, r
res.json({ ok: true, taskerInviteRequest: result.taskerInviteRequest });
}));
app.post("/api/internal/tasker/profile-sync", asyncRoute(async (req, res) => {
if (!isInternalRequestAuthorized(req)) {
res.status(config.internalAccessToken ? 401 : 503).json({
ok: false,
error: config.internalAccessToken ? "internal_access_unauthorized" : "internal_access_not_configured",
});
return;
}
const snapshot = controlPlaneStore.getSnapshot({ name: "NODE.DC tasker profile sync" });
const user = findInternalAccessUser(snapshot.data, req.body);
if (!user) {
res.status(404).json({ ok: false, error: "user_not_found" });
return;
}
const patch = sanitizeTaskerProfilePatch(req.body);
if (Object.keys(patch).length === 0) {
res.json({ ok: true, user, data: snapshot.data, skipped: true });
return;
}
const actor = {
sub: "tasker-profile-sync",
name: req.body?.source === "tasker" ? "Operational Core" : "NODE.DC profile sync",
email: typeof req.body?.email === "string" ? req.body.email : user.email,
source: "tasker",
};
const result = await controlPlaneStore.updateUserProfile(user.id, patch, actor);
const syncResult = await syncUsersToAuthentik(result.data, [user.id], actor);
const updatedUser = syncResult.data.users.find((candidate) => candidate.id === user.id) ?? result.user;
publishControlPlaneEvent("tasker.profile.updated", [user.id]);
res.json({ ok: true, user: updatedUser, data: syncResult.data });
}));
app.patch("/api/profile", requireSession, asyncRoute(async (req, res) => {
const { actor } = getLauncherProfileContext(req.nodedcSession);
const result = await controlPlaneStore.updateUserProfile(actor.id, sanitizeSelfProfilePatch(req.body), req.nodedcSession.user);
@ -1735,6 +1773,37 @@ function sanitizeSelfProfilePatch(payload) {
};
}
function sanitizeTaskerProfilePatch(payload) {
const patch = {};
const name = firstNonEmptyString(payload?.displayName, payload?.display_name, payload?.name);
const hasAvatar =
Object.hasOwn(payload ?? {}, "avatarUrl") ||
Object.hasOwn(payload ?? {}, "avatar_url") ||
Object.hasOwn(payload ?? {}, "avatar");
if (name) {
patch.name = name;
}
if (hasAvatar) {
patch.avatarUrl = nullableProfileUrl(payload?.avatarUrl ?? payload?.avatar_url ?? payload?.avatar);
}
return patch;
}
function firstNonEmptyString(...values) {
for (const value of values) {
if (typeof value === "string" && value.trim()) return value.trim();
}
return null;
}
function nullableProfileUrl(value) {
return typeof value === "string" && value.trim() ? value.trim() : null;
}
function toProvisioningResponse(provisionedUser) {
return {
authentikUserId: provisionedUser.authentikUserId,