АРХ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: managedBy policy Launcher
This commit is contained in:
parent
fd1d5baef3
commit
01e0988031
|
|
@ -27,6 +27,7 @@ const appRoles = new Set(["viewer", "member", "admin", "owner"]);
|
||||||
const grantStatuses = new Set(["active", "disabled"]);
|
const grantStatuses = new Set(["active", "disabled"]);
|
||||||
const exceptionTypes = new Set(["deny", "allow"]);
|
const exceptionTypes = new Set(["deny", "allow"]);
|
||||||
const serviceStatuses = new Set(["active", "maintenance", "hidden", "disabled"]);
|
const serviceStatuses = new Set(["active", "maintenance", "hidden", "disabled"]);
|
||||||
|
const taskManagerWorkspaceManagedByValues = new Set(["launcher", "tasker"]);
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
brand: {
|
brand: {
|
||||||
logoLinkUrl: "/",
|
logoLinkUrl: "/",
|
||||||
|
|
@ -177,6 +178,7 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
workspaceSlug: workspace.slug ?? payload?.workspaceSlug,
|
workspaceSlug: workspace.slug ?? payload?.workspaceSlug,
|
||||||
workspaceName: workspace.name ?? payload?.workspaceName ?? null,
|
workspaceName: workspace.name ?? payload?.workspaceName ?? null,
|
||||||
role: normalizeTaskManagerMembershipRole(payload?.role),
|
role: normalizeTaskManagerMembershipRole(payload?.role),
|
||||||
|
managedBy: payload?.managedBy,
|
||||||
planeUserId: membership.member?.id ?? null,
|
planeUserId: membership.member?.id ?? null,
|
||||||
planeRole: typeof membership.role === "number" ? membership.role : null,
|
planeRole: typeof membership.role === "number" ? membership.role : null,
|
||||||
});
|
});
|
||||||
|
|
@ -234,15 +236,16 @@ export function createControlPlaneStore({ projectRoot }) {
|
||||||
upsertTaskManagerProjectMembership(data, {
|
upsertTaskManagerProjectMembership(data, {
|
||||||
clientId: client.id,
|
clientId: client.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
workspaceSlug: workspace.slug ?? payload?.workspaceSlug,
|
workspaceSlug: workspace.slug ?? payload?.workspaceSlug,
|
||||||
workspaceName: workspace.name ?? payload?.workspaceName ?? null,
|
workspaceName: workspace.name ?? payload?.workspaceName ?? null,
|
||||||
projectId: project.id ?? payload?.projectId,
|
projectId: project.id ?? payload?.projectId,
|
||||||
projectIdentifier: project.identifier ?? payload?.projectIdentifier ?? null,
|
projectIdentifier: project.identifier ?? payload?.projectIdentifier ?? null,
|
||||||
projectName: project.name ?? payload?.projectName ?? null,
|
projectName: project.name ?? payload?.projectName ?? null,
|
||||||
role: normalizeTaskManagerMembershipRole(payload?.role),
|
role: normalizeTaskManagerMembershipRole(payload?.role),
|
||||||
planeUserId: membership.member?.id ?? null,
|
managedBy: payload?.managedBy,
|
||||||
planeRole: typeof membership.role === "number" ? membership.role : null,
|
planeUserId: membership.member?.id ?? null,
|
||||||
});
|
planeRole: typeof membership.role === "number" ? membership.role : null,
|
||||||
|
});
|
||||||
|
|
||||||
addAuditEvent(data, actor, {
|
addAuditEvent(data, actor, {
|
||||||
action: "Назначен Tasker project",
|
action: "Назначен Tasker project",
|
||||||
|
|
@ -1252,11 +1255,12 @@ function normalizeTaskManagerWorkspaces(taskManager, fallbackTaskManager = {}) {
|
||||||
slug,
|
slug,
|
||||||
name: nullableStringWithFallback(item.name, null),
|
name: nullableStringWithFallback(item.name, null),
|
||||||
isPrimary: item.isPrimary === true,
|
isPrimary: item.isPrimary === true,
|
||||||
|
managedBy: normalizeTaskManagerWorkspaceManagedBy(item.managedBy),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (legacySlug && !bySlug.has(legacySlug)) {
|
if (legacySlug && !bySlug.has(legacySlug)) {
|
||||||
bySlug.set(legacySlug, { slug: legacySlug, name: legacyName, isPrimary: true });
|
bySlug.set(legacySlug, { slug: legacySlug, name: legacyName, isPrimary: true, managedBy: "launcher" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const workspaces = [...bySlug.values()];
|
const workspaces = [...bySlug.values()];
|
||||||
|
|
@ -1274,10 +1278,15 @@ function normalizeTaskManagerWorkspaces(taskManager, fallbackTaskManager = {}) {
|
||||||
slug: workspace.slug,
|
slug: workspace.slug,
|
||||||
name: workspace.name ?? null,
|
name: workspace.name ?? null,
|
||||||
isPrimary,
|
isPrimary,
|
||||||
|
managedBy: normalizeTaskManagerWorkspaceManagedBy(workspace.managedBy),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeTaskManagerWorkspaceManagedBy(value) {
|
||||||
|
return taskManagerWorkspaceManagedByValues.has(value) ? value : "launcher";
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeTaskManagerMembershipRole(value) {
|
function normalizeTaskManagerMembershipRole(value) {
|
||||||
return value === "guest" || value === "admin" || value === "member" ? value : "member";
|
return value === "guest" || value === "admin" || value === "member" ? value : "member";
|
||||||
}
|
}
|
||||||
|
|
@ -1294,6 +1303,7 @@ function upsertTaskManagerMembership(data, payload) {
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
workspaceName: nullableStringWithFallback(payload.workspaceName, existingMembership?.workspaceName ?? null),
|
workspaceName: nullableStringWithFallback(payload.workspaceName, existingMembership?.workspaceName ?? null),
|
||||||
role: normalizeTaskManagerMembershipRole(payload.role),
|
role: normalizeTaskManagerMembershipRole(payload.role),
|
||||||
|
managedBy: normalizeTaskManagerWorkspaceManagedBy(payload.managedBy ?? existingMembership?.managedBy),
|
||||||
planeUserId: nullableStringWithFallback(payload.planeUserId, existingMembership?.planeUserId ?? null),
|
planeUserId: nullableStringWithFallback(payload.planeUserId, existingMembership?.planeUserId ?? null),
|
||||||
planeRole: typeof payload.planeRole === "number" ? payload.planeRole : (existingMembership?.planeRole ?? null),
|
planeRole: typeof payload.planeRole === "number" ? payload.planeRole : (existingMembership?.planeRole ?? null),
|
||||||
updatedAt: isoNow(),
|
updatedAt: isoNow(),
|
||||||
|
|
@ -1330,6 +1340,7 @@ function upsertTaskManagerProjectMembership(data, payload) {
|
||||||
projectIdentifier: nullableStringWithFallback(payload.projectIdentifier, existingMembership?.projectIdentifier ?? null),
|
projectIdentifier: nullableStringWithFallback(payload.projectIdentifier, existingMembership?.projectIdentifier ?? null),
|
||||||
projectName: nullableStringWithFallback(payload.projectName, existingMembership?.projectName ?? null),
|
projectName: nullableStringWithFallback(payload.projectName, existingMembership?.projectName ?? null),
|
||||||
role: normalizeTaskManagerMembershipRole(payload.role),
|
role: normalizeTaskManagerMembershipRole(payload.role),
|
||||||
|
managedBy: normalizeTaskManagerWorkspaceManagedBy(payload.managedBy ?? existingMembership?.managedBy),
|
||||||
planeUserId: nullableStringWithFallback(payload.planeUserId, existingMembership?.planeUserId ?? null),
|
planeUserId: nullableStringWithFallback(payload.planeUserId, existingMembership?.planeUserId ?? null),
|
||||||
planeRole: typeof payload.planeRole === "number" ? payload.planeRole : (existingMembership?.planeRole ?? null),
|
planeRole: typeof payload.planeRole === "number" ? payload.planeRole : (existingMembership?.planeRole ?? null),
|
||||||
updatedAt: isoNow(),
|
updatedAt: isoNow(),
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ app.post("/api/internal/access/check", (req, res) => {
|
||||||
const app = getAppsForUser(groups).find((candidate) => candidate.slug === serviceSlug);
|
const app = getAppsForUser(groups).find((candidate) => candidate.slug === serviceSlug);
|
||||||
const allowed = Boolean(app?.hasAccess);
|
const allowed = Boolean(app?.hasAccess);
|
||||||
const workspacePolicy =
|
const workspacePolicy =
|
||||||
serviceSlug === "task-manager" ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed) : null;
|
serviceSlug === "task-manager" ? resolveTaskManagerWorkspacePolicy(snapshot.data, groups, allowed, user) : null;
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
@ -569,6 +569,7 @@ app.post("/api/admin/task-manager/workspace-memberships/ensure", requireLauncher
|
||||||
|
|
||||||
const membership = snapshot.data.memberships.find((candidate) => candidate.clientId === client.id && candidate.userId === user.id);
|
const membership = snapshot.data.memberships.find((candidate) => candidate.clientId === client.id && candidate.userId === user.id);
|
||||||
const workspaceSlug = normalizeOptionalText(req.body?.workspaceSlug) ?? client.integrations?.taskManager?.workspaceSlug ?? null;
|
const workspaceSlug = normalizeOptionalText(req.body?.workspaceSlug) ?? client.integrations?.taskManager?.workspaceSlug ?? null;
|
||||||
|
const workspaceManagedBy = resolveTaskManagerWorkspaceManagedByForClient(client, workspaceSlug);
|
||||||
|
|
||||||
if (!workspaceSlug) {
|
if (!workspaceSlug) {
|
||||||
res.status(400).json({ ok: false, error: "task_manager_workspace_not_configured" });
|
res.status(400).json({ ok: false, error: "task_manager_workspace_not_configured" });
|
||||||
|
|
@ -585,6 +586,7 @@ app.post("/api/admin/task-manager/workspace-memberships/ensure", requireLauncher
|
||||||
avatarUrl: resolveUserAvatarPublicUrl(user),
|
avatarUrl: resolveUserAvatarPublicUrl(user),
|
||||||
role,
|
role,
|
||||||
companyRole: membership?.role ?? null,
|
companyRole: membership?.role ?? null,
|
||||||
|
managedBy: workspaceManagedBy,
|
||||||
setLastWorkspace: req.body?.setLastWorkspace !== false,
|
setLastWorkspace: req.body?.setLastWorkspace !== false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -595,6 +597,7 @@ app.post("/api/admin/task-manager/workspace-memberships/ensure", requireLauncher
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
role,
|
role,
|
||||||
|
managedBy: workspaceManagedBy,
|
||||||
taskManager,
|
taskManager,
|
||||||
},
|
},
|
||||||
req.nodedcSession.user
|
req.nodedcSession.user
|
||||||
|
|
@ -641,17 +644,19 @@ app.post("/api/admin/task-manager/workspace-memberships/remove", requireLauncher
|
||||||
email: user.email,
|
email: user.email,
|
||||||
subject: user.authentikUserId ?? undefined,
|
subject: user.authentikUserId ?? undefined,
|
||||||
avatarUrl: resolveUserAvatarPublicUrl(user),
|
avatarUrl: resolveUserAvatarPublicUrl(user),
|
||||||
role: "admin",
|
role: "admin",
|
||||||
companyRole: membership.role,
|
companyRole: membership.role,
|
||||||
setLastWorkspace: false,
|
managedBy: resolveTaskManagerWorkspaceManagedByForClient(client, workspaceSlug),
|
||||||
},
|
setLastWorkspace: false,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
const result = await controlPlaneStore.recordTaskManagerWorkspaceMembership(
|
const result = await controlPlaneStore.recordTaskManagerWorkspaceMembership(
|
||||||
{
|
{
|
||||||
clientId: client.id,
|
clientId: client.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
role: "admin",
|
role: "admin",
|
||||||
|
managedBy: resolveTaskManagerWorkspaceManagedByForClient(client, workspaceSlug),
|
||||||
taskManager,
|
taskManager,
|
||||||
},
|
},
|
||||||
req.nodedcSession.user
|
req.nodedcSession.user
|
||||||
|
|
@ -707,6 +712,7 @@ app.post("/api/admin/task-manager/project-memberships/ensure", requireLauncherAd
|
||||||
const workspaceSlug = normalizeOptionalText(req.body?.workspaceSlug);
|
const workspaceSlug = normalizeOptionalText(req.body?.workspaceSlug);
|
||||||
const projectId = normalizeOptionalText(req.body?.projectId);
|
const projectId = normalizeOptionalText(req.body?.projectId);
|
||||||
const role = normalizeTaskManagerRole(req.body?.role);
|
const role = normalizeTaskManagerRole(req.body?.role);
|
||||||
|
const workspaceManagedBy = resolveTaskManagerWorkspaceManagedByForClient(client, workspaceSlug);
|
||||||
|
|
||||||
if (!workspaceSlug) {
|
if (!workspaceSlug) {
|
||||||
res.status(400).json({ ok: false, error: "task_manager_workspace_not_configured" });
|
res.status(400).json({ ok: false, error: "task_manager_workspace_not_configured" });
|
||||||
|
|
@ -732,6 +738,7 @@ app.post("/api/admin/task-manager/project-memberships/ensure", requireLauncherAd
|
||||||
subject: user.authentikUserId ?? undefined,
|
subject: user.authentikUserId ?? undefined,
|
||||||
avatarUrl: resolveUserAvatarPublicUrl(user),
|
avatarUrl: resolveUserAvatarPublicUrl(user),
|
||||||
role,
|
role,
|
||||||
|
managedBy: workspaceManagedBy,
|
||||||
setLastWorkspace: false,
|
setLastWorkspace: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -743,6 +750,7 @@ app.post("/api/admin/task-manager/project-memberships/ensure", requireLauncherAd
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
projectId,
|
projectId,
|
||||||
role,
|
role,
|
||||||
|
managedBy: workspaceManagedBy,
|
||||||
taskManager,
|
taskManager,
|
||||||
},
|
},
|
||||||
req.nodedcSession.user
|
req.nodedcSession.user
|
||||||
|
|
@ -1632,6 +1640,72 @@ function resolveTaskManagerRoleForMembership(role) {
|
||||||
return role === "client_owner" || role === "client_admin" ? "admin" : "member";
|
return role === "client_owner" || role === "client_admin" ? "admin" : "member";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeTaskManagerWorkspaceManagedBy(value) {
|
||||||
|
return value === "tasker" ? "tasker" : "launcher";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientTaskManagerWorkspaces(client) {
|
||||||
|
const taskManager = client?.integrations?.taskManager;
|
||||||
|
const workspaces = Array.isArray(taskManager?.workspaces) ? taskManager.workspaces : [];
|
||||||
|
const legacySlug = normalizeOptionalText(taskManager?.workspaceSlug);
|
||||||
|
|
||||||
|
if (!legacySlug || workspaces.some((workspace) => normalizeOptionalText(workspace?.slug) === legacySlug)) {
|
||||||
|
return workspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...workspaces,
|
||||||
|
{
|
||||||
|
slug: legacySlug,
|
||||||
|
name: normalizeOptionalText(taskManager?.workspaceName),
|
||||||
|
isPrimary: true,
|
||||||
|
managedBy: "launcher",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTaskManagerWorkspaceBinding(client, workspaceSlug) {
|
||||||
|
const normalizedWorkspaceSlug = normalizeOptionalText(workspaceSlug);
|
||||||
|
if (!normalizedWorkspaceSlug) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
getClientTaskManagerWorkspaces(client).find((workspace) => normalizeOptionalText(workspace?.slug) === normalizedWorkspaceSlug) ?? null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTaskManagerWorkspaceManagedByForClient(client, workspaceSlug) {
|
||||||
|
return normalizeTaskManagerWorkspaceManagedBy(resolveTaskManagerWorkspaceBinding(client, workspaceSlug)?.managedBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTaskManagerWorkspaceAssignments(data, user) {
|
||||||
|
if (!user?.id) return [];
|
||||||
|
|
||||||
|
const bySlug = new Map();
|
||||||
|
for (const membership of data.taskManagerMemberships ?? []) {
|
||||||
|
if (membership.userId !== user.id) continue;
|
||||||
|
const workspaceSlug = normalizeOptionalText(membership.workspaceSlug);
|
||||||
|
if (!workspaceSlug) continue;
|
||||||
|
|
||||||
|
const client = data.clients.find((candidate) => candidate.id === membership.clientId);
|
||||||
|
const managedBy = normalizeTaskManagerWorkspaceManagedBy(
|
||||||
|
membership.managedBy ?? resolveTaskManagerWorkspaceBinding(client, workspaceSlug)?.managedBy
|
||||||
|
);
|
||||||
|
const current = bySlug.get(workspaceSlug);
|
||||||
|
if (current && current.managedBy === "launcher") continue;
|
||||||
|
|
||||||
|
bySlug.set(workspaceSlug, {
|
||||||
|
slug: workspaceSlug,
|
||||||
|
name: normalizeOptionalText(membership.workspaceName ?? resolveTaskManagerWorkspaceBinding(client, workspaceSlug)?.name),
|
||||||
|
managedBy,
|
||||||
|
clientId: client?.id ?? membership.clientId ?? null,
|
||||||
|
clientName: client?.name ?? null,
|
||||||
|
role: normalizeTaskManagerRole(membership.role) ?? "member",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...bySlug.values()];
|
||||||
|
}
|
||||||
|
|
||||||
function createServiceHandoff(serviceSlug, user) {
|
function createServiceHandoff(serviceSlug, user) {
|
||||||
pruneExpiredServiceHandoffs();
|
pruneExpiredServiceHandoffs();
|
||||||
|
|
||||||
|
|
@ -1679,15 +1753,21 @@ function pruneExpiredServiceHandoffs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess) {
|
function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess, user) {
|
||||||
const mode = data.settings?.taskManager?.workspaceCreationPolicy ?? "any_authorized_user";
|
const mode = data.settings?.taskManager?.workspaceCreationPolicy ?? "any_authorized_user";
|
||||||
const groupSet = new Set(groups);
|
const groupSet = new Set(groups);
|
||||||
const isSuperAdmin = groupSet.has("nodedc:superadmin");
|
const isSuperAdmin = groupSet.has("nodedc:superadmin");
|
||||||
const isTaskManagerAdmin = groupSet.has("nodedc:taskmanager:admin");
|
const isTaskManagerAdmin = groupSet.has("nodedc:taskmanager:admin");
|
||||||
|
const workspaces = resolveTaskManagerWorkspaceAssignments(data, user);
|
||||||
|
const hasLauncherManagedWorkspace = workspaces.some((workspace) => workspace.managedBy === "launcher");
|
||||||
|
const defaultManagedBy = hasLauncherManagedWorkspace && !isSuperAdmin ? "launcher" : "tasker";
|
||||||
|
|
||||||
if (!hasTaskManagerAccess) {
|
if (!hasTaskManagerAccess) {
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
|
managedBy: defaultManagedBy,
|
||||||
|
defaultManagedBy,
|
||||||
|
workspaces,
|
||||||
canCreateWorkspace: false,
|
canCreateWorkspace: false,
|
||||||
reason: "Нет доступа к Operational Core.",
|
reason: "Нет доступа к Operational Core.",
|
||||||
};
|
};
|
||||||
|
|
@ -1696,14 +1776,31 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess) {
|
||||||
if (mode === "disabled") {
|
if (mode === "disabled") {
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
|
managedBy: defaultManagedBy,
|
||||||
|
defaultManagedBy,
|
||||||
|
workspaces,
|
||||||
canCreateWorkspace: false,
|
canCreateWorkspace: false,
|
||||||
reason: "Создание рабочих пространств отключено на уровне платформы.",
|
reason: "Создание рабочих пространств отключено на уровне платформы.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasLauncherManagedWorkspace && !isSuperAdmin) {
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
managedBy: "launcher",
|
||||||
|
defaultManagedBy: "launcher",
|
||||||
|
workspaces,
|
||||||
|
canCreateWorkspace: false,
|
||||||
|
reason: "Рабочие пространства этого пользователя управляются через Launcher.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === "task_admins_only" && !isSuperAdmin && !isTaskManagerAdmin) {
|
if (mode === "task_admins_only" && !isSuperAdmin && !isTaskManagerAdmin) {
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
|
managedBy: defaultManagedBy,
|
||||||
|
defaultManagedBy,
|
||||||
|
workspaces,
|
||||||
canCreateWorkspace: false,
|
canCreateWorkspace: false,
|
||||||
reason: "Создание рабочих пространств доступно только администраторам Operational Core.",
|
reason: "Создание рабочих пространств доступно только администраторам Operational Core.",
|
||||||
};
|
};
|
||||||
|
|
@ -1711,6 +1808,9 @@ function resolveTaskManagerWorkspacePolicy(data, groups, hasTaskManagerAccess) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
|
managedBy: "tasker",
|
||||||
|
defaultManagedBy: "tasker",
|
||||||
|
workspaces,
|
||||||
canCreateWorkspace: true,
|
canCreateWorkspace: true,
|
||||||
reason: "Создание рабочих пространств разрешено платформенной policy.",
|
reason: "Создание рабочих пространств разрешено платформенной policy.",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
export type ClientType = "company" | "person";
|
export type ClientType = "company" | "person";
|
||||||
export type ClientStatus = "active" | "suspended" | "demo" | "expired";
|
export type ClientStatus = "active" | "suspended" | "demo" | "expired";
|
||||||
|
export type TaskManagerWorkspaceManagedBy = "launcher" | "tasker";
|
||||||
|
|
||||||
export interface ClientTaskManagerWorkspaceBinding {
|
export interface ClientTaskManagerWorkspaceBinding {
|
||||||
slug: string;
|
slug: string;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
isPrimary?: boolean;
|
isPrimary?: boolean;
|
||||||
|
managedBy?: TaskManagerWorkspaceManagedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Client {
|
export interface Client {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ServiceAccessException, ServiceAppRole, ServiceGrant } from "../../entities/access/types";
|
import type { ServiceAccessException, ServiceAppRole, ServiceGrant } from "../../entities/access/types";
|
||||||
import type { Client } from "../../entities/client/types";
|
import type { Client, TaskManagerWorkspaceManagedBy } from "../../entities/client/types";
|
||||||
import type { Invite } from "../../entities/invite/types";
|
import type { Invite } from "../../entities/invite/types";
|
||||||
import type { Service } from "../../entities/service/types";
|
import type { Service } from "../../entities/service/types";
|
||||||
import type { SyncStatus } from "../../entities/sync/types";
|
import type { SyncStatus } from "../../entities/sync/types";
|
||||||
|
|
@ -35,6 +35,7 @@ export interface TaskManagerWorkspaceSummary {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
managedBy?: TaskManagerWorkspaceManagedBy;
|
||||||
ownerEmail: string | null;
|
ownerEmail: string | null;
|
||||||
memberCount: number;
|
memberCount: number;
|
||||||
projects?: TaskManagerProjectSummary[];
|
projects?: TaskManagerProjectSummary[];
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { computeEffectiveAccess } from "../../entities/access/computeEffectiveAccess";
|
import { computeEffectiveAccess } from "../../entities/access/computeEffectiveAccess";
|
||||||
import type { EffectiveAccessResult, ServiceAccessException, ServiceGrant } from "../../entities/access/types";
|
import type { EffectiveAccessResult, ServiceAccessException, ServiceGrant } from "../../entities/access/types";
|
||||||
import type { Client } from "../../entities/client/types";
|
import type { Client, TaskManagerWorkspaceManagedBy } from "../../entities/client/types";
|
||||||
import type { Invite } from "../../entities/invite/types";
|
import type { Invite } from "../../entities/invite/types";
|
||||||
import { getServiceLaunchLink } from "../../entities/service/links";
|
import { getServiceLaunchLink } from "../../entities/service/links";
|
||||||
import type { LauncherServiceView, Service } from "../../entities/service/types";
|
import type { LauncherServiceView, Service } from "../../entities/service/types";
|
||||||
|
|
@ -72,6 +72,7 @@ export interface TaskManagerMembershipAssignment {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
workspaceName?: string | null;
|
workspaceName?: string | null;
|
||||||
role: "guest" | "member" | "admin";
|
role: "guest" | "member" | "admin";
|
||||||
|
managedBy?: TaskManagerWorkspaceManagedBy;
|
||||||
planeUserId?: string | null;
|
planeUserId?: string | null;
|
||||||
planeRole?: number | null;
|
planeRole?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|
@ -87,6 +88,7 @@ export interface TaskManagerProjectMembershipAssignment {
|
||||||
projectIdentifier?: string | null;
|
projectIdentifier?: string | null;
|
||||||
projectName?: string | null;
|
projectName?: string | null;
|
||||||
role: "guest" | "member" | "admin";
|
role: "guest" | "member" | "admin";
|
||||||
|
managedBy?: TaskManagerWorkspaceManagedBy;
|
||||||
planeUserId?: string | null;
|
planeUserId?: string | null;
|
||||||
planeRole?: number | null;
|
planeRole?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|
|
||||||
|
|
@ -989,6 +989,7 @@ function getClientTaskManagerWorkspaces(client: Client): ClientTaskManagerWorksp
|
||||||
slug: workspace.slug,
|
slug: workspace.slug,
|
||||||
name: workspace.name ?? null,
|
name: workspace.name ?? null,
|
||||||
isPrimary: workspace.isPrimary === true,
|
isPrimary: workspace.isPrimary === true,
|
||||||
|
managedBy: workspace.managedBy ?? "launcher",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -997,6 +998,7 @@ function getClientTaskManagerWorkspaces(client: Client): ClientTaskManagerWorksp
|
||||||
slug: taskManager.workspaceSlug,
|
slug: taskManager.workspaceSlug,
|
||||||
name: taskManager.workspaceName ?? null,
|
name: taskManager.workspaceName ?? null,
|
||||||
isPrimary: true,
|
isPrimary: true,
|
||||||
|
managedBy: "launcher",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1055,6 +1057,7 @@ function normalizeClientTaskManagerWorkspaceDraft(workspaces: ClientTaskManagerW
|
||||||
slug: workspace.slug,
|
slug: workspace.slug,
|
||||||
name: workspace.name ?? null,
|
name: workspace.name ?? null,
|
||||||
isPrimary: workspace.isPrimary === true,
|
isPrimary: workspace.isPrimary === true,
|
||||||
|
managedBy: workspace.managedBy ?? "launcher",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1781,6 +1784,7 @@ function ClientEditorModal({
|
||||||
slug: workspace.slug,
|
slug: workspace.slug,
|
||||||
name: workspace.name,
|
name: workspace.name,
|
||||||
isPrimary: currentWorkspaces.length === 0,
|
isPrimary: currentWorkspaces.length === 0,
|
||||||
|
managedBy: "launcher",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -1919,7 +1923,7 @@ function ClientEditorModal({
|
||||||
<span>
|
<span>
|
||||||
<strong>{workspace.name}</strong>
|
<strong>{workspace.name}</strong>
|
||||||
<small>
|
<small>
|
||||||
{workspace.slug} · {workspace.memberCount} участников
|
{workspace.slug} · {workspace.memberCount} участников · Launcher-managed
|
||||||
</small>
|
</small>
|
||||||
</span>
|
</span>
|
||||||
{selected ? (
|
{selected ? (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue