mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-29 06:28:23 +02:00
* feat(security): replace instant member-add with invitation acceptance flow Users invited to a workspace must now explicitly accept the invitation before becoming a member. This fixes the security vulnerability where knowing someone's email was enough to auto-register their runtime to your workspace. Changes: - Add workspace_invitation table with pending/accepted/declined/expired states - Replace CreateMember with CreateInvitation (same endpoint, new behavior) - Add accept/decline/revoke/list invitation API endpoints - Add invitation WS events for real-time notification - Frontend: invitation accept/decline UI in workspace switcher - Frontend: pending invitations section in members settings tab * fix(invitation): address PR review nits - Fix invitation:revoked listener to send event to invitee user (was no-op) - Remove duplicate queryClient2 in app-sidebar.tsx, reuse existing queryClient - Add expires_at > now() filter to ListPendingInvitationsByWorkspace query
64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
import { queryOptions } from "@tanstack/react-query";
|
|
import { api } from "../api";
|
|
|
|
export const workspaceKeys = {
|
|
all: (wsId: string) => ["workspaces", wsId] as const,
|
|
list: () => ["workspaces", "list"] as const,
|
|
members: (wsId: string) => ["workspaces", wsId, "members"] as const,
|
|
invitations: (wsId: string) => ["workspaces", wsId, "invitations"] as const,
|
|
myInvitations: () => ["invitations", "mine"] as const,
|
|
agents: (wsId: string) => ["workspaces", wsId, "agents"] as const,
|
|
skills: (wsId: string) => ["workspaces", wsId, "skills"] as const,
|
|
assigneeFrequency: (wsId: string) => ["workspaces", wsId, "assignee-frequency"] as const,
|
|
};
|
|
|
|
export function workspaceListOptions() {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.list(),
|
|
queryFn: () => api.listWorkspaces(),
|
|
});
|
|
}
|
|
|
|
export function memberListOptions(wsId: string) {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.members(wsId),
|
|
queryFn: () => api.listMembers(wsId),
|
|
});
|
|
}
|
|
|
|
export function agentListOptions(wsId: string) {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.agents(wsId),
|
|
queryFn: () =>
|
|
api.listAgents({ workspace_id: wsId, include_archived: true }),
|
|
});
|
|
}
|
|
|
|
export function skillListOptions(wsId: string) {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.skills(wsId),
|
|
queryFn: () => api.listSkills(),
|
|
});
|
|
}
|
|
|
|
export function invitationListOptions(wsId: string) {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.invitations(wsId),
|
|
queryFn: () => api.listWorkspaceInvitations(wsId),
|
|
});
|
|
}
|
|
|
|
export function myInvitationListOptions() {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.myInvitations(),
|
|
queryFn: () => api.listMyInvitations(),
|
|
});
|
|
}
|
|
|
|
export function assigneeFrequencyOptions(wsId: string) {
|
|
return queryOptions({
|
|
queryKey: workspaceKeys.assigneeFrequency(wsId),
|
|
queryFn: () => api.getAssigneeFrequency(),
|
|
});
|
|
}
|