@intelligo-dev/auth 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/client.js +25 -0
- package/dist/client.js.map +1 -0
- package/dist/edge.js +39 -0
- package/dist/edge.js.map +1 -0
- package/dist/helpers.js +248 -0
- package/dist/helpers.js.map +1 -0
- package/dist/impersonation.js +42 -0
- package/dist/impersonation.js.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding/errors.js +31 -0
- package/dist/onboarding/errors.js.map +1 -0
- package/dist/onboarding/schemas.js +24 -0
- package/dist/onboarding/schemas.js.map +1 -0
- package/dist/onboarding/service.js +147 -0
- package/dist/onboarding/service.js.map +1 -0
- package/dist/org-api.js +86 -0
- package/dist/org-api.js.map +1 -0
- package/dist/profile/errors.js +32 -0
- package/dist/profile/errors.js.map +1 -0
- package/dist/profile/schemas.js +20 -0
- package/dist/profile/schemas.js.map +1 -0
- package/dist/profile/service.js +157 -0
- package/dist/profile/service.js.map +1 -0
- package/dist/roles.js +17 -0
- package/dist/roles.js.map +1 -0
- package/dist/server.js +251 -0
- package/dist/server.js.map +1 -0
- package/dist/team/errors.js +31 -0
- package/dist/team/errors.js.map +1 -0
- package/dist/team/schemas.js +29 -0
- package/dist/team/schemas.js.map +1 -0
- package/dist/team/service.js +438 -0
- package/dist/team/service.js.map +1 -0
- package/dist/workspace/errors.js +32 -0
- package/dist/workspace/errors.js.map +1 -0
- package/dist/workspace/schemas.js +45 -0
- package/dist/workspace/schemas.js.map +1 -0
- package/dist/workspace/service.js +268 -0
- package/dist/workspace/service.js.map +1 -0
- package/dist/workspace-init.js +121 -0
- package/dist/workspace-init.js.map +1 -0
- package/package.json +58 -0
- package/src/client.ts +27 -0
- package/src/edge.ts +43 -0
- package/src/helpers.ts +317 -0
- package/src/impersonation.ts +57 -0
- package/src/index.ts +109 -0
- package/src/onboarding/errors.ts +52 -0
- package/src/onboarding/schemas.ts +27 -0
- package/src/onboarding/service.ts +174 -0
- package/src/org-api.ts +198 -0
- package/src/profile/errors.ts +58 -0
- package/src/profile/schemas.ts +23 -0
- package/src/profile/service.ts +208 -0
- package/src/roles.ts +17 -0
- package/src/server.ts +305 -0
- package/src/team/errors.ts +71 -0
- package/src/team/schemas.ts +35 -0
- package/src/team/service.ts +611 -0
- package/src/workspace/errors.ts +69 -0
- package/src/workspace/schemas.ts +57 -0
- package/src/workspace/service.ts +381 -0
- package/src/workspace-init.ts +140 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team management service — the durable business rules behind
|
|
3
|
+
* workspace membership and invitations, extracted from
|
|
4
|
+
* the product application’s team actions (first slice of the page/registry
|
|
5
|
+
* migration, section C1).
|
|
6
|
+
*
|
|
7
|
+
* Mirrors `createExecutions(ports)` (packages/executions/src/lifecycle.ts):
|
|
8
|
+
* a factory over optional ports, so this package's allowlisted
|
|
9
|
+
* dependency (`@intelligo-dev/core` only — see
|
|
10
|
+
* tests/architecture/dependency-direction.test.ts) never grows to
|
|
11
|
+
* include billing, email, or notifications. A consumer binds those in
|
|
12
|
+
* at its composition root:
|
|
13
|
+
*
|
|
14
|
+
* const teamService = createTeamService({
|
|
15
|
+
* checkMemberLimit: checkTeamMemberLimit, // @intelligo-dev/billing
|
|
16
|
+
* sendInvitationEmail: ..., // see note below
|
|
17
|
+
* notifyMemberJoined: triggerTeamMemberJoinedNotification, // @intelligo-dev/core/notifications
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* Authorization (`requireAuth`/`requireWorkspace`/`requireRole`) lives
|
|
21
|
+
* INSIDE each method, not at the transport. Every recognized failure
|
|
22
|
+
* throws `TeamServiceError` with a stable `code` — no
|
|
23
|
+
* revalidatePath/Sentry/next-intl/toast here; that shaping is the
|
|
24
|
+
* transport's job (a Server Action, a route handler).
|
|
25
|
+
*
|
|
26
|
+
* ---------------------------------------------------------------------
|
|
27
|
+
* Invitation-email duplication (investigated for this extraction)
|
|
28
|
+
* ---------------------------------------------------------------------
|
|
29
|
+
* `packages/auth/src/server.ts` configures the Better-Auth organization
|
|
30
|
+
* plugin's own `sendInvitationEmail` hook (EMAIL-06). Reading the
|
|
31
|
+
* installed org plugin (`better-auth@1.6.30`,
|
|
32
|
+
* `plugins/organization/routes/crud-invites.mjs`): every successful
|
|
33
|
+
* `/organization/invite-member` call — both the "new invitation" path
|
|
34
|
+
* and the "resend" path — unconditionally does
|
|
35
|
+
* `if (ctx.context.orgOptions.sendInvitationEmail) await
|
|
36
|
+
* runInBackgroundOrAwait(orgOptions.sendInvitationEmail(...))` once the
|
|
37
|
+
* invitation row is created. Because `server.ts` always sets that
|
|
38
|
+
* option, the hook fires on every `inviteMember()` call this service
|
|
39
|
+
* makes — there is no code path where it does not.
|
|
40
|
+
*
|
|
41
|
+
* ignite's current `actions/team.ts` ALSO calls
|
|
42
|
+
* `@intelligo-dev/core/email`'s `sendInvitationEmail` directly after the
|
|
43
|
+
* same `/organization/invite-member` call. That means **two** emails
|
|
44
|
+
* go out per invitation today. This is a live duplication bug, not a
|
|
45
|
+
* hypothetical.
|
|
46
|
+
*
|
|
47
|
+
* Decision: `inviteMember` below does NOT call `ports.sendInvitationEmail`
|
|
48
|
+
* by default — the org-plugin hook already covers it, so exactly one
|
|
49
|
+
* email fires per invitation as long as `server.ts`'s hook stays wired.
|
|
50
|
+
* The port is kept (and tested) for a consumer that disables or
|
|
51
|
+
* replaces that hook (e.g. a fork of `server.ts`, or a future
|
|
52
|
+
* environment where the org plugin's hook is intentionally left unset)
|
|
53
|
+
* — pass `sendInvitationEmail` and this service will use it. Bumping
|
|
54
|
+
* both at once will resume the duplication; do not turn the port back
|
|
55
|
+
* on without also removing the hook in `server.ts`.
|
|
56
|
+
*
|
|
57
|
+
* ---------------------------------------------------------------------
|
|
58
|
+
* Active-organization resolution (found the same way, via the
|
|
59
|
+
* integration test's real DB)
|
|
60
|
+
* ---------------------------------------------------------------------
|
|
61
|
+
* Better-Auth's organization plugin resolves "the caller's active
|
|
62
|
+
* workspace" from `session.activeOrganizationId` when a call omits an
|
|
63
|
+
* explicit `organizationId`. That column does not exist on this repo's
|
|
64
|
+
* Drizzle `sessions` schema (`packages/core/src/db/schema/auth.ts`), so
|
|
65
|
+
* every bare `auth.api.getFullOrganization({ headers })` call —
|
|
66
|
+
* ignite's original pattern in `listMembers`, `listInvitations`, and
|
|
67
|
+
* the sole-owner check in `leaveWorkspace` — resolves to *no*
|
|
68
|
+
* organization, regardless of what the caller most recently activated.
|
|
69
|
+
* `requireWorkspace()` (packages/auth/src/helpers.ts) never hits this:
|
|
70
|
+
* it has its own fallback that lists the caller's organizations and
|
|
71
|
+
* fetches the first one by explicit id. This service does the same —
|
|
72
|
+
* every `getFullOrganization` call below passes an explicit
|
|
73
|
+
* `organizationId` sourced from `requireWorkspace`/`requireRole`'s
|
|
74
|
+
* resolved `workspace.id` (or, in `acceptInvitation`, the invitation's
|
|
75
|
+
* own `organizationId`) rather than relying on session state. This is
|
|
76
|
+
* a behavior fix, not a stylistic change: without it, a fresh
|
|
77
|
+
* single-workspace user's own membership list comes back empty.
|
|
78
|
+
*/
|
|
79
|
+
import { headers } from "next/headers";
|
|
80
|
+
import { createLogger } from "@intelligo-dev/core/logger";
|
|
81
|
+
import { auth } from "../server";
|
|
82
|
+
import { requireAuth, requireRole, requireWorkspace } from "../helpers";
|
|
83
|
+
import { orgApi } from "../org-api";
|
|
84
|
+
import { inviteMemberSchema, updateRoleSchema } from "./schemas";
|
|
85
|
+
import { TeamServiceError, isTeamServiceError } from "./errors";
|
|
86
|
+
const log = createLogger("TeamService");
|
|
87
|
+
function errorMessage(error) {
|
|
88
|
+
return error instanceof Error ? error.message : String(error);
|
|
89
|
+
}
|
|
90
|
+
/** Maps requireAuth/requireWorkspace/requireRole failures to `forbidden`. */
|
|
91
|
+
function toForbidden(error) {
|
|
92
|
+
if (isTeamServiceError(error))
|
|
93
|
+
return error;
|
|
94
|
+
return new TeamServiceError("forbidden", errorMessage(error), {
|
|
95
|
+
cause: error,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function parseInput(schema, input) {
|
|
99
|
+
const result = schema.safeParse(input);
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
throw new TeamServiceError("invalid_input", result.error.issues.map((issue) => issue.message).join("; ") ||
|
|
102
|
+
"Invalid input", { cause: result.error });
|
|
103
|
+
}
|
|
104
|
+
return result.data;
|
|
105
|
+
}
|
|
106
|
+
export function createTeamService(ports = {}) {
|
|
107
|
+
async function callRequireAuth() {
|
|
108
|
+
try {
|
|
109
|
+
return await requireAuth();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw toForbidden(error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function callRequireWorkspace() {
|
|
116
|
+
try {
|
|
117
|
+
return await requireWorkspace();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
throw toForbidden(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function callRequireRole(allowedRoles) {
|
|
124
|
+
try {
|
|
125
|
+
return await requireRole(allowedRoles);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
throw toForbidden(error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Wraps a Better-Auth org-plugin call; unrecognized failures become `provider_error`. */
|
|
132
|
+
async function callOrgApi(context, fn) {
|
|
133
|
+
try {
|
|
134
|
+
return await fn();
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (isTeamServiceError(error))
|
|
138
|
+
throw error;
|
|
139
|
+
log.error("Org API call failed", { context, error: errorMessage(error) });
|
|
140
|
+
throw new TeamServiceError("provider_error", `Better-Auth organization API call failed (${context})`, { cause: error });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* List members of the caller's active workspace.
|
|
145
|
+
*/
|
|
146
|
+
async function listMembers() {
|
|
147
|
+
const { workspace } = await callRequireWorkspace();
|
|
148
|
+
const hdrs = await headers();
|
|
149
|
+
const org = await callOrgApi("getFullOrganization", () => auth.api.getFullOrganization({
|
|
150
|
+
headers: hdrs,
|
|
151
|
+
query: { organizationId: workspace.id },
|
|
152
|
+
}));
|
|
153
|
+
return (org?.members ?? []);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* List pending invitations for the caller's active workspace.
|
|
157
|
+
*/
|
|
158
|
+
async function listInvitations() {
|
|
159
|
+
const { workspace } = await callRequireWorkspace();
|
|
160
|
+
const hdrs = await headers();
|
|
161
|
+
const org = await callOrgApi("getFullOrganization", () => auth.api.getFullOrganization({
|
|
162
|
+
headers: hdrs,
|
|
163
|
+
query: { organizationId: workspace.id },
|
|
164
|
+
}));
|
|
165
|
+
return (org?.invitations ?? []);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Invite a member by email. Owner/admin only (TEAM-01).
|
|
169
|
+
*
|
|
170
|
+
* The member-limit port, when bound, gates the invite before it is
|
|
171
|
+
* created. Exactly one invitation email is sent — see the module doc
|
|
172
|
+
* comment on the email-duplication finding.
|
|
173
|
+
*/
|
|
174
|
+
async function inviteMember(input) {
|
|
175
|
+
const { workspace, user } = await callRequireRole(["owner", "admin"]);
|
|
176
|
+
const validated = parseInput(inviteMemberSchema, input);
|
|
177
|
+
const hdrs = await headers();
|
|
178
|
+
// Check team member limit (FLAG-05), via port only.
|
|
179
|
+
if (ports.checkMemberLimit) {
|
|
180
|
+
const org = await callOrgApi("getFullOrganization", () => auth.api.getFullOrganization({
|
|
181
|
+
headers: hdrs,
|
|
182
|
+
query: { organizationId: workspace.id },
|
|
183
|
+
}));
|
|
184
|
+
const currentMemberCount = org?.members?.length ?? 0;
|
|
185
|
+
const limitCheck = await ports.checkMemberLimit(workspace.id, currentMemberCount);
|
|
186
|
+
if (!limitCheck.allowed) {
|
|
187
|
+
throw new TeamServiceError("member_limit_reached", `Your plan allows up to ${limitCheck.limit} team member${limitCheck.limit === 1 ? "" : "s"}. Upgrade to add more members.`, { meta: { limit: limitCheck.limit } });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Better-Auth's own duplicate-pending-invite guard
|
|
191
|
+
// (USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION) runs inside this
|
|
192
|
+
// call and surfaces as a provider_error if tripped — ignite's
|
|
193
|
+
// action never added a second check on top of it, so neither does
|
|
194
|
+
// this service.
|
|
195
|
+
const result = await callOrgApi("invite-member", () => orgApi["/organization/invite-member"]({
|
|
196
|
+
headers: hdrs,
|
|
197
|
+
body: {
|
|
198
|
+
email: validated.email,
|
|
199
|
+
role: validated.role,
|
|
200
|
+
organizationId: workspace.id,
|
|
201
|
+
},
|
|
202
|
+
}));
|
|
203
|
+
if (ports.sendInvitationEmail) {
|
|
204
|
+
const invitationId = result?.id ?? "";
|
|
205
|
+
ports
|
|
206
|
+
.sendInvitationEmail({
|
|
207
|
+
to: validated.email,
|
|
208
|
+
inviterName: user.name || "A team member",
|
|
209
|
+
workspaceName: workspace.name,
|
|
210
|
+
invitationId,
|
|
211
|
+
role: validated.role,
|
|
212
|
+
})
|
|
213
|
+
.catch((err) => log.error("Failed to send invitation email", {
|
|
214
|
+
error: errorMessage(err),
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Accept invitation (TEAM-03).
|
|
221
|
+
*
|
|
222
|
+
* Defence-in-depth against stolen-invitationId attacks: we (a)
|
|
223
|
+
* require the caller to be authenticated, (b) verify the
|
|
224
|
+
* invitationId is in the caller's pending list before forwarding it
|
|
225
|
+
* to Better-Auth, and (c) confirm the user is actually a member of
|
|
226
|
+
* the resulting org after the call. Better-Auth's accept-invitation
|
|
227
|
+
* endpoint already checks the email match, but layering these guards
|
|
228
|
+
* means a future upstream regression can't silently grant
|
|
229
|
+
* cross-tenant access.
|
|
230
|
+
*/
|
|
231
|
+
async function acceptInvitation(invitationId) {
|
|
232
|
+
const { user } = await callRequireAuth();
|
|
233
|
+
const hdrs = await headers();
|
|
234
|
+
if (typeof invitationId !== "string" || invitationId.length === 0) {
|
|
235
|
+
throw new TeamServiceError("invalid_input", "Invalid invitation id");
|
|
236
|
+
}
|
|
237
|
+
// Pre-check: invitationId must be in the caller's pending list.
|
|
238
|
+
const pending = await callOrgApi("list-user-invitations", () => orgApi["/organization/list-user-invitations"]({ headers: hdrs }));
|
|
239
|
+
const matched = pending?.find((inv) => inv.id === invitationId);
|
|
240
|
+
if (!matched) {
|
|
241
|
+
log.warn("acceptInvitation rejected — invitation not in caller's pending list", { userId: user.id, invitationId });
|
|
242
|
+
throw new TeamServiceError("invitation_not_found", "Invitation not found");
|
|
243
|
+
}
|
|
244
|
+
await callOrgApi("accept-invitation", () => orgApi["/organization/accept-invitation"]({
|
|
245
|
+
headers: hdrs,
|
|
246
|
+
body: { invitationId },
|
|
247
|
+
}));
|
|
248
|
+
// Post-check: caller must now be a member of the target org.
|
|
249
|
+
const targetOrg = await callOrgApi("getFullOrganization", () => auth.api.getFullOrganization({
|
|
250
|
+
headers: hdrs,
|
|
251
|
+
query: { organizationId: matched.organizationId },
|
|
252
|
+
}));
|
|
253
|
+
const isMember = targetOrg?.members?.some((m) => m.userId === user.id);
|
|
254
|
+
if (!isMember) {
|
|
255
|
+
log.error("acceptInvitation post-check failed — not a member after accept", {
|
|
256
|
+
userId: user.id,
|
|
257
|
+
invitationId,
|
|
258
|
+
organizationId: matched.organizationId,
|
|
259
|
+
});
|
|
260
|
+
throw new TeamServiceError("accept_verification_failed", "Failed to accept invitation");
|
|
261
|
+
}
|
|
262
|
+
// Notify the workspace owner (fire-and-forget, non-blocking —
|
|
263
|
+
// invitation acceptance has already succeeded above).
|
|
264
|
+
//
|
|
265
|
+
// Reuses `targetOrg` from the post-check above (scoped to
|
|
266
|
+
// `matched.organizationId`, the org the caller just joined) rather
|
|
267
|
+
// than re-fetching "the active org" the way ignite's original
|
|
268
|
+
// action did: Better-Auth's org plugin needs a
|
|
269
|
+
// `sessions.activeOrganizationId` column to resolve an org from
|
|
270
|
+
// headers alone, and this repo's Drizzle schema for `sessions`
|
|
271
|
+
// does not define one, so a bare `getFullOrganization({ headers })`
|
|
272
|
+
// call resolves to no organization at all — this notify lookup
|
|
273
|
+
// would silently no-op every time. `targetOrg` sidesteps that by
|
|
274
|
+
// asking for the org we already know the answer for.
|
|
275
|
+
if (ports.notifyMemberJoined) {
|
|
276
|
+
try {
|
|
277
|
+
const session = await auth.api.getSession({ headers: hdrs });
|
|
278
|
+
if (session?.session && targetOrg) {
|
|
279
|
+
const owner = targetOrg.members?.find((m) => m.role === "owner");
|
|
280
|
+
if (owner) {
|
|
281
|
+
const memberName = session.user?.name || "A new member";
|
|
282
|
+
const memberEmail = session.user?.email || "";
|
|
283
|
+
ports
|
|
284
|
+
.notifyMemberJoined({
|
|
285
|
+
workspaceId: targetOrg.id,
|
|
286
|
+
workspaceName: targetOrg.name,
|
|
287
|
+
memberName,
|
|
288
|
+
memberEmail,
|
|
289
|
+
ownerId: owner.userId,
|
|
290
|
+
})
|
|
291
|
+
.catch((err) => log.error("Failed to send join notification", {
|
|
292
|
+
error: errorMessage(err),
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (notifError) {
|
|
298
|
+
log.error("Notification lookup failed", {
|
|
299
|
+
error: errorMessage(notifError),
|
|
300
|
+
});
|
|
301
|
+
// Non-blocking — invitation acceptance still succeeds.
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Reject/decline invitation (TEAM-04).
|
|
307
|
+
*
|
|
308
|
+
* Same caller-side guard as acceptInvitation: require auth and
|
|
309
|
+
* verify the invitationId is in the caller's pending list before
|
|
310
|
+
* forwarding.
|
|
311
|
+
*/
|
|
312
|
+
async function rejectInvitation(invitationId) {
|
|
313
|
+
await callRequireAuth();
|
|
314
|
+
const hdrs = await headers();
|
|
315
|
+
if (typeof invitationId !== "string" || invitationId.length === 0) {
|
|
316
|
+
throw new TeamServiceError("invalid_input", "Invalid invitation id");
|
|
317
|
+
}
|
|
318
|
+
const pending = await callOrgApi("list-user-invitations", () => orgApi["/organization/list-user-invitations"]({ headers: hdrs }));
|
|
319
|
+
if (!pending?.some((inv) => inv.id === invitationId)) {
|
|
320
|
+
throw new TeamServiceError("invitation_not_found", "Invitation not found");
|
|
321
|
+
}
|
|
322
|
+
await callOrgApi("reject-invitation", () => orgApi["/organization/reject-invitation"]({
|
|
323
|
+
headers: hdrs,
|
|
324
|
+
body: { invitationId },
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Cancel a pending invitation. Owner/admin only.
|
|
329
|
+
*/
|
|
330
|
+
async function cancelInvitation(invitationId) {
|
|
331
|
+
await callRequireRole(["owner", "admin"]);
|
|
332
|
+
const hdrs = await headers();
|
|
333
|
+
await callOrgApi("cancel-invitation", () => orgApi["/organization/cancel-invitation"]({
|
|
334
|
+
headers: hdrs,
|
|
335
|
+
body: { invitationId },
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Remove a member from workspace. Owner/admin only (TEAM-06).
|
|
340
|
+
*/
|
|
341
|
+
async function removeMember(memberId) {
|
|
342
|
+
const { workspace } = await callRequireRole(["owner", "admin"]);
|
|
343
|
+
const hdrs = await headers();
|
|
344
|
+
await callOrgApi("remove-member", () => orgApi["/organization/remove-member"]({
|
|
345
|
+
headers: hdrs,
|
|
346
|
+
body: { memberIdOrEmail: memberId, organizationId: workspace.id },
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Update member role. Owner/admin only (TEAM-05, TEAM-08).
|
|
351
|
+
*/
|
|
352
|
+
async function updateMemberRole(input) {
|
|
353
|
+
const { workspace } = await callRequireRole(["owner", "admin"]);
|
|
354
|
+
const validated = parseInput(updateRoleSchema, input);
|
|
355
|
+
const hdrs = await headers();
|
|
356
|
+
await callOrgApi("update-member-role", () => orgApi["/organization/update-member-role"]({
|
|
357
|
+
headers: hdrs,
|
|
358
|
+
body: {
|
|
359
|
+
memberId: validated.memberId,
|
|
360
|
+
role: validated.role,
|
|
361
|
+
organizationId: workspace.id,
|
|
362
|
+
},
|
|
363
|
+
}));
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Leave workspace (TEAM-09). Sole owner cannot leave — must transfer
|
|
367
|
+
* ownership first.
|
|
368
|
+
*/
|
|
369
|
+
async function leaveWorkspace() {
|
|
370
|
+
const { workspace, membership } = await callRequireWorkspace();
|
|
371
|
+
const hdrs = await headers();
|
|
372
|
+
if (membership.role === "owner") {
|
|
373
|
+
const org = await callOrgApi("getFullOrganization", () => auth.api.getFullOrganization({
|
|
374
|
+
headers: hdrs,
|
|
375
|
+
query: { organizationId: workspace.id },
|
|
376
|
+
}));
|
|
377
|
+
const owners = org?.members.filter((m) => m.role === "owner") ?? [];
|
|
378
|
+
if (owners.length <= 1) {
|
|
379
|
+
throw new TeamServiceError("sole_owner", "Cannot leave workspace as the sole owner. Transfer ownership first.");
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
await callOrgApi("leave", () => orgApi["/organization/leave"]({
|
|
383
|
+
headers: hdrs,
|
|
384
|
+
body: { organizationId: workspace.id },
|
|
385
|
+
}));
|
|
386
|
+
// Switch to another workspace, if one exists.
|
|
387
|
+
const orgs = await callOrgApi("list", () => orgApi["/organization/list"]({ headers: hdrs }));
|
|
388
|
+
if (orgs && orgs.length > 0) {
|
|
389
|
+
await callOrgApi("set-active", () => orgApi["/organization/set-active"]({
|
|
390
|
+
headers: hdrs,
|
|
391
|
+
body: { organizationId: orgs[0].id },
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Transfer workspace ownership to another member. Owner only (TEAM-07).
|
|
397
|
+
*/
|
|
398
|
+
async function transferOwnership(targetMemberId) {
|
|
399
|
+
const { workspace } = await callRequireRole(["owner"]);
|
|
400
|
+
const hdrs = await headers();
|
|
401
|
+
// Promote target to owner. Note: Better-Auth may handle demotion of
|
|
402
|
+
// the previous owner automatically. If not, the old owner remains
|
|
403
|
+
// as co-owner, which is acceptable (ported behavior).
|
|
404
|
+
await callOrgApi("update-member-role", () => orgApi["/organization/update-member-role"]({
|
|
405
|
+
headers: hdrs,
|
|
406
|
+
body: {
|
|
407
|
+
memberId: targetMemberId,
|
|
408
|
+
role: "owner",
|
|
409
|
+
organizationId: workspace.id,
|
|
410
|
+
},
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get the caller's own pending invitations (for the invitation
|
|
415
|
+
* accept page). No explicit requireAuth here — Better-Auth's
|
|
416
|
+
* list-user-invitations endpoint reads the session off `headers`
|
|
417
|
+
* itself; ported as-is from ignite's action.
|
|
418
|
+
*/
|
|
419
|
+
async function getUserInvitations() {
|
|
420
|
+
const hdrs = await headers();
|
|
421
|
+
const invitations = await callOrgApi("list-user-invitations", () => orgApi["/organization/list-user-invitations"]({ headers: hdrs }));
|
|
422
|
+
return invitations ?? [];
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
listMembers,
|
|
426
|
+
listInvitations,
|
|
427
|
+
inviteMember,
|
|
428
|
+
cancelInvitation,
|
|
429
|
+
removeMember,
|
|
430
|
+
updateMemberRole,
|
|
431
|
+
leaveWorkspace,
|
|
432
|
+
transferOwnership,
|
|
433
|
+
acceptInvitation,
|
|
434
|
+
rejectInvitation,
|
|
435
|
+
getUserInvitations,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/team/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace service error type.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `../team/errors.ts` exactly, one directory over: the
|
|
5
|
+
* workspace service (./service.ts) throws this for every failure it
|
|
6
|
+
* recognizes rather than returning an ad-hoc `{ success, error }`
|
|
7
|
+
* envelope — that shaping is a transport concern (a Server Action, a
|
|
8
|
+
* route handler) and belongs one layer up, alongside
|
|
9
|
+
* revalidatePath/Sentry/toast/i18n, none of which this package may
|
|
10
|
+
* depend on.
|
|
11
|
+
*/
|
|
12
|
+
export class WorkspaceServiceError extends Error {
|
|
13
|
+
constructor(code, message, options) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "WorkspaceServiceError";
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.meta = options?.meta;
|
|
18
|
+
if (options?.cause !== undefined) {
|
|
19
|
+
// ES2020 target predates the standard `cause` constructor option;
|
|
20
|
+
// assign it directly so `instanceof Error` consumers (and Node's
|
|
21
|
+
// own error inspection) still see it.
|
|
22
|
+
this.cause = options.cause;
|
|
23
|
+
}
|
|
24
|
+
// Restore prototype chain (extending built-ins across some
|
|
25
|
+
// transpilation targets loses `instanceof`).
|
|
26
|
+
Object.setPrototypeOf(this, WorkspaceServiceError.prototype);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function isWorkspaceServiceError(error) {
|
|
30
|
+
return error instanceof WorkspaceServiceError;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/workspace/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA4BH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAI9C,YACE,IAA+B,EAC/B,OAAe,EACf,OAA+D;QAE/D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC;QAC1B,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YACjC,kEAAkE;YAClE,iEAAiE;YACjE,sCAAsC;YACrC,IAA4B,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACtD,CAAC;QAED,2DAA2D;QAC3D,6CAA6C;QAC7C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAED,MAAM,UAAU,uBAAuB,CACrC,KAAc;IAEd,OAAO,KAAK,YAAY,qBAAqB,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Validation Schemas
|
|
3
|
+
*
|
|
4
|
+
* Zod schemas for workspace create/update inputs, shared by the
|
|
5
|
+
* workspace service and its transports.
|
|
6
|
+
*
|
|
7
|
+
* (Ported from the product application's workspace validation module —
|
|
8
|
+
* same semantics. Ignite's copy is retired at cutover.)
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
/**
|
|
12
|
+
* Create Workspace Schema
|
|
13
|
+
* Name + optional slug for new workspaces
|
|
14
|
+
*/
|
|
15
|
+
export const createWorkspaceSchema = z.object({
|
|
16
|
+
name: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(2, "Workspace name must be at least 2 characters")
|
|
19
|
+
.max(50, "Workspace name must be at most 50 characters"),
|
|
20
|
+
slug: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(2, "Slug must be at least 2 characters")
|
|
23
|
+
.max(50, "Slug must be at most 50 characters")
|
|
24
|
+
.regex(/^[a-z0-9-]+$/, "Slug can only contain lowercase letters, numbers, and hyphens")
|
|
25
|
+
.optional(), // Auto-generated from name if not provided
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Update Workspace Schema
|
|
29
|
+
* Optional name, slug, and logo for workspace settings
|
|
30
|
+
*/
|
|
31
|
+
export const updateWorkspaceSchema = z.object({
|
|
32
|
+
name: z
|
|
33
|
+
.string()
|
|
34
|
+
.min(2, "Workspace name must be at least 2 characters")
|
|
35
|
+
.max(50, "Workspace name must be at most 50 characters")
|
|
36
|
+
.optional(),
|
|
37
|
+
slug: z
|
|
38
|
+
.string()
|
|
39
|
+
.min(2, "Slug must be at least 2 characters")
|
|
40
|
+
.max(50, "Slug must be at most 50 characters")
|
|
41
|
+
.regex(/^[a-z0-9-]+$/, "Slug can only contain lowercase letters, numbers, and hyphens")
|
|
42
|
+
.optional(),
|
|
43
|
+
logo: z.string().url("Logo must be a valid URL").optional().nullable(),
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/workspace/schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,8CAA8C,CAAC;SACtD,GAAG,CAAC,EAAE,EAAE,8CAA8C,CAAC;IAC1D,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;SAC5C,GAAG,CAAC,EAAE,EAAE,oCAAoC,CAAC;SAC7C,KAAK,CACJ,cAAc,EACd,+DAA+D,CAChE;SACA,QAAQ,EAAE,EAAE,2CAA2C;CAC3D,CAAC,CAAC;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,8CAA8C,CAAC;SACtD,GAAG,CAAC,EAAE,EAAE,8CAA8C,CAAC;SACvD,QAAQ,EAAE;IACb,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,EAAE,oCAAoC,CAAC;SAC5C,GAAG,CAAC,EAAE,EAAE,oCAAoC,CAAC;SAC7C,KAAK,CACJ,cAAc,EACd,+DAA+D,CAChE;SACA,QAAQ,EAAE;IACb,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACvE,CAAC,CAAC"}
|