FIX - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: автоподтверждение повторных workspace-инвайтов
This commit is contained in:
parent
0ba6dc7115
commit
784195f747
|
|
@ -892,6 +892,8 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
const workspaceName = optionalString(payload?.workspaceName, workspaceSlug);
|
const workspaceName = optionalString(payload?.workspaceName, workspaceSlug);
|
||||||
const inviteeEmail = requireString(payload?.inviteeEmail, "inviteeEmail").toLowerCase();
|
const inviteeEmail = requireString(payload?.inviteeEmail, "inviteeEmail").toLowerCase();
|
||||||
const role = normalizeTaskManagerInviteRole(payload?.role);
|
const role = normalizeTaskManagerInviteRole(payload?.role);
|
||||||
|
const inviteeUser = data.users.find((user) => user.email.toLowerCase() === inviteeEmail && user.globalStatus === "active");
|
||||||
|
const autoApproveExistingUser = Boolean(inviteeUser && !hasTaskManagerDenyException(data, inviteeUser.id));
|
||||||
const existingRequest = data.taskerInviteRequests.find(
|
const existingRequest = data.taskerInviteRequests.find(
|
||||||
(request) =>
|
(request) =>
|
||||||
request.taskerInviteId === taskerInviteId ||
|
request.taskerInviteId === taskerInviteId ||
|
||||||
|
|
@ -904,6 +906,18 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
taskerInviteId,
|
taskerInviteId,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
};
|
};
|
||||||
|
let nextStatus = "new";
|
||||||
|
if (existingRequest?.status && existingRequest.status !== "rejected") {
|
||||||
|
nextStatus = existingRequest.status;
|
||||||
|
}
|
||||||
|
if (autoApproveExistingUser) {
|
||||||
|
nextStatus = "approved";
|
||||||
|
}
|
||||||
|
|
||||||
|
let auditAction = existingRequest ? "Обновлена заявка workspace-инвайта" : "Создана заявка workspace-инвайта";
|
||||||
|
if (autoApproveExistingUser) {
|
||||||
|
auditAction = "Автоподтверждена заявка workspace-инвайта";
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(request, {
|
Object.assign(request, {
|
||||||
taskerInviteId,
|
taskerInviteId,
|
||||||
|
|
@ -916,11 +930,13 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
inviterPlaneUserId: nullableStringWithFallback(payload?.inviterPlaneUserId, request.inviterPlaneUserId ?? null),
|
inviterPlaneUserId: nullableStringWithFallback(payload?.inviterPlaneUserId, request.inviterPlaneUserId ?? null),
|
||||||
inviterEmail: requireString(payload?.inviterEmail, "inviterEmail").toLowerCase(),
|
inviterEmail: requireString(payload?.inviterEmail, "inviterEmail").toLowerCase(),
|
||||||
inviterName: optionalString(payload?.inviterName, payload?.inviterEmail ?? "Operational Core user"),
|
inviterName: optionalString(payload?.inviterName, payload?.inviterEmail ?? "Operational Core user"),
|
||||||
status: existingRequest?.status && existingRequest.status !== "rejected" ? existingRequest.status : "new",
|
status: nextStatus,
|
||||||
taskerInviteLink: existingRequest?.taskerInviteLink ?? null,
|
taskerInviteLink: existingRequest?.taskerInviteLink ?? null,
|
||||||
reviewedByUserId: existingRequest?.reviewedByUserId ?? null,
|
reviewedByUserId: autoApproveExistingUser ? actor.id : existingRequest?.reviewedByUserId ?? null,
|
||||||
reviewedAt: existingRequest?.reviewedAt ?? null,
|
reviewedAt: autoApproveExistingUser ? now : existingRequest?.reviewedAt ?? null,
|
||||||
comment: nullableStringWithFallback(payload?.comment, existingRequest?.comment ?? null),
|
comment: autoApproveExistingUser
|
||||||
|
? "Автоподтверждено: пользователь уже активен в NODE.DC."
|
||||||
|
: nullableStringWithFallback(payload?.comment, existingRequest?.comment ?? null),
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -928,8 +944,20 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
data.taskerInviteRequests.push(request);
|
data.taskerInviteRequests.push(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (autoApproveExistingUser && inviteeUser) {
|
||||||
|
ensureTaskerInviteServiceAccess(
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
source: "tasker_workspace_invite",
|
||||||
|
sourceTaskerInviteRequestId: request.id,
|
||||||
|
},
|
||||||
|
inviteeUser,
|
||||||
|
now
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
addAuditEvent(data, actor, {
|
addAuditEvent(data, actor, {
|
||||||
action: existingRequest ? "Обновлена заявка workspace-инвайта" : "Создана заявка workspace-инвайта",
|
action: auditAction,
|
||||||
objectType: "tasker_invite_request",
|
objectType: "tasker_invite_request",
|
||||||
objectName: `${workspaceSlug}:${inviteeEmail}`,
|
objectName: `${workspaceSlug}:${inviteeEmail}`,
|
||||||
result: "success",
|
result: "success",
|
||||||
|
|
@ -937,7 +965,12 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
await writeData(data);
|
await writeData(data);
|
||||||
return { taskerInviteRequest: request, data };
|
return {
|
||||||
|
taskerInviteRequest: request,
|
||||||
|
autoApproved: autoApproveExistingUser,
|
||||||
|
affectedUserIds: [payload?.inviterUserId, inviteeUser?.id].filter((userId) => typeof userId === "string" && userId),
|
||||||
|
data,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function approveTaskerInviteRequest(taskerInviteRequestId, payload, identity) {
|
async function approveTaskerInviteRequest(taskerInviteRequestId, payload, identity) {
|
||||||
|
|
@ -2295,6 +2328,17 @@ function ensureTaskerInviteServiceAccess(data, invite, user, now) {
|
||||||
return grant;
|
return grant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasTaskManagerDenyException(data, userId) {
|
||||||
|
const service = data.services.find((candidate) => candidate.slug === "task-manager");
|
||||||
|
if (!service) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.exceptions.some(
|
||||||
|
(exception) => exception.serviceId === service.id && exception.userId === userId && exception.type === "deny"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function findTaskerInviteRequestForCancellation(data, payload) {
|
function findTaskerInviteRequestForCancellation(data, payload) {
|
||||||
const requestId = nullableString(payload?.requestId);
|
const requestId = nullableString(payload?.requestId);
|
||||||
const taskerInviteId = nullableString(payload?.taskerInviteId);
|
const taskerInviteId = nullableString(payload?.taskerInviteId);
|
||||||
|
|
|
||||||
|
|
@ -501,8 +501,11 @@ app.post("/api/internal/tasker/invite-requests", asyncRoute(async (req, res) =>
|
||||||
inviterName: inviter.name,
|
inviterName: inviter.name,
|
||||||
}, inviter);
|
}, inviter);
|
||||||
|
|
||||||
publishControlPlaneEvent("tasker.invite-request.created", [inviter.id]);
|
publishControlPlaneEvent(
|
||||||
res.json({ ok: true, taskerInviteRequest: result.taskerInviteRequest });
|
"tasker.invite-request.created",
|
||||||
|
result.affectedUserIds?.length ? result.affectedUserIds : [inviter.id]
|
||||||
|
);
|
||||||
|
res.json({ ok: true, taskerInviteRequest: result.taskerInviteRequest, autoApproved: Boolean(result.autoApproved) });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.post("/api/internal/tasker/invite-requests/cancel", asyncRoute(async (req, res) => {
|
app.post("/api/internal/tasker/invite-requests/cancel", asyncRoute(async (req, res) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue