File size: 2,256 Bytes
95b671e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { db, schema } from "@superlog/db";
import { and, eq, inArray } from "drizzle-orm";
import { HTTPException } from "hono/http-exception";

export type AccessibleProject = {
  id: string;
  name: string;
  slug: string;
  orgId: string;
  orgName: string;
  orgSlug: string;
};

export async function listAccessibleProjects(userId: string): Promise<AccessibleProject[]> {
  const memberships = await db.query.orgMembers.findMany({
    where: eq(schema.orgMembers.userId, userId),
  });
  if (memberships.length === 0) return [];
  const orgIds = memberships.map((m) => m.orgId);

  const orgs = await db.query.orgs.findMany({
    where: inArray(schema.orgs.id, orgIds),
  });
  const orgById = new Map(orgs.map((o) => [o.id, o]));

  const projects = await db.query.projects.findMany({
    where: inArray(schema.projects.orgId, orgIds),
  });

  return projects
    .map((p) => {
      const o = orgById.get(p.orgId);
      if (!o) return null;
      return {
        id: p.id,
        name: p.name,
        slug: p.slug,
        orgId: o.id,
        orgName: o.name,
        orgSlug: o.slug,
      };
    })
    .filter((x): x is AccessibleProject => x !== null);
}

export async function assertProjectAccess(userId: string, projectId: string): Promise<void> {
  const project = await db.query.projects.findFirst({
    where: eq(schema.projects.id, projectId),
  });
  if (!project) throw new HTTPException(404, { message: "project not found" });

  const membership = await db.query.orgMembers.findFirst({
    where: and(
      eq(schema.orgMembers.userId, userId),
      eq(schema.orgMembers.orgId, project.orgId),
    ),
  });
  if (!membership) throw new HTTPException(403, { message: "no access to project" });
}

export async function setActiveProjectForToken(
  tokenId: string,
  userId: string,
  projectId: string,
): Promise<AccessibleProject> {
  await assertProjectAccess(userId, projectId);
  await db
    .update(schema.mcpOauthTokens)
    .set({ projectId })
    .where(eq(schema.mcpOauthTokens.id, tokenId));

  const projects = await listAccessibleProjects(userId);
  const found = projects.find((p) => p.id === projectId);
  if (!found) throw new HTTPException(500, { message: "project vanished after switch" });
  return found;
}