Skip to content

Commit 4da8d4b

Browse files
committed
feat: refactor organization handling by introducing organization context, updating project routes, and removing deprecated container routes
1 parent c7942e0 commit 4da8d4b

14 files changed

Lines changed: 322 additions & 248 deletions

File tree

apps/api/app/v1/[[...route]]/route.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import notifications from "../routes/notifications";
1717
import domain from "../routes/domain";
1818
import envs from "../routes/environments";
1919
import file from "../routes/file";
20-
import containers from "../routes/containers";
20+
// import containers from "../routes/containers";
2121
import telementryEvents from "../routes/telemetry";
2222
import auditLogs from "../routes/logger";
2323
import { rateLimitHandler } from "@/middleware/ratelimit";
@@ -27,6 +27,7 @@ import vectorizer from "../routes/webhooks/vectorizer";
2727
import clerk from "../routes/webhooks/clerk";
2828
import { clerkMiddleware } from "@hono/clerk-auth";
2929
import inbox from "../routes/inbox";
30+
import store from "../routes/store";
3031

3132
export const runtime = "edge";
3233
const app = new Hono().basePath("/v1");
@@ -66,6 +67,7 @@ app.use(rateLimitHandler);
6667
// Import routes
6768
app.route("/health", health);
6869
app.route("/auth", auth);
70+
app.route("/store", store);
6971
app.route("/ai", ai);
7072
app.route("/user", user);
7173
app.route("/project", project);
@@ -75,7 +77,7 @@ app.route("/notification", notifications);
7577
app.route("/api", api);
7678
app.route("/context", cntxt);
7779
app.route("/link", link);
78-
app.route("/container", containers);
80+
// app.route("/container", containers);
7981
app.route("/file", file);
8082
app.route("/telementry", telementryEvents);
8183
app.route("/logger", auditLogs);

apps/api/app/v1/routes/containers.ts

Lines changed: 0 additions & 88 deletions
This file was deleted.

apps/api/app/v1/routes/project.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ project.use(checkUser);
1212
// project.use(auditLogs);
1313

1414
project.post("/create", async (c) => {
15-
const { name, userId, description, slug } = await c.req.json();
16-
if (!name || !userId) {
17-
return c.json({ message: "Name and UserId are required" }, 400);
15+
const { name, orgId, description, slug } = await c.req.json();
16+
if (!name || !orgId) {
17+
return c.json({ message: "Name and OrgId are required" }, 400);
1818
}
1919

2020
const pod = await MailClient.pods.create({
@@ -24,7 +24,7 @@ project.post("/create", async (c) => {
2424
const newProject = await prisma.project.create({
2525
data: {
2626
name: name,
27-
userId: userId,
27+
orgId: orgId,
2828
slug: slug,
2929
description: description || null,
3030
podId: pod.podId,
@@ -54,12 +54,20 @@ project.delete("/delete", async (c) => {
5454

5555
const proj = await prisma.project.findUnique({
5656
where: { id },
57-
select: { id: true, userId: true },
57+
select: { id: true, orgId: true },
5858
});
5959
if (!proj) {
6060
return c.json({ message: "Not found" }, 404);
6161
}
62-
if (proj.userId !== user.id) {
62+
// Authorize by checking org's creator
63+
const org = await prisma.organization.findUnique({
64+
where: { id: proj.orgId },
65+
select: { createdBy: true },
66+
});
67+
if (!org) {
68+
return c.json({ message: "Organization not found" }, 404);
69+
}
70+
if (org.createdBy !== user.id) {
6371
return c.json({ message: "Forbidden" }, 403);
6472
}
6573

@@ -106,19 +114,9 @@ project.post("/update", async (c) => {
106114
project.get("/all", async (c) => {
107115
const userId = c.get("userId");
108116

109-
if (!userId) {
110-
return c.json(
111-
{
112-
message: "Oops! seems like your session is expired",
113-
status: 400,
114-
},
115-
400,
116-
);
117-
}
118-
119117
try {
120118
const projects = await prisma.project.findMany({
121-
where: { userId },
119+
where: { org: { createdBy: userId } },
122120
orderBy: { createdAt: "desc" },
123121
cacheStrategy: {
124122
ttl: 60,

apps/api/app/v1/routes/store.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { checkUser } from "@/middleware/check.user";
2+
import { Variables } from "@/types";
3+
import { prisma } from "@prexo/db";
4+
import { Hono } from "hono";
5+
import { cache } from "@prexo/cache";
6+
7+
const store = new Hono<{ Variables: Variables }>();
8+
store.use(checkUser);
9+
10+
store.get("/", async (c) => {
11+
const userId = c.get("userId");
12+
const cacheKey = `@prexo/store_${userId}`;
13+
14+
// Return cached response if available
15+
const cached = await cache.get(cacheKey);
16+
if (cached) {
17+
console.log("Store cache hit for user:", userId);
18+
return c.json(cached, 200);
19+
}
20+
21+
const orgs = await prisma.organization.findMany({
22+
where: { createdBy: userId },
23+
orderBy: { createdAt: "desc" },
24+
cacheStrategy: {
25+
ttl: 29,
26+
swr: 29,
27+
tags: ["store_orgs"],
28+
},
29+
});
30+
31+
const projects = await prisma.project.findMany({
32+
where: { org: { createdBy: userId } },
33+
orderBy: { createdAt: "desc" },
34+
cacheStrategy: {
35+
ttl: 29,
36+
swr: 29,
37+
tags: ["store_projects"],
38+
},
39+
});
40+
41+
const payload = { userId, orgs, projects };
42+
// Store in cache for a short TTL
43+
await cache.set(cacheKey, payload, { ex: 30 });
44+
console.log("Store cache set for user:", userId);
45+
return c.json(payload, 200);
46+
});
47+
48+
export default store;

apps/api/app/v1/routes/webhooks/clerk.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,6 @@ clerk.post("/", async (c) => {
5050
});
5151
if (existingByEmail && existingByEmail.id !== id) {
5252
// Migrate related records to new Clerk id, then update the user id
53-
await prisma.$transaction([
54-
prisma.session.updateMany({
55-
where: { userId: existingByEmail.id },
56-
data: { userId: id },
57-
}),
58-
prisma.account.updateMany({
59-
where: { userId: existingByEmail.id },
60-
data: { userId: id },
61-
}),
62-
prisma.passkey.updateMany({
63-
where: { userId: existingByEmail.id },
64-
data: { userId: id },
65-
}),
66-
prisma.project.updateMany({
67-
where: { userId: existingByEmail.id },
68-
data: { userId: id },
69-
}),
70-
]);
7153

7254
user = await prisma.user.update({
7355
where: { id: existingByEmail.id },
@@ -147,6 +129,26 @@ clerk.post("/", async (c) => {
147129
if (eventType === "organization.created") {
148130
console.log("New org created:", id);
149131
// Handle user creation logic here
132+
if (!evt.data.created_by) {
133+
throw new Error(
134+
"Organization created event missing created_by user id",
135+
);
136+
}
137+
const org = await prisma.organization.create({
138+
data: {
139+
id: evt.data.id,
140+
name: evt.data.name,
141+
slug: evt.data.slug,
142+
imgUrl: evt.data.image_url,
143+
status: "ACTIVE",
144+
membersCount: evt.data.members_count,
145+
maxAllowedMembers: evt.data.max_allowed_memberships,
146+
user: {
147+
connect: { id: evt.data.created_by },
148+
},
149+
},
150+
});
151+
console.log("Organization created in DB:", org);
150152
await logTelegram({
151153
logTitle: "Clerk Webhook - Organization Created",
152154
logSummary: `A new organization has been created with ID: ${id}`,
@@ -160,11 +162,27 @@ clerk.post("/", async (c) => {
160162
if (eventType === "organization.updated") {
161163
console.log("New org updated:", id);
162164
// Handle user creation logic here
165+
const org = await prisma.organization.updateMany({
166+
where: { id: evt.data.id },
167+
data: {
168+
name: evt.data.name,
169+
slug: evt.data.slug,
170+
imgUrl: evt.data.image_url,
171+
membersCount: evt.data.members_count,
172+
maxAllowedMembers: evt.data.max_allowed_memberships,
173+
updatedAt: new Date(),
174+
},
175+
});
163176
}
164177

165178
if (eventType === "organization.deleted") {
166179
console.log("New org deleted:", id);
167180
// Handle user creation logic here
181+
const org = await prisma.organization.updateMany({
182+
where: { id: evt.data.id },
183+
data: { status: "DELETED", deletedAt: new Date() },
184+
});
185+
console.log("Organization marked as deleted in DB:", org);
168186
await logTelegram({
169187
logTitle: "Clerk Webhook - Organization Deleted",
170188
logSummary: `An organization has been deleted with ID: ${evt.data.id}`,

apps/app/app/(routes)/orgs/[slug]/apps/[id]/page.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"use client";
22

3-
import { useProjectsStore } from "@prexo/store";
3+
import { useOrganizationStore, useProjectsStore } from "@prexo/store";
44
import { useRouter } from "next/navigation";
5-
import { use, useEffect } from "react";
5+
import { use, useEffect, useMemo } from "react";
66
import { Button } from "@/components/ui/button";
77
import {
88
Dialog,
@@ -19,12 +19,24 @@ export default function page({ params }: { params: Promise<{ id: string }> }) {
1919
const { id } = use(params);
2020
const router = useRouter();
2121
const { projects } = useProjectsStore();
22+
const { orgs } = useOrganizationStore();
2223
const [_, setSelectedApp, removeSelectedApp] = useLocalStorage(
2324
"@prexo-#selectedApp",
2425
"",
2526
);
27+
const [ selectedOrgSlug ] =
28+
useLocalStorage("@prexo-#selectedOrgSlug", "");
2629

27-
if (!projects.find((proj) => proj.slug === id)) {
30+
const selectedOrg = useMemo(() => { {
31+
return orgs.find((o) => o.slug === selectedOrgSlug);
32+
}}, [orgs, selectedOrgSlug]);
33+
34+
const appsOfSelectedOrg = useMemo(() => {
35+
if (!selectedOrg) return [];
36+
return projects.filter((p) => p.orgId === selectedOrg.id);
37+
}, [projects, selectedOrg]);
38+
39+
if (!appsOfSelectedOrg.find((proj) => proj.slug === id)) {
2840
return (
2941
<Dialog open={true}>
3042
<DialogPopup>
@@ -56,7 +68,7 @@ export default function page({ params }: { params: Promise<{ id: string }> }) {
5668
}
5769

5870
useEffect(() => {
59-
const matchedProj = projects.find((proj) => proj.slug === id);
71+
const matchedProj = appsOfSelectedOrg.find((proj) => proj.slug === id);
6072
if (matchedProj) {
6173
setSelectedApp(matchedProj.id);
6274
} else {

0 commit comments

Comments
 (0)