Add org-scoped data isolation across all server actions to prevent cross-org data leakage. Add read-only demo mode with mutation guards on all write endpoints. Multi-tenancy: - org filter on executeDashboardQueries (all query types) - org boundary checks on getChannel, joinChannel - searchMentionableUsers derives org from session - getConversationUsage scoped to user, not org-wide for admins - organizations table, members, org switcher component Demo mode: - /demo route sets strict sameSite cookie - isDemoUser guards on all mutation server actions - demo banner, CTA dialog, and gate components - seed script for demo org data Also: exclude scripts/ from tsconfig (fixes build), add multi-tenancy architecture documentation. Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
30 lines
787 B
TypeScript
Executable File
30 lines
787 B
TypeScript
Executable File
"use server"
|
|
|
|
import { getCloudflareContext } from "@opennextjs/cloudflare"
|
|
import { getDb } from "@/db"
|
|
import { projects } from "@/db/schema"
|
|
import { asc, eq } from "drizzle-orm"
|
|
import { requireAuth } from "@/lib/auth"
|
|
import { requireOrg } from "@/lib/org-scope"
|
|
|
|
export async function getProjects(): Promise<{ id: string; name: string }[]> {
|
|
try {
|
|
const user = await requireAuth()
|
|
const orgId = requireOrg(user)
|
|
|
|
const { env } = await getCloudflareContext()
|
|
if (!env?.DB) return []
|
|
|
|
const db = getDb(env.DB)
|
|
const allProjects = await db
|
|
.select({ id: projects.id, name: projects.name })
|
|
.from(projects)
|
|
.where(eq(projects.organizationId, orgId))
|
|
.orderBy(asc(projects.name))
|
|
|
|
return allProjects
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|