@brightweblabs/module-orgs 0.3.20 → 0.4.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/package.json +3 -3
- package/src/data.ts +29 -0
- package/src/handlers.ts +4 -1
- package/src/http.ts +33 -1
- package/src/index.ts +2 -0
- package/src/invitations.ts +4 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightweblabs/module-orgs",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@supabase/supabase-js": "^2.110.8",
|
|
25
25
|
"server-only": "^0.0.1",
|
|
26
|
-
"@brightweblabs/app-shell": "0.
|
|
27
|
-
"@brightweblabs/core-auth": "0.
|
|
26
|
+
"@brightweblabs/app-shell": "0.11.1",
|
|
27
|
+
"@brightweblabs/core-auth": "0.10.1",
|
|
28
28
|
"@brightweblabs/infra": "0.7.0"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/data.ts
CHANGED
|
@@ -220,6 +220,35 @@ export async function updateOrganization(
|
|
|
220
220
|
return normalizeOrganization(data as RawOrganization);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
export async function deleteOrganization(
|
|
224
|
+
supabase: SupabaseClient,
|
|
225
|
+
organizationId: string,
|
|
226
|
+
): Promise<{ id: string; name: string }> {
|
|
227
|
+
const { count: projectCount, error: projectError } = await supabase
|
|
228
|
+
.from("projects")
|
|
229
|
+
.select("id", { count: "exact", head: true })
|
|
230
|
+
.eq("organization_id", organizationId);
|
|
231
|
+
if (projectError && projectError.code !== "42P01" && projectError.code !== "PGRST205") {
|
|
232
|
+
throw new Error(projectError.message);
|
|
233
|
+
}
|
|
234
|
+
if ((projectCount ?? 0) > 0) {
|
|
235
|
+
throw new Error("A organização tem projetos associados. Elimine ou mova esses projetos primeiro.");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const { data, error } = await supabase
|
|
239
|
+
.from("organizations")
|
|
240
|
+
.delete()
|
|
241
|
+
.eq("id", organizationId)
|
|
242
|
+
.select("id, name")
|
|
243
|
+
.maybeSingle<{ id: string; name: string }>();
|
|
244
|
+
if (error?.code === "23503") {
|
|
245
|
+
throw new Error("A organização tem projetos associados. Elimine ou mova esses projetos primeiro.");
|
|
246
|
+
}
|
|
247
|
+
if (error) throw new Error(error.message);
|
|
248
|
+
if (!data) throw new Error("Organização não encontrada.");
|
|
249
|
+
return data;
|
|
250
|
+
}
|
|
251
|
+
|
|
223
252
|
export async function listOrganizationMembers(
|
|
224
253
|
supabase: SupabaseClient,
|
|
225
254
|
organizationId: string,
|
package/src/handlers.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { requireOrganizationManageAccess, requireOrganizationsStaffAccess } from "./access";
|
|
2
|
-
import { createOrganization, updateOrganization } from "./data";
|
|
2
|
+
import { createOrganization, deleteOrganization, updateOrganization } from "./data";
|
|
3
3
|
import {
|
|
4
4
|
inviteOrganizationMembers,
|
|
5
5
|
logOrganizationActivity,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "./invitations";
|
|
10
10
|
import {
|
|
11
11
|
createOrganizationInvitationDeleteHandler,
|
|
12
|
+
createOrganizationDeleteHandler,
|
|
12
13
|
createOrganizationInvitationsHandler,
|
|
13
14
|
createOrganizationPatchHandler,
|
|
14
15
|
createOrganizationsPostHandler,
|
|
@@ -19,6 +20,7 @@ const writeDependencies = {
|
|
|
19
20
|
getManageAccess: requireOrganizationManageAccess,
|
|
20
21
|
createOrganization,
|
|
21
22
|
updateOrganization,
|
|
23
|
+
deleteOrganization,
|
|
22
24
|
inviteMembers: inviteOrganizationMembers,
|
|
23
25
|
logActivity: logOrganizationActivity,
|
|
24
26
|
};
|
|
@@ -34,6 +36,7 @@ const invitationDependencies = {
|
|
|
34
36
|
|
|
35
37
|
export const handleOrganizationsPostRequest = createOrganizationsPostHandler(writeDependencies);
|
|
36
38
|
export const handleOrganizationPatchRequest = createOrganizationPatchHandler(writeDependencies);
|
|
39
|
+
export const handleOrganizationDeleteRequest = createOrganizationDeleteHandler(writeDependencies);
|
|
37
40
|
|
|
38
41
|
const invitationHandlers = createOrganizationInvitationsHandler(invitationDependencies);
|
|
39
42
|
export const handleOrganizationInvitationsGetRequest = invitationHandlers.GET;
|
package/src/http.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function json(body: unknown, init?: ResponseInit) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
type OrganizationAccess =
|
|
20
|
-
| { ok: true; serviceSupabase: SupabaseClient; profileId: string }
|
|
20
|
+
| { ok: true; serviceSupabase: SupabaseClient; profileId: string; role?: string | null }
|
|
21
21
|
| { ok: false; status: number; error: string };
|
|
22
22
|
|
|
23
23
|
type OrganizationWriteDependencies = {
|
|
@@ -25,6 +25,7 @@ type OrganizationWriteDependencies = {
|
|
|
25
25
|
getManageAccess(organizationId: string): Promise<OrganizationAccess>;
|
|
26
26
|
createOrganization: (supabase: SupabaseClient, input: CreateOrganizationInput) => Promise<unknown>;
|
|
27
27
|
updateOrganization: (supabase: SupabaseClient, id: string, input: CreateOrganizationInput) => Promise<unknown>;
|
|
28
|
+
deleteOrganization: (supabase: SupabaseClient, id: string) => Promise<{ id: string; name: string }>;
|
|
28
29
|
inviteMembers: typeof inviteOrganizationMembers;
|
|
29
30
|
logActivity: typeof logOrganizationActivity;
|
|
30
31
|
};
|
|
@@ -86,6 +87,9 @@ function parseOrganizationInput(body: Record<string, unknown>): CreateOrganizati
|
|
|
86
87
|
|
|
87
88
|
function organizationError(error: unknown): Response {
|
|
88
89
|
const message = error instanceof Error ? error.message : "";
|
|
90
|
+
if (message === "Convite pendente não encontrado.") {
|
|
91
|
+
return json({ error: message }, { status: 404 });
|
|
92
|
+
}
|
|
89
93
|
if (message.startsWith("Não foi possível enviar o email de convite.")) {
|
|
90
94
|
return json({ error: message }, { status: 502 });
|
|
91
95
|
}
|
|
@@ -166,6 +170,34 @@ export function createOrganizationPatchHandler(dependencies: OrganizationWriteDe
|
|
|
166
170
|
};
|
|
167
171
|
}
|
|
168
172
|
|
|
173
|
+
export function createOrganizationDeleteHandler(dependencies: OrganizationWriteDependencies) {
|
|
174
|
+
return async function handleOrganizationDeleteRequest(
|
|
175
|
+
_request: Request,
|
|
176
|
+
context: { params: Promise<{ id: string }> },
|
|
177
|
+
): Promise<Response> {
|
|
178
|
+
const { id } = await context.params;
|
|
179
|
+
if (!id) return json({ error: "id é obrigatório." }, { status: 400 });
|
|
180
|
+
const access = await dependencies.getManageAccess(id);
|
|
181
|
+
if (!access.ok) return json({ error: access.error }, { status: access.status });
|
|
182
|
+
try {
|
|
183
|
+
const organization = await dependencies.deleteOrganization(access.serviceSupabase, id);
|
|
184
|
+
await dependencies.logActivity(access.serviceSupabase, {
|
|
185
|
+
actorProfileId: access.profileId,
|
|
186
|
+
organizationId: id,
|
|
187
|
+
eventType: "crm_organization_deleted",
|
|
188
|
+
summary: `Organização CRM eliminada: ${organization.name}`,
|
|
189
|
+
payload: { organization_id: id, organization_name: organization.name },
|
|
190
|
+
});
|
|
191
|
+
return json({ data: { deletedId: id } });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
const message = error instanceof Error ? error.message : "";
|
|
194
|
+
if (message.includes("projetos associados")) return json({ error: message }, { status: 409 });
|
|
195
|
+
if (message === "Organização não encontrada.") return json({ error: message }, { status: 404 });
|
|
196
|
+
return organizationError(error);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
169
201
|
export function createOrganizationInvitationsHandler(dependencies: OrganizationInvitationDependencies) {
|
|
170
202
|
return {
|
|
171
203
|
GET: async (_request: Request, context: { params: Promise<{ id: string }> }): Promise<Response> => {
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export {
|
|
|
2
2
|
ORGANIZATIONS_DEFAULT_PAGE_SIZE,
|
|
3
3
|
ORGANIZATIONS_MAX_PAGE_SIZE,
|
|
4
4
|
createOrganization,
|
|
5
|
+
deleteOrganization,
|
|
5
6
|
listOrganizationMembers,
|
|
6
7
|
listOrganizations,
|
|
7
8
|
setOrganizationMemberRole,
|
|
@@ -17,6 +18,7 @@ export {
|
|
|
17
18
|
} from "./data";
|
|
18
19
|
export {
|
|
19
20
|
handleOrganizationInvitationDeleteRequest,
|
|
21
|
+
handleOrganizationDeleteRequest,
|
|
20
22
|
handleOrganizationInvitationsGetRequest,
|
|
21
23
|
handleOrganizationInvitationsPostRequest,
|
|
22
24
|
handleOrganizationPatchRequest,
|
package/src/invitations.ts
CHANGED
|
@@ -390,10 +390,12 @@ export async function revokeOrganizationInvitation(
|
|
|
390
390
|
organizationId: string,
|
|
391
391
|
invitationId: string,
|
|
392
392
|
): Promise<void> {
|
|
393
|
-
const { error } = await supabase.from("organization_invitations")
|
|
393
|
+
const { data, error } = await supabase.from("organization_invitations")
|
|
394
394
|
.update({ status: "revoked", revoked_at: new Date().toISOString() })
|
|
395
|
-
.eq("id", invitationId).eq("organization_id", organizationId).eq("status", "pending")
|
|
395
|
+
.eq("id", invitationId).eq("organization_id", organizationId).eq("status", "pending")
|
|
396
|
+
.select("id");
|
|
396
397
|
if (error) throw new Error(error.message);
|
|
398
|
+
if (!(data ?? []).some((row) => row.id === invitationId)) throw new Error("Convite pendente não encontrado.");
|
|
397
399
|
}
|
|
398
400
|
|
|
399
401
|
export async function acceptOrganizationInvitation(
|