@shipfox/api-workspaces 2.0.0
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/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +97 -0
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/core/entities/index.d.ts +4 -0
- package/dist/core/entities/index.d.ts.map +1 -0
- package/dist/core/entities/index.js +3 -0
- package/dist/core/entities/index.js.map +1 -0
- package/dist/core/entities/invitation.d.ts +14 -0
- package/dist/core/entities/invitation.d.ts.map +1 -0
- package/dist/core/entities/invitation.js +3 -0
- package/dist/core/entities/invitation.js.map +1 -0
- package/dist/core/entities/membership.d.ts +10 -0
- package/dist/core/entities/membership.d.ts.map +1 -0
- package/dist/core/entities/membership.js +3 -0
- package/dist/core/entities/membership.js.map +1 -0
- package/dist/core/entities/workspace.d.ts +10 -0
- package/dist/core/entities/workspace.d.ts.map +1 -0
- package/dist/core/entities/workspace.js +3 -0
- package/dist/core/entities/workspace.js.map +1 -0
- package/dist/core/errors.d.ts +52 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +104 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/invitations.d.ts +51 -0
- package/dist/core/invitations.d.ts.map +1 -0
- package/dist/core/invitations.js +136 -0
- package/dist/core/invitations.js.map +1 -0
- package/dist/core/memberships.d.ts +9 -0
- package/dist/core/memberships.d.ts.map +1 -0
- package/dist/core/memberships.js +13 -0
- package/dist/core/memberships.js.map +1 -0
- package/dist/core/workspaces.d.ts +40 -0
- package/dist/core/workspaces.d.ts.map +1 -0
- package/dist/core/workspaces.js +85 -0
- package/dist/core/workspaces.js.map +1 -0
- package/dist/db/db.d.ts +1253 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +24 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +10 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/invitations.d.ts +47 -0
- package/dist/db/invitations.d.ts.map +1 -0
- package/dist/db/invitations.js +107 -0
- package/dist/db/invitations.js.map +1 -0
- package/dist/db/memberships.d.ts +44 -0
- package/dist/db/memberships.d.ts.map +1 -0
- package/dist/db/memberships.js +75 -0
- package/dist/db/memberships.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/invitations.d.ts +199 -0
- package/dist/db/schema/invitations.d.ts.map +1 -0
- package/dist/db/schema/invitations.js +47 -0
- package/dist/db/schema/invitations.js.map +1 -0
- package/dist/db/schema/memberships.d.ts +131 -0
- package/dist/db/schema/memberships.d.ts.map +1 -0
- package/dist/db/schema/memberships.js +35 -0
- package/dist/db/schema/memberships.js.map +1 -0
- package/dist/db/schema/outbox.d.ts +195 -0
- package/dist/db/schema/outbox.d.ts.map +1 -0
- package/dist/db/schema/outbox.js +5 -0
- package/dist/db/schema/outbox.js.map +1 -0
- package/dist/db/schema/workspaces.d.ts +115 -0
- package/dist/db/schema/workspaces.d.ts.map +1 -0
- package/dist/db/schema/workspaces.js +32 -0
- package/dist/db/schema/workspaces.js.map +1 -0
- package/dist/db/workspaces.d.ts +20 -0
- package/dist/db/workspaces.d.ts.map +1 -0
- package/dist/db/workspaces.js +53 -0
- package/dist/db/workspaces.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instance.d.ts +8 -0
- package/dist/metrics/instance.d.ts.map +1 -0
- package/dist/metrics/instance.js +41 -0
- package/dist/metrics/instance.js.map +1 -0
- package/dist/metrics/service.d.ts +2 -0
- package/dist/metrics/service.d.ts.map +1 -0
- package/dist/metrics/service.js +26 -0
- package/dist/metrics/service.js.map +1 -0
- package/dist/presentation/dto/index.d.ts +4 -0
- package/dist/presentation/dto/index.d.ts.map +1 -0
- package/dist/presentation/dto/index.js +5 -0
- package/dist/presentation/dto/index.js.map +1 -0
- package/dist/presentation/dto/invitation.d.ts +4 -0
- package/dist/presentation/dto/invitation.d.ts.map +1 -0
- package/dist/presentation/dto/invitation.js +15 -0
- package/dist/presentation/dto/invitation.js.map +1 -0
- package/dist/presentation/dto/membership.d.ts +5 -0
- package/dist/presentation/dto/membership.d.ts.map +1 -0
- package/dist/presentation/dto/membership.js +23 -0
- package/dist/presentation/dto/membership.js.map +1 -0
- package/dist/presentation/dto/workspace.d.ts +4 -0
- package/dist/presentation/dto/workspace.d.ts.map +1 -0
- package/dist/presentation/dto/workspace.js +12 -0
- package/dist/presentation/dto/workspace.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-invitation.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-invitation.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-invitation.js +39 -0
- package/dist/presentation/e2eRoutes/create-invitation.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-workspace.d.ts +2 -0
- package/dist/presentation/e2eRoutes/create-workspace.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-workspace.js +27 -0
- package/dist/presentation/e2eRoutes/create-workspace.js.map +1 -0
- package/dist/presentation/e2eRoutes/index.d.ts +3 -0
- package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/index.js +11 -0
- package/dist/presentation/e2eRoutes/index.js.map +1 -0
- package/dist/presentation/index.d.ts +2 -0
- package/dist/presentation/index.d.ts.map +1 -0
- package/dist/presentation/index.js +3 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/presentation/routes/index.d.ts +3 -0
- package/dist/presentation/routes/index.d.ts.map +1 -0
- package/dist/presentation/routes/index.js +23 -0
- package/dist/presentation/routes/index.js.map +1 -0
- package/dist/presentation/routes/invitations/accept.d.ts +2 -0
- package/dist/presentation/routes/invitations/accept.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/accept.js +67 -0
- package/dist/presentation/routes/invitations/accept.js.map +1 -0
- package/dist/presentation/routes/invitations/create.d.ts +2 -0
- package/dist/presentation/routes/invitations/create.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/create.js +66 -0
- package/dist/presentation/routes/invitations/create.js.map +1 -0
- package/dist/presentation/routes/invitations/index.d.ts +4 -0
- package/dist/presentation/routes/invitations/index.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/index.js +19 -0
- package/dist/presentation/routes/invitations/index.js.map +1 -0
- package/dist/presentation/routes/invitations/list.d.ts +2 -0
- package/dist/presentation/routes/invitations/list.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/list.js +58 -0
- package/dist/presentation/routes/invitations/list.js.map +1 -0
- package/dist/presentation/routes/invitations/preview.d.ts +2 -0
- package/dist/presentation/routes/invitations/preview.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/preview.js +47 -0
- package/dist/presentation/routes/invitations/preview.js.map +1 -0
- package/dist/presentation/routes/invitations/revoke.d.ts +2 -0
- package/dist/presentation/routes/invitations/revoke.d.ts.map +1 -0
- package/dist/presentation/routes/invitations/revoke.js +66 -0
- package/dist/presentation/routes/invitations/revoke.js.map +1 -0
- package/dist/presentation/routes/members/index.d.ts +3 -0
- package/dist/presentation/routes/members/index.d.ts.map +1 -0
- package/dist/presentation/routes/members/index.js +8 -0
- package/dist/presentation/routes/members/index.js.map +1 -0
- package/dist/presentation/routes/members/list.d.ts +2 -0
- package/dist/presentation/routes/members/list.d.ts.map +1 -0
- package/dist/presentation/routes/members/list.js +58 -0
- package/dist/presentation/routes/members/list.js.map +1 -0
- package/dist/presentation/routes/members/remove.d.ts +2 -0
- package/dist/presentation/routes/members/remove.d.ts.map +1 -0
- package/dist/presentation/routes/members/remove.js +71 -0
- package/dist/presentation/routes/members/remove.js.map +1 -0
- package/dist/presentation/routes/workspaces/create.d.ts +2 -0
- package/dist/presentation/routes/workspaces/create.d.ts.map +1 -0
- package/dist/presentation/routes/workspaces/create.js +36 -0
- package/dist/presentation/routes/workspaces/create.js.map +1 -0
- package/dist/presentation/routes/workspaces/index.d.ts +3 -0
- package/dist/presentation/routes/workspaces/index.d.ts.map +1 -0
- package/dist/presentation/routes/workspaces/index.js +4 -0
- package/dist/presentation/routes/workspaces/index.js.map +1 -0
- package/dist/presentation/routes/workspaces/list.d.ts +2 -0
- package/dist/presentation/routes/workspaces/list.d.ts.map +1 -0
- package/dist/presentation/routes/workspaces/list.js +32 -0
- package/dist/presentation/routes/workspaces/list.js.map +1 -0
- package/dist/presentation/subscribers/index.d.ts +2 -0
- package/dist/presentation/subscribers/index.d.ts.map +1 -0
- package/dist/presentation/subscribers/index.js +3 -0
- package/dist/presentation/subscribers/index.js.map +1 -0
- package/dist/presentation/subscribers/on-invitation-send-requested.d.ts +3 -0
- package/dist/presentation/subscribers/on-invitation-send-requested.d.ts.map +1 -0
- package/dist/presentation/subscribers/on-invitation-send-requested.js +16 -0
- package/dist/presentation/subscribers/on-invitation-send-requested.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_initial.sql +9 -0
- package/drizzle/0001_memberships_invitations.sql +30 -0
- package/drizzle/0002_membership_user_snapshots.sql +40 -0
- package/drizzle/0003_outgoing_enchantress.sql +16 -0
- package/drizzle/meta/0000_snapshot.json +80 -0
- package/drizzle/meta/0001_snapshot.json +705 -0
- package/drizzle/meta/0003_snapshot.json +442 -0
- package/drizzle/meta/_journal.json +34 -0
- package/drizzle.config.ts +7 -0
- package/package.json +71 -0
- package/src/config.ts +51 -0
- package/src/core/entities/index.ts +3 -0
- package/src/core/entities/invitation.ts +13 -0
- package/src/core/entities/membership.ts +9 -0
- package/src/core/entities/workspace.ts +10 -0
- package/src/core/errors.ts +118 -0
- package/src/core/index.ts +35 -0
- package/src/core/invitations.test.ts +251 -0
- package/src/core/invitations.ts +191 -0
- package/src/core/memberships.test.ts +77 -0
- package/src/core/memberships.ts +17 -0
- package/src/core/workspaces.test.ts +72 -0
- package/src/core/workspaces.ts +126 -0
- package/src/db/db.ts +24 -0
- package/src/db/index.ts +43 -0
- package/src/db/invitations.test.ts +204 -0
- package/src/db/invitations.ts +235 -0
- package/src/db/memberships.test.ts +107 -0
- package/src/db/memberships.ts +139 -0
- package/src/db/schema/common.ts +5 -0
- package/src/db/schema/invitations.ts +47 -0
- package/src/db/schema/memberships.ts +39 -0
- package/src/db/schema/outbox.ts +4 -0
- package/src/db/schema/workspaces.ts +33 -0
- package/src/db/workspaces.test.ts +114 -0
- package/src/db/workspaces.ts +81 -0
- package/src/index.test.ts +25 -0
- package/src/index.ts +43 -0
- package/src/metrics/index.ts +1 -0
- package/src/metrics/instance.test.ts +74 -0
- package/src/metrics/instance.ts +57 -0
- package/src/metrics/service.ts +26 -0
- package/src/presentation/dto/index.ts +3 -0
- package/src/presentation/dto/invitation.ts +16 -0
- package/src/presentation/dto/membership.ts +27 -0
- package/src/presentation/dto/workspace.ts +13 -0
- package/src/presentation/e2eRoutes/create-invitation.ts +44 -0
- package/src/presentation/e2eRoutes/create-workspace.ts +30 -0
- package/src/presentation/e2eRoutes/index.ts +8 -0
- package/src/presentation/index.ts +1 -0
- package/src/presentation/routes/index.ts +23 -0
- package/src/presentation/routes/invitations/accept.test.ts +154 -0
- package/src/presentation/routes/invitations/accept.ts +70 -0
- package/src/presentation/routes/invitations/create.test.ts +99 -0
- package/src/presentation/routes/invitations/create.ts +60 -0
- package/src/presentation/routes/invitations/index.ts +17 -0
- package/src/presentation/routes/invitations/list.test.ts +94 -0
- package/src/presentation/routes/invitations/list.ts +53 -0
- package/src/presentation/routes/invitations/preview.test.ts +140 -0
- package/src/presentation/routes/invitations/preview.ts +46 -0
- package/src/presentation/routes/invitations/revoke.test.ts +99 -0
- package/src/presentation/routes/invitations/revoke.ts +64 -0
- package/src/presentation/routes/members/index.ts +5 -0
- package/src/presentation/routes/members/list.test.ts +95 -0
- package/src/presentation/routes/members/list.ts +53 -0
- package/src/presentation/routes/members/remove.test.ts +110 -0
- package/src/presentation/routes/members/remove.ts +66 -0
- package/src/presentation/routes/workspaces/create.test.ts +87 -0
- package/src/presentation/routes/workspaces/create.ts +36 -0
- package/src/presentation/routes/workspaces/index.ts +2 -0
- package/src/presentation/routes/workspaces/list.test.ts +56 -0
- package/src/presentation/routes/workspaces/list.ts +29 -0
- package/src/presentation/subscribers/index.ts +1 -0
- package/src/presentation/subscribers/invitation-send-requested.test.ts +64 -0
- package/src/presentation/subscribers/on-invitation-send-requested.ts +15 -0
- package/test/env.ts +8 -0
- package/test/factories/index.ts +3 -0
- package/test/factories/membership.ts +24 -0
- package/test/factories/user.ts +15 -0
- package/test/factories/workspace.ts +18 -0
- package/test/globalSetup.ts +15 -0
- package/test/index.ts +3 -0
- package/test/routes.ts +255 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { acceptInvitationBodySchema, acceptInvitationResponseSchema } from '@shipfox/api-workspaces-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { InvitationEmailMismatchError, TokenAlreadyUsedError, TokenExpiredError, TokenInvalidError } from '#core/errors.js';
|
|
5
|
+
import { acceptWorkspaceInvitation } from '#core/index.js';
|
|
6
|
+
export const acceptInvitationRoute = defineRoute({
|
|
7
|
+
method: 'POST',
|
|
8
|
+
path: '/accept',
|
|
9
|
+
description: 'Accept an invitation to join a workspace.',
|
|
10
|
+
auth: AUTH_USER,
|
|
11
|
+
schema: {
|
|
12
|
+
body: acceptInvitationBodySchema,
|
|
13
|
+
response: {
|
|
14
|
+
200: acceptInvitationResponseSchema,
|
|
15
|
+
201: acceptInvitationResponseSchema
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
errorHandler: (error)=>{
|
|
19
|
+
if (error instanceof TokenInvalidError) {
|
|
20
|
+
throw new ClientError('Invitation token is invalid', 'token-invalid', {
|
|
21
|
+
status: 410
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (error instanceof TokenAlreadyUsedError) {
|
|
25
|
+
throw new ClientError('Invitation has already been accepted', 'token-already-used', {
|
|
26
|
+
status: 410
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (error instanceof TokenExpiredError) {
|
|
30
|
+
throw new ClientError('Invitation has expired', 'token-expired', {
|
|
31
|
+
status: 410
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (error instanceof InvitationEmailMismatchError) {
|
|
35
|
+
throw new ClientError('Invitation email does not match authenticated user', 'forbidden', {
|
|
36
|
+
status: 403
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
},
|
|
41
|
+
handler: async (request, reply)=>{
|
|
42
|
+
const client = getUserContext(request);
|
|
43
|
+
if (!client) {
|
|
44
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
45
|
+
status: 401
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const { token } = request.body;
|
|
49
|
+
const result = await acceptWorkspaceInvitation({
|
|
50
|
+
token,
|
|
51
|
+
userId: client.userId,
|
|
52
|
+
email: client.email,
|
|
53
|
+
name: client.name
|
|
54
|
+
});
|
|
55
|
+
reply.code(result.alreadyMember ? 200 : 201);
|
|
56
|
+
return {
|
|
57
|
+
membership: {
|
|
58
|
+
id: result.membership.id,
|
|
59
|
+
user_id: result.membership.userId,
|
|
60
|
+
workspace_id: result.membership.workspaceId
|
|
61
|
+
},
|
|
62
|
+
already_member: result.alreadyMember
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=accept.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/accept.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {\n acceptInvitationBodySchema,\n acceptInvitationResponseSchema,\n} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {\n InvitationEmailMismatchError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n} from '#core/errors.js';\nimport {acceptWorkspaceInvitation} from '#core/index.js';\n\nexport const acceptInvitationRoute = defineRoute({\n method: 'POST',\n path: '/accept',\n description: 'Accept an invitation to join a workspace.',\n auth: AUTH_USER,\n schema: {\n body: acceptInvitationBodySchema,\n response: {\n 200: acceptInvitationResponseSchema,\n 201: acceptInvitationResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof TokenInvalidError) {\n throw new ClientError('Invitation token is invalid', 'token-invalid', {status: 410});\n }\n if (error instanceof TokenAlreadyUsedError) {\n throw new ClientError('Invitation has already been accepted', 'token-already-used', {\n status: 410,\n });\n }\n if (error instanceof TokenExpiredError) {\n throw new ClientError('Invitation has expired', 'token-expired', {status: 410});\n }\n if (error instanceof InvitationEmailMismatchError) {\n throw new ClientError('Invitation email does not match authenticated user', 'forbidden', {\n status: 403,\n });\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {token} = request.body;\n const result = await acceptWorkspaceInvitation({\n token,\n userId: client.userId,\n email: client.email,\n name: client.name,\n });\n\n reply.code(result.alreadyMember ? 200 : 201);\n return {\n membership: {\n id: result.membership.id,\n user_id: result.membership.userId,\n workspace_id: result.membership.workspaceId,\n },\n already_member: result.alreadyMember,\n };\n },\n});\n"],"names":["AUTH_USER","getUserContext","acceptInvitationBodySchema","acceptInvitationResponseSchema","ClientError","defineRoute","InvitationEmailMismatchError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","acceptWorkspaceInvitation","acceptInvitationRoute","method","path","description","auth","schema","body","response","errorHandler","error","status","handler","request","reply","client","token","result","userId","email","name","code","alreadyMember","membership","id","user_id","workspace_id","workspaceId","already_member"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SACEC,0BAA0B,EAC1BC,8BAA8B,QACzB,8BAA8B;AACrC,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SACEC,4BAA4B,EAC5BC,qBAAqB,EACrBC,iBAAiB,EACjBC,iBAAiB,QACZ,kBAAkB;AACzB,SAAQC,yBAAyB,QAAO,iBAAiB;AAEzD,OAAO,MAAMC,wBAAwBN,YAAY;IAC/CO,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMf;IACNgB,QAAQ;QACNC,MAAMf;QACNgB,UAAU;YACR,KAAKf;YACL,KAAKA;QACP;IACF;IACAgB,cAAc,CAACC;QACb,IAAIA,iBAAiBX,mBAAmB;YACtC,MAAM,IAAIL,YAAY,+BAA+B,iBAAiB;gBAACiB,QAAQ;YAAG;QACpF;QACA,IAAID,iBAAiBb,uBAAuB;YAC1C,MAAM,IAAIH,YAAY,wCAAwC,sBAAsB;gBAClFiB,QAAQ;YACV;QACF;QACA,IAAID,iBAAiBZ,mBAAmB;YACtC,MAAM,IAAIJ,YAAY,0BAA0B,iBAAiB;gBAACiB,QAAQ;YAAG;QAC/E;QACA,IAAID,iBAAiBd,8BAA8B;YACjD,MAAM,IAAIF,YAAY,sDAAsD,aAAa;gBACvFiB,QAAQ;YACV;QACF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAASxB,eAAesB;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAIrB,YAAY,2BAA2B,gBAAgB;gBAACiB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACK,KAAK,EAAC,GAAGH,QAAQN,IAAI;QAC5B,MAAMU,SAAS,MAAMjB,0BAA0B;YAC7CgB;YACAE,QAAQH,OAAOG,MAAM;YACrBC,OAAOJ,OAAOI,KAAK;YACnBC,MAAML,OAAOK,IAAI;QACnB;QAEAN,MAAMO,IAAI,CAACJ,OAAOK,aAAa,GAAG,MAAM;QACxC,OAAO;YACLC,YAAY;gBACVC,IAAIP,OAAOM,UAAU,CAACC,EAAE;gBACxBC,SAASR,OAAOM,UAAU,CAACL,MAAM;gBACjCQ,cAAcT,OAAOM,UAAU,CAACI,WAAW;YAC7C;YACAC,gBAAgBX,OAAOK,aAAa;QACtC;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/invitations/create.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,qBAAqB,iDA8ChC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { createInvitationBodySchema, invitationDtoSchema } from '@shipfox/api-workspaces-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { MembershipRequiredError, OpenInvitationExistsError, WorkspaceInactiveError, WorkspaceNotFoundError } from '#core/errors.js';
|
|
6
|
+
import { createWorkspaceInvitation } from '#core/index.js';
|
|
7
|
+
import { toInvitationDto } from '#presentation/dto/index.js';
|
|
8
|
+
export const createInvitationRoute = defineRoute({
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: '/',
|
|
11
|
+
description: 'Invite someone to join a workspace by email.',
|
|
12
|
+
auth: AUTH_USER,
|
|
13
|
+
schema: {
|
|
14
|
+
params: z.object({
|
|
15
|
+
workspaceId: z.string().uuid()
|
|
16
|
+
}),
|
|
17
|
+
body: createInvitationBodySchema,
|
|
18
|
+
response: {
|
|
19
|
+
201: invitationDtoSchema
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
errorHandler: (error)=>{
|
|
23
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
24
|
+
throw new ClientError('Workspace not found', 'not-found', {
|
|
25
|
+
status: 404
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (error instanceof MembershipRequiredError) {
|
|
29
|
+
throw new ClientError('Not a member of this workspace', 'forbidden', {
|
|
30
|
+
status: 403
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (error instanceof OpenInvitationExistsError) {
|
|
34
|
+
throw new ClientError(error.message, 'open-invitation-exists', {
|
|
35
|
+
status: 409
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (error instanceof WorkspaceInactiveError) {
|
|
39
|
+
throw new ClientError('Workspace is not active', 'workspace-inactive', {
|
|
40
|
+
status: 403
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
},
|
|
45
|
+
handler: async (request, reply)=>{
|
|
46
|
+
const client = getUserContext(request);
|
|
47
|
+
if (!client) {
|
|
48
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
49
|
+
status: 401
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const { workspaceId } = request.params;
|
|
53
|
+
const { email } = request.body;
|
|
54
|
+
const invitation = await createWorkspaceInvitation({
|
|
55
|
+
workspaceId,
|
|
56
|
+
email,
|
|
57
|
+
invitedByUserId: client.userId,
|
|
58
|
+
invitedByDisplay: client.name ?? 'A workspace admin',
|
|
59
|
+
invitedByMemberships: client.memberships
|
|
60
|
+
});
|
|
61
|
+
reply.code(201);
|
|
62
|
+
return toInvitationDto(invitation);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/create.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {createInvitationBodySchema, invitationDtoSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {\n MembershipRequiredError,\n OpenInvitationExistsError,\n WorkspaceInactiveError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nimport {createWorkspaceInvitation} from '#core/index.js';\nimport {toInvitationDto} from '#presentation/dto/index.js';\n\nexport const createInvitationRoute = defineRoute({\n method: 'POST',\n path: '/',\n description: 'Invite someone to join a workspace by email.',\n auth: AUTH_USER,\n schema: {\n params: z.object({workspaceId: z.string().uuid()}),\n body: createInvitationBodySchema,\n response: {\n 201: invitationDtoSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof MembershipRequiredError) {\n throw new ClientError('Not a member of this workspace', 'forbidden', {status: 403});\n }\n if (error instanceof OpenInvitationExistsError) {\n throw new ClientError(error.message, 'open-invitation-exists', {status: 409});\n }\n if (error instanceof WorkspaceInactiveError) {\n throw new ClientError('Workspace is not active', 'workspace-inactive', {status: 403});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {workspaceId} = request.params;\n const {email} = request.body;\n const invitation = await createWorkspaceInvitation({\n workspaceId,\n email,\n invitedByUserId: client.userId,\n invitedByDisplay: client.name ?? 'A workspace admin',\n invitedByMemberships: client.memberships,\n });\n\n reply.code(201);\n return toInvitationDto(invitation);\n },\n});\n"],"names":["AUTH_USER","getUserContext","createInvitationBodySchema","invitationDtoSchema","ClientError","defineRoute","z","MembershipRequiredError","OpenInvitationExistsError","WorkspaceInactiveError","WorkspaceNotFoundError","createWorkspaceInvitation","toInvitationDto","createInvitationRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","body","response","errorHandler","error","status","message","handler","request","reply","client","email","invitation","invitedByUserId","userId","invitedByDisplay","name","invitedByMemberships","memberships","code"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,0BAA0B,EAAEC,mBAAmB,QAAO,8BAA8B;AAC5F,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,uBAAuB,EACvBC,yBAAyB,EACzBC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AACzB,SAAQC,yBAAyB,QAAO,iBAAiB;AACzD,SAAQC,eAAe,QAAO,6BAA6B;AAE3D,OAAO,MAAMC,wBAAwBR,YAAY;IAC/CS,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMjB;IACNkB,QAAQ;QACNC,QAAQb,EAAEc,MAAM,CAAC;YAACC,aAAaf,EAAEgB,MAAM,GAAGC,IAAI;QAAE;QAChDC,MAAMtB;QACNuB,UAAU;YACR,KAAKtB;QACP;IACF;IACAuB,cAAc,CAACC;QACb,IAAIA,iBAAiBjB,wBAAwB;YAC3C,MAAM,IAAIN,YAAY,uBAAuB,aAAa;gBAACwB,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBpB,yBAAyB;YAC5C,MAAM,IAAIH,YAAY,kCAAkC,aAAa;gBAACwB,QAAQ;YAAG;QACnF;QACA,IAAID,iBAAiBnB,2BAA2B;YAC9C,MAAM,IAAIJ,YAAYuB,MAAME,OAAO,EAAE,0BAA0B;gBAACD,QAAQ;YAAG;QAC7E;QACA,IAAID,iBAAiBlB,wBAAwB;YAC3C,MAAM,IAAIL,YAAY,2BAA2B,sBAAsB;gBAACwB,QAAQ;YAAG;QACrF;QACA,MAAMD;IACR;IACAG,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAAShC,eAAe8B;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAI7B,YAAY,2BAA2B,gBAAgB;gBAACwB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACP,WAAW,EAAC,GAAGU,QAAQZ,MAAM;QACpC,MAAM,EAACe,KAAK,EAAC,GAAGH,QAAQP,IAAI;QAC5B,MAAMW,aAAa,MAAMxB,0BAA0B;YACjDU;YACAa;YACAE,iBAAiBH,OAAOI,MAAM;YAC9BC,kBAAkBL,OAAOM,IAAI,IAAI;YACjCC,sBAAsBP,OAAOQ,WAAW;QAC1C;QAEAT,MAAMU,IAAI,CAAC;QACX,OAAO9B,gBAAgBuB;IACzB;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/invitations/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAE,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAOvE,eAAO,MAAM,gCAAgC,EAAE,eAAe,EAI7D,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,UAGpC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { acceptInvitationRoute } from './accept.js';
|
|
2
|
+
import { createInvitationRoute } from './create.js';
|
|
3
|
+
import { listInvitationsRoute } from './list.js';
|
|
4
|
+
import { previewInvitationRoute } from './preview.js';
|
|
5
|
+
import { revokeInvitationRoute } from './revoke.js';
|
|
6
|
+
export const invitationsWorkspaceScopedRoutes = [
|
|
7
|
+
createInvitationRoute,
|
|
8
|
+
listInvitationsRoute,
|
|
9
|
+
revokeInvitationRoute
|
|
10
|
+
];
|
|
11
|
+
export const invitationsAcceptGroup = {
|
|
12
|
+
prefix: '/invitations',
|
|
13
|
+
routes: [
|
|
14
|
+
acceptInvitationRoute,
|
|
15
|
+
previewInvitationRoute
|
|
16
|
+
]
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/index.ts"],"sourcesContent":["import type {RouteDefinition, RouteGroup} from '@shipfox/node-fastify';\nimport {acceptInvitationRoute} from './accept.js';\nimport {createInvitationRoute} from './create.js';\nimport {listInvitationsRoute} from './list.js';\nimport {previewInvitationRoute} from './preview.js';\nimport {revokeInvitationRoute} from './revoke.js';\n\nexport const invitationsWorkspaceScopedRoutes: RouteDefinition[] = [\n createInvitationRoute,\n listInvitationsRoute,\n revokeInvitationRoute,\n];\n\nexport const invitationsAcceptGroup: RouteGroup = {\n prefix: '/invitations',\n routes: [acceptInvitationRoute, previewInvitationRoute],\n};\n"],"names":["acceptInvitationRoute","createInvitationRoute","listInvitationsRoute","previewInvitationRoute","revokeInvitationRoute","invitationsWorkspaceScopedRoutes","invitationsAcceptGroup","prefix","routes"],"mappings":"AACA,SAAQA,qBAAqB,QAAO,cAAc;AAClD,SAAQC,qBAAqB,QAAO,cAAc;AAClD,SAAQC,oBAAoB,QAAO,YAAY;AAC/C,SAAQC,sBAAsB,QAAO,eAAe;AACpD,SAAQC,qBAAqB,QAAO,cAAc;AAElD,OAAO,MAAMC,mCAAsD;IACjEJ;IACAC;IACAE;CACD,CAAC;AAEF,OAAO,MAAME,yBAAqC;IAChDC,QAAQ;IACRC,QAAQ;QAACR;QAAuBG;KAAuB;AACzD,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/invitations/list.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,oBAAoB,iDAwC/B,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { listInvitationsResponseSchema } from '@shipfox/api-workspaces-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { MembershipRequiredError, WorkspaceInactiveError, WorkspaceNotFoundError } from '#core/errors.js';
|
|
6
|
+
import { listWorkspaceInvitations } from '#core/index.js';
|
|
7
|
+
import { toInvitationDto } from '#presentation/dto/index.js';
|
|
8
|
+
export const listInvitationsRoute = defineRoute({
|
|
9
|
+
method: 'GET',
|
|
10
|
+
path: '/',
|
|
11
|
+
description: 'List pending invitations for a workspace.',
|
|
12
|
+
auth: AUTH_USER,
|
|
13
|
+
schema: {
|
|
14
|
+
params: z.object({
|
|
15
|
+
workspaceId: z.string().uuid()
|
|
16
|
+
}),
|
|
17
|
+
response: {
|
|
18
|
+
200: listInvitationsResponseSchema
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
errorHandler: (error)=>{
|
|
22
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
23
|
+
throw new ClientError('Workspace not found', 'not-found', {
|
|
24
|
+
status: 404
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof MembershipRequiredError) {
|
|
28
|
+
throw new ClientError('Not a member of this workspace', 'forbidden', {
|
|
29
|
+
status: 403
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof WorkspaceInactiveError) {
|
|
33
|
+
throw new ClientError('Workspace is not active', 'workspace-inactive', {
|
|
34
|
+
status: 403
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
},
|
|
39
|
+
handler: async (request)=>{
|
|
40
|
+
const client = getUserContext(request);
|
|
41
|
+
if (!client) {
|
|
42
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
43
|
+
status: 401
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const { workspaceId } = request.params;
|
|
47
|
+
const invitations = await listWorkspaceInvitations({
|
|
48
|
+
workspaceId,
|
|
49
|
+
requesterUserId: client.userId,
|
|
50
|
+
requesterMemberships: client.memberships
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
invitations: invitations.map(toInvitationDto)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/list.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {listInvitationsResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {\n MembershipRequiredError,\n WorkspaceInactiveError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nimport {listWorkspaceInvitations} from '#core/index.js';\nimport {toInvitationDto} from '#presentation/dto/index.js';\n\nexport const listInvitationsRoute = defineRoute({\n method: 'GET',\n path: '/',\n description: 'List pending invitations for a workspace.',\n auth: AUTH_USER,\n schema: {\n params: z.object({workspaceId: z.string().uuid()}),\n response: {\n 200: listInvitationsResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof MembershipRequiredError) {\n throw new ClientError('Not a member of this workspace', 'forbidden', {status: 403});\n }\n if (error instanceof WorkspaceInactiveError) {\n throw new ClientError('Workspace is not active', 'workspace-inactive', {status: 403});\n }\n throw error;\n },\n handler: async (request) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {workspaceId} = request.params;\n const invitations = await listWorkspaceInvitations({\n workspaceId,\n requesterUserId: client.userId,\n requesterMemberships: client.memberships,\n });\n\n return {\n invitations: invitations.map(toInvitationDto),\n };\n },\n});\n"],"names":["AUTH_USER","getUserContext","listInvitationsResponseSchema","ClientError","defineRoute","z","MembershipRequiredError","WorkspaceInactiveError","WorkspaceNotFoundError","listWorkspaceInvitations","toInvitationDto","listInvitationsRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","response","errorHandler","error","status","handler","request","client","invitations","requesterUserId","userId","requesterMemberships","memberships","map"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,6BAA6B,QAAO,8BAA8B;AAC1E,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,uBAAuB,EACvBC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AACzB,SAAQC,wBAAwB,QAAO,iBAAiB;AACxD,SAAQC,eAAe,QAAO,6BAA6B;AAE3D,OAAO,MAAMC,uBAAuBP,YAAY;IAC9CQ,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMf;IACNgB,QAAQ;QACNC,QAAQZ,EAAEa,MAAM,CAAC;YAACC,aAAad,EAAEe,MAAM,GAAGC,IAAI;QAAE;QAChDC,UAAU;YACR,KAAKpB;QACP;IACF;IACAqB,cAAc,CAACC;QACb,IAAIA,iBAAiBhB,wBAAwB;YAC3C,MAAM,IAAIL,YAAY,uBAAuB,aAAa;gBAACsB,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBlB,yBAAyB;YAC5C,MAAM,IAAIH,YAAY,kCAAkC,aAAa;gBAACsB,QAAQ;YAAG;QACnF;QACA,IAAID,iBAAiBjB,wBAAwB;YAC3C,MAAM,IAAIJ,YAAY,2BAA2B,sBAAsB;gBAACsB,QAAQ;YAAG;QACrF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC;QACd,MAAMC,SAAS3B,eAAe0B;QAC9B,IAAI,CAACC,QAAQ;YACX,MAAM,IAAIzB,YAAY,2BAA2B,gBAAgB;gBAACsB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACN,WAAW,EAAC,GAAGQ,QAAQV,MAAM;QACpC,MAAMY,cAAc,MAAMpB,yBAAyB;YACjDU;YACAW,iBAAiBF,OAAOG,MAAM;YAC9BC,sBAAsBJ,OAAOK,WAAW;QAC1C;QAEA,OAAO;YACLJ,aAAaA,YAAYK,GAAG,CAACxB;QAC/B;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/invitations/preview.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,sBAAsB,iDAsCjC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { previewInvitationQuerySchema, previewInvitationResponseSchema } from '@shipfox/api-workspaces-dto';
|
|
2
|
+
import { defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { previewInvitation } from '#core/index.js';
|
|
4
|
+
export const previewInvitationRoute = defineRoute({
|
|
5
|
+
method: 'GET',
|
|
6
|
+
path: '/preview',
|
|
7
|
+
description: 'Preview an invitation by opaque token (public, no auth).',
|
|
8
|
+
schema: {
|
|
9
|
+
querystring: previewInvitationQuerySchema,
|
|
10
|
+
response: {
|
|
11
|
+
200: previewInvitationResponseSchema
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
handler: async (request)=>{
|
|
15
|
+
const result = await previewInvitation({
|
|
16
|
+
token: request.query.token
|
|
17
|
+
});
|
|
18
|
+
switch(result.status){
|
|
19
|
+
case 'pending':
|
|
20
|
+
return {
|
|
21
|
+
status: 'pending',
|
|
22
|
+
workspace_id: result.workspaceId,
|
|
23
|
+
workspace_name: result.workspaceName,
|
|
24
|
+
email: result.email,
|
|
25
|
+
invited_by_display: result.invitedByDisplay,
|
|
26
|
+
expires_at: result.expiresAt.toISOString()
|
|
27
|
+
};
|
|
28
|
+
case 'expired':
|
|
29
|
+
return {
|
|
30
|
+
status: 'expired',
|
|
31
|
+
workspace_name: result.workspaceName,
|
|
32
|
+
expires_at: result.expiresAt.toISOString()
|
|
33
|
+
};
|
|
34
|
+
case 'already_used':
|
|
35
|
+
return {
|
|
36
|
+
status: 'already_used',
|
|
37
|
+
workspace_name: result.workspaceName
|
|
38
|
+
};
|
|
39
|
+
case 'invalid':
|
|
40
|
+
return {
|
|
41
|
+
status: 'invalid'
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/preview.ts"],"sourcesContent":["import {\n previewInvitationQuerySchema,\n previewInvitationResponseSchema,\n} from '@shipfox/api-workspaces-dto';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {previewInvitation} from '#core/index.js';\n\nexport const previewInvitationRoute = defineRoute({\n method: 'GET',\n path: '/preview',\n description: 'Preview an invitation by opaque token (public, no auth).',\n schema: {\n querystring: previewInvitationQuerySchema,\n response: {\n 200: previewInvitationResponseSchema,\n },\n },\n handler: async (request) => {\n const result = await previewInvitation({token: request.query.token});\n\n switch (result.status) {\n case 'pending':\n return {\n status: 'pending' as const,\n workspace_id: result.workspaceId,\n workspace_name: result.workspaceName,\n email: result.email,\n invited_by_display: result.invitedByDisplay,\n expires_at: result.expiresAt.toISOString(),\n };\n case 'expired':\n return {\n status: 'expired' as const,\n workspace_name: result.workspaceName,\n expires_at: result.expiresAt.toISOString(),\n };\n case 'already_used':\n return {\n status: 'already_used' as const,\n workspace_name: result.workspaceName,\n };\n case 'invalid':\n return {status: 'invalid' as const};\n }\n },\n});\n"],"names":["previewInvitationQuerySchema","previewInvitationResponseSchema","defineRoute","previewInvitation","previewInvitationRoute","method","path","description","schema","querystring","response","handler","request","result","token","query","status","workspace_id","workspaceId","workspace_name","workspaceName","email","invited_by_display","invitedByDisplay","expires_at","expiresAt","toISOString"],"mappings":"AAAA,SACEA,4BAA4B,EAC5BC,+BAA+B,QAC1B,8BAA8B;AACrC,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,iBAAiB,QAAO,iBAAiB;AAEjD,OAAO,MAAMC,yBAAyBF,YAAY;IAChDG,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,aAAaT;QACbU,UAAU;YACR,KAAKT;QACP;IACF;IACAU,SAAS,OAAOC;QACd,MAAMC,SAAS,MAAMV,kBAAkB;YAACW,OAAOF,QAAQG,KAAK,CAACD,KAAK;QAAA;QAElE,OAAQD,OAAOG,MAAM;YACnB,KAAK;gBACH,OAAO;oBACLA,QAAQ;oBACRC,cAAcJ,OAAOK,WAAW;oBAChCC,gBAAgBN,OAAOO,aAAa;oBACpCC,OAAOR,OAAOQ,KAAK;oBACnBC,oBAAoBT,OAAOU,gBAAgB;oBAC3CC,YAAYX,OAAOY,SAAS,CAACC,WAAW;gBAC1C;YACF,KAAK;gBACH,OAAO;oBACLV,QAAQ;oBACRG,gBAAgBN,OAAOO,aAAa;oBACpCI,YAAYX,OAAOY,SAAS,CAACC,WAAW;gBAC1C;YACF,KAAK;gBACH,OAAO;oBACLV,QAAQ;oBACRG,gBAAgBN,OAAOO,aAAa;gBACtC;YACF,KAAK;gBACH,OAAO;oBAACJ,QAAQ;gBAAkB;QACtC;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revoke.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/invitations/revoke.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,qBAAqB,iDAmDhC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { InvitationNotFoundError, InvitationWorkspaceMismatchError, MembershipRequiredError, WorkspaceInactiveError, WorkspaceNotFoundError } from '#core/errors.js';
|
|
5
|
+
import { revokeWorkspaceInvitation } from '#core/index.js';
|
|
6
|
+
export const revokeInvitationRoute = defineRoute({
|
|
7
|
+
method: 'DELETE',
|
|
8
|
+
path: '/:invitationId',
|
|
9
|
+
description: 'Cancel a pending invitation.',
|
|
10
|
+
auth: AUTH_USER,
|
|
11
|
+
schema: {
|
|
12
|
+
params: z.object({
|
|
13
|
+
workspaceId: z.string().uuid(),
|
|
14
|
+
invitationId: z.string().uuid()
|
|
15
|
+
}),
|
|
16
|
+
response: {
|
|
17
|
+
204: z.void()
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
errorHandler: (error)=>{
|
|
21
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
22
|
+
throw new ClientError('Workspace not found', 'not-found', {
|
|
23
|
+
status: 404
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (error instanceof MembershipRequiredError) {
|
|
27
|
+
throw new ClientError('Not a member of this workspace', 'forbidden', {
|
|
28
|
+
status: 403
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof InvitationNotFoundError) {
|
|
32
|
+
throw new ClientError('Invitation not found', 'not-found', {
|
|
33
|
+
status: 404
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (error instanceof InvitationWorkspaceMismatchError) {
|
|
37
|
+
throw new ClientError('Invitation does not belong to this workspace', 'forbidden', {
|
|
38
|
+
status: 403
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof WorkspaceInactiveError) {
|
|
42
|
+
throw new ClientError('Workspace is not active', 'workspace-inactive', {
|
|
43
|
+
status: 403
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
throw error;
|
|
47
|
+
},
|
|
48
|
+
handler: async (request, reply)=>{
|
|
49
|
+
const client = getUserContext(request);
|
|
50
|
+
if (!client) {
|
|
51
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
52
|
+
status: 401
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const { workspaceId, invitationId } = request.params;
|
|
56
|
+
await revokeWorkspaceInvitation({
|
|
57
|
+
workspaceId,
|
|
58
|
+
invitationId,
|
|
59
|
+
requesterUserId: client.userId,
|
|
60
|
+
requesterMemberships: client.memberships
|
|
61
|
+
});
|
|
62
|
+
reply.code(204);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=revoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/invitations/revoke.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {\n InvitationNotFoundError,\n InvitationWorkspaceMismatchError,\n MembershipRequiredError,\n WorkspaceInactiveError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nimport {revokeWorkspaceInvitation} from '#core/index.js';\n\nexport const revokeInvitationRoute = defineRoute({\n method: 'DELETE',\n path: '/:invitationId',\n description: 'Cancel a pending invitation.',\n auth: AUTH_USER,\n schema: {\n params: z.object({\n workspaceId: z.string().uuid(),\n invitationId: z.string().uuid(),\n }),\n response: {\n 204: z.void(),\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof MembershipRequiredError) {\n throw new ClientError('Not a member of this workspace', 'forbidden', {status: 403});\n }\n if (error instanceof InvitationNotFoundError) {\n throw new ClientError('Invitation not found', 'not-found', {status: 404});\n }\n if (error instanceof InvitationWorkspaceMismatchError) {\n throw new ClientError('Invitation does not belong to this workspace', 'forbidden', {\n status: 403,\n });\n }\n if (error instanceof WorkspaceInactiveError) {\n throw new ClientError('Workspace is not active', 'workspace-inactive', {status: 403});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {workspaceId, invitationId} = request.params;\n\n await revokeWorkspaceInvitation({\n workspaceId,\n invitationId,\n requesterUserId: client.userId,\n requesterMemberships: client.memberships,\n });\n\n reply.code(204);\n },\n});\n"],"names":["AUTH_USER","getUserContext","ClientError","defineRoute","z","InvitationNotFoundError","InvitationWorkspaceMismatchError","MembershipRequiredError","WorkspaceInactiveError","WorkspaceNotFoundError","revokeWorkspaceInvitation","revokeInvitationRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","invitationId","response","void","errorHandler","error","status","handler","request","reply","client","requesterUserId","userId","requesterMemberships","memberships","code"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,uBAAuB,EACvBC,gCAAgC,EAChCC,uBAAuB,EACvBC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AACzB,SAAQC,yBAAyB,QAAO,iBAAiB;AAEzD,OAAO,MAAMC,wBAAwBR,YAAY;IAC/CS,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMf;IACNgB,QAAQ;QACNC,QAAQb,EAAEc,MAAM,CAAC;YACfC,aAAaf,EAAEgB,MAAM,GAAGC,IAAI;YAC5BC,cAAclB,EAAEgB,MAAM,GAAGC,IAAI;QAC/B;QACAE,UAAU;YACR,KAAKnB,EAAEoB,IAAI;QACb;IACF;IACAC,cAAc,CAACC;QACb,IAAIA,iBAAiBjB,wBAAwB;YAC3C,MAAM,IAAIP,YAAY,uBAAuB,aAAa;gBAACyB,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBnB,yBAAyB;YAC5C,MAAM,IAAIL,YAAY,kCAAkC,aAAa;gBAACyB,QAAQ;YAAG;QACnF;QACA,IAAID,iBAAiBrB,yBAAyB;YAC5C,MAAM,IAAIH,YAAY,wBAAwB,aAAa;gBAACyB,QAAQ;YAAG;QACzE;QACA,IAAID,iBAAiBpB,kCAAkC;YACrD,MAAM,IAAIJ,YAAY,gDAAgD,aAAa;gBACjFyB,QAAQ;YACV;QACF;QACA,IAAID,iBAAiBlB,wBAAwB;YAC3C,MAAM,IAAIN,YAAY,2BAA2B,sBAAsB;gBAACyB,QAAQ;YAAG;QACrF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAAS9B,eAAe4B;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAI7B,YAAY,2BAA2B,gBAAgB;gBAACyB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACR,WAAW,EAAEG,YAAY,EAAC,GAAGO,QAAQZ,MAAM;QAElD,MAAMP,0BAA0B;YAC9BS;YACAG;YACAU,iBAAiBD,OAAOE,MAAM;YAC9BC,sBAAsBH,OAAOI,WAAW;QAC1C;QAEAL,MAAMM,IAAI,CAAC;IACb;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/members/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAI3D,eAAO,MAAM,YAAY,EAAE,eAAe,EAA0C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/members/index.ts"],"sourcesContent":["import type {RouteDefinition} from '@shipfox/node-fastify';\nimport {listMembersRoute} from './list.js';\nimport {removeMemberRoute} from './remove.js';\n\nexport const memberRoutes: RouteDefinition[] = [listMembersRoute, removeMemberRoute];\n"],"names":["listMembersRoute","removeMemberRoute","memberRoutes"],"mappings":"AACA,SAAQA,gBAAgB,QAAO,YAAY;AAC3C,SAAQC,iBAAiB,QAAO,cAAc;AAE9C,OAAO,MAAMC,eAAkC;IAACF;IAAkBC;CAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/members/list.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,gBAAgB,iDAwC3B,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { listMembersResponseSchema } from '@shipfox/api-workspaces-dto';
|
|
3
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { MembershipRequiredError, WorkspaceInactiveError, WorkspaceNotFoundError } from '#core/errors.js';
|
|
6
|
+
import { listWorkspaceMembers } from '#core/index.js';
|
|
7
|
+
import { toMembershipWithUserDto } from '#presentation/dto/index.js';
|
|
8
|
+
export const listMembersRoute = defineRoute({
|
|
9
|
+
method: 'GET',
|
|
10
|
+
path: '/',
|
|
11
|
+
description: 'List the members of a workspace.',
|
|
12
|
+
auth: AUTH_USER,
|
|
13
|
+
schema: {
|
|
14
|
+
params: z.object({
|
|
15
|
+
workspaceId: z.string().uuid()
|
|
16
|
+
}),
|
|
17
|
+
response: {
|
|
18
|
+
200: listMembersResponseSchema
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
errorHandler: (error)=>{
|
|
22
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
23
|
+
throw new ClientError('Workspace not found', 'not-found', {
|
|
24
|
+
status: 404
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof MembershipRequiredError) {
|
|
28
|
+
throw new ClientError('Not a member of this workspace', 'forbidden', {
|
|
29
|
+
status: 403
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof WorkspaceInactiveError) {
|
|
33
|
+
throw new ClientError('Workspace is not active', 'workspace-inactive', {
|
|
34
|
+
status: 403
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
},
|
|
39
|
+
handler: async (request)=>{
|
|
40
|
+
const client = getUserContext(request);
|
|
41
|
+
if (!client) {
|
|
42
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
43
|
+
status: 401
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const { workspaceId } = request.params;
|
|
47
|
+
const members = await listWorkspaceMembers({
|
|
48
|
+
workspaceId,
|
|
49
|
+
requesterUserId: client.userId,
|
|
50
|
+
requesterMemberships: client.memberships
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
members: members.map(toMembershipWithUserDto)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/members/list.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {listMembersResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {\n MembershipRequiredError,\n WorkspaceInactiveError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nimport {listWorkspaceMembers} from '#core/index.js';\nimport {toMembershipWithUserDto} from '#presentation/dto/index.js';\n\nexport const listMembersRoute = defineRoute({\n method: 'GET',\n path: '/',\n description: 'List the members of a workspace.',\n auth: AUTH_USER,\n schema: {\n params: z.object({workspaceId: z.string().uuid()}),\n response: {\n 200: listMembersResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof MembershipRequiredError) {\n throw new ClientError('Not a member of this workspace', 'forbidden', {status: 403});\n }\n if (error instanceof WorkspaceInactiveError) {\n throw new ClientError('Workspace is not active', 'workspace-inactive', {status: 403});\n }\n throw error;\n },\n handler: async (request) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {workspaceId} = request.params;\n const members = await listWorkspaceMembers({\n workspaceId,\n requesterUserId: client.userId,\n requesterMemberships: client.memberships,\n });\n\n return {\n members: members.map(toMembershipWithUserDto),\n };\n },\n});\n"],"names":["AUTH_USER","getUserContext","listMembersResponseSchema","ClientError","defineRoute","z","MembershipRequiredError","WorkspaceInactiveError","WorkspaceNotFoundError","listWorkspaceMembers","toMembershipWithUserDto","listMembersRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","response","errorHandler","error","status","handler","request","client","members","requesterUserId","userId","requesterMemberships","memberships","map"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,yBAAyB,QAAO,8BAA8B;AACtE,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,uBAAuB,EACvBC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AACzB,SAAQC,oBAAoB,QAAO,iBAAiB;AACpD,SAAQC,uBAAuB,QAAO,6BAA6B;AAEnE,OAAO,MAAMC,mBAAmBP,YAAY;IAC1CQ,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMf;IACNgB,QAAQ;QACNC,QAAQZ,EAAEa,MAAM,CAAC;YAACC,aAAad,EAAEe,MAAM,GAAGC,IAAI;QAAE;QAChDC,UAAU;YACR,KAAKpB;QACP;IACF;IACAqB,cAAc,CAACC;QACb,IAAIA,iBAAiBhB,wBAAwB;YAC3C,MAAM,IAAIL,YAAY,uBAAuB,aAAa;gBAACsB,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBlB,yBAAyB;YAC5C,MAAM,IAAIH,YAAY,kCAAkC,aAAa;gBAACsB,QAAQ;YAAG;QACnF;QACA,IAAID,iBAAiBjB,wBAAwB;YAC3C,MAAM,IAAIJ,YAAY,2BAA2B,sBAAsB;gBAACsB,QAAQ;YAAG;QACrF;QACA,MAAMD;IACR;IACAE,SAAS,OAAOC;QACd,MAAMC,SAAS3B,eAAe0B;QAC9B,IAAI,CAACC,QAAQ;YACX,MAAM,IAAIzB,YAAY,2BAA2B,gBAAgB;gBAACsB,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACN,WAAW,EAAC,GAAGQ,QAAQV,MAAM;QACpC,MAAMY,UAAU,MAAMpB,qBAAqB;YACzCU;YACAW,iBAAiBF,OAAOG,MAAM;YAC9BC,sBAAsBJ,OAAOK,WAAW;QAC1C;QAEA,OAAO;YACLJ,SAASA,QAAQK,GAAG,CAACxB;QACvB;IACF;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/members/remove.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,iBAAiB,iDAoD5B,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { AUTH_USER, getUserContext } from '@shipfox/api-auth-context';
|
|
2
|
+
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { LastMemberError, MembershipNotFoundError, MembershipRequiredError, SelfRemovalNotAllowedError, WorkspaceInactiveError, WorkspaceNotFoundError } from '#core/errors.js';
|
|
5
|
+
import { removeWorkspaceMember } from '#core/index.js';
|
|
6
|
+
export const removeMemberRoute = defineRoute({
|
|
7
|
+
method: 'DELETE',
|
|
8
|
+
path: '/:userId',
|
|
9
|
+
description: 'Remove a member from a workspace.',
|
|
10
|
+
auth: AUTH_USER,
|
|
11
|
+
schema: {
|
|
12
|
+
params: z.object({
|
|
13
|
+
workspaceId: z.string().uuid(),
|
|
14
|
+
userId: z.string().uuid()
|
|
15
|
+
}),
|
|
16
|
+
response: {
|
|
17
|
+
204: z.void()
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
errorHandler: (error)=>{
|
|
21
|
+
if (error instanceof WorkspaceNotFoundError) {
|
|
22
|
+
throw new ClientError('Workspace not found', 'not-found', {
|
|
23
|
+
status: 404
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (error instanceof MembershipRequiredError) {
|
|
27
|
+
throw new ClientError('Not a member of this workspace', 'forbidden', {
|
|
28
|
+
status: 403
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof MembershipNotFoundError) {
|
|
32
|
+
throw new ClientError('Member not found', 'not-found', {
|
|
33
|
+
status: 404
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (error instanceof LastMemberError) {
|
|
37
|
+
throw new ClientError(error.message, 'last-member', {
|
|
38
|
+
status: 409
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof SelfRemovalNotAllowedError) {
|
|
42
|
+
throw new ClientError(error.message, 'self-removal-not-allowed', {
|
|
43
|
+
status: 409
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (error instanceof WorkspaceInactiveError) {
|
|
47
|
+
throw new ClientError('Workspace is not active', 'workspace-inactive', {
|
|
48
|
+
status: 403
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
throw error;
|
|
52
|
+
},
|
|
53
|
+
handler: async (request, reply)=>{
|
|
54
|
+
const client = getUserContext(request);
|
|
55
|
+
if (!client) {
|
|
56
|
+
throw new ClientError('Authentication required', 'unauthorized', {
|
|
57
|
+
status: 401
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const { workspaceId, userId } = request.params;
|
|
61
|
+
await removeWorkspaceMember({
|
|
62
|
+
workspaceId,
|
|
63
|
+
userId,
|
|
64
|
+
requesterUserId: client.userId,
|
|
65
|
+
requesterMemberships: client.memberships
|
|
66
|
+
});
|
|
67
|
+
reply.code(204);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=remove.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/presentation/routes/members/remove.ts"],"sourcesContent":["import {AUTH_USER, getUserContext} from '@shipfox/api-auth-context';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {\n LastMemberError,\n MembershipNotFoundError,\n MembershipRequiredError,\n SelfRemovalNotAllowedError,\n WorkspaceInactiveError,\n WorkspaceNotFoundError,\n} from '#core/errors.js';\nimport {removeWorkspaceMember} from '#core/index.js';\n\nexport const removeMemberRoute = defineRoute({\n method: 'DELETE',\n path: '/:userId',\n description: 'Remove a member from a workspace.',\n auth: AUTH_USER,\n schema: {\n params: z.object({\n workspaceId: z.string().uuid(),\n userId: z.string().uuid(),\n }),\n response: {\n 204: z.void(),\n },\n },\n errorHandler: (error) => {\n if (error instanceof WorkspaceNotFoundError) {\n throw new ClientError('Workspace not found', 'not-found', {status: 404});\n }\n if (error instanceof MembershipRequiredError) {\n throw new ClientError('Not a member of this workspace', 'forbidden', {status: 403});\n }\n if (error instanceof MembershipNotFoundError) {\n throw new ClientError('Member not found', 'not-found', {status: 404});\n }\n if (error instanceof LastMemberError) {\n throw new ClientError(error.message, 'last-member', {status: 409});\n }\n if (error instanceof SelfRemovalNotAllowedError) {\n throw new ClientError(error.message, 'self-removal-not-allowed', {status: 409});\n }\n if (error instanceof WorkspaceInactiveError) {\n throw new ClientError('Workspace is not active', 'workspace-inactive', {status: 403});\n }\n throw error;\n },\n handler: async (request, reply) => {\n const client = getUserContext(request);\n if (!client) {\n throw new ClientError('Authentication required', 'unauthorized', {status: 401});\n }\n\n const {workspaceId, userId} = request.params;\n\n await removeWorkspaceMember({\n workspaceId,\n userId,\n requesterUserId: client.userId,\n requesterMemberships: client.memberships,\n });\n\n reply.code(204);\n },\n});\n"],"names":["AUTH_USER","getUserContext","ClientError","defineRoute","z","LastMemberError","MembershipNotFoundError","MembershipRequiredError","SelfRemovalNotAllowedError","WorkspaceInactiveError","WorkspaceNotFoundError","removeWorkspaceMember","removeMemberRoute","method","path","description","auth","schema","params","object","workspaceId","string","uuid","userId","response","void","errorHandler","error","status","message","handler","request","reply","client","requesterUserId","requesterMemberships","memberships","code"],"mappings":"AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,4BAA4B;AACpE,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SACEC,eAAe,EACfC,uBAAuB,EACvBC,uBAAuB,EACvBC,0BAA0B,EAC1BC,sBAAsB,EACtBC,sBAAsB,QACjB,kBAAkB;AACzB,SAAQC,qBAAqB,QAAO,iBAAiB;AAErD,OAAO,MAAMC,oBAAoBT,YAAY;IAC3CU,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,MAAMhB;IACNiB,QAAQ;QACNC,QAAQd,EAAEe,MAAM,CAAC;YACfC,aAAahB,EAAEiB,MAAM,GAAGC,IAAI;YAC5BC,QAAQnB,EAAEiB,MAAM,GAAGC,IAAI;QACzB;QACAE,UAAU;YACR,KAAKpB,EAAEqB,IAAI;QACb;IACF;IACAC,cAAc,CAACC;QACb,IAAIA,iBAAiBjB,wBAAwB;YAC3C,MAAM,IAAIR,YAAY,uBAAuB,aAAa;gBAAC0B,QAAQ;YAAG;QACxE;QACA,IAAID,iBAAiBpB,yBAAyB;YAC5C,MAAM,IAAIL,YAAY,kCAAkC,aAAa;gBAAC0B,QAAQ;YAAG;QACnF;QACA,IAAID,iBAAiBrB,yBAAyB;YAC5C,MAAM,IAAIJ,YAAY,oBAAoB,aAAa;gBAAC0B,QAAQ;YAAG;QACrE;QACA,IAAID,iBAAiBtB,iBAAiB;YACpC,MAAM,IAAIH,YAAYyB,MAAME,OAAO,EAAE,eAAe;gBAACD,QAAQ;YAAG;QAClE;QACA,IAAID,iBAAiBnB,4BAA4B;YAC/C,MAAM,IAAIN,YAAYyB,MAAME,OAAO,EAAE,4BAA4B;gBAACD,QAAQ;YAAG;QAC/E;QACA,IAAID,iBAAiBlB,wBAAwB;YAC3C,MAAM,IAAIP,YAAY,2BAA2B,sBAAsB;gBAAC0B,QAAQ;YAAG;QACrF;QACA,MAAMD;IACR;IACAG,SAAS,OAAOC,SAASC;QACvB,MAAMC,SAAShC,eAAe8B;QAC9B,IAAI,CAACE,QAAQ;YACX,MAAM,IAAI/B,YAAY,2BAA2B,gBAAgB;gBAAC0B,QAAQ;YAAG;QAC/E;QAEA,MAAM,EAACR,WAAW,EAAEG,MAAM,EAAC,GAAGQ,QAAQb,MAAM;QAE5C,MAAMP,sBAAsB;YAC1BS;YACAG;YACAW,iBAAiBD,OAAOV,MAAM;YAC9BY,sBAAsBF,OAAOG,WAAW;QAC1C;QAEAJ,MAAMK,IAAI,CAAC;IACb;AACF,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/presentation/routes/workspaces/create.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,iDA6B/B,CAAC"}
|