diff --git a/.gitignore b/.gitignore index 7383c977d07..06038334232 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ mise.toml # Next.js .next out +next-env.d.ts # Superset # Ignore .superset directory except for config and scripts diff --git a/apps/admin/next-env.d.ts b/apps/admin/next-env.d.ts deleted file mode 100644 index 830fb594ca2..00000000000 --- a/apps/admin/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/admin/next.config.ts b/apps/admin/next.config.ts index 737f1468308..d741b07ad86 100644 --- a/apps/admin/next.config.ts +++ b/apps/admin/next.config.ts @@ -1,9 +1,7 @@ import type { NextConfig } from "next"; const config: NextConfig = { - experimental: { - reactCompiler: true, - }, + reactCompiler: true, typescript: { ignoreBuildErrors: true }, }; diff --git a/apps/admin/package.json b/apps/admin/package.json index 36b92d1ffc9..850a5e4c1d7 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -24,10 +24,10 @@ "@trpc/tanstack-react-query": "^11.7.1", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "server-only": "^0.0.1", "superjson": "^2.2.5", "zod": "^4.1.13" @@ -36,8 +36,8 @@ "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3" diff --git a/apps/admin/public/icon.png b/apps/admin/public/icon.png new file mode 100644 index 00000000000..3b6373301d0 Binary files /dev/null and b/apps/admin/public/icon.png differ diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx b/apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx new file mode 100644 index 00000000000..4c25a046f22 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx @@ -0,0 +1,177 @@ +"use client"; + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@superset/ui/collapsible"; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarRail, +} from "@superset/ui/sidebar"; +import { + BarChart3, + Bot, + ChevronRight, + Database, + Home, + Settings, + Shield, + Users, + Webhook, +} from "lucide-react"; + +import type { User } from "@/lib/auth/types"; +import { AppSidebarHeader } from "./components/AppSidebarHeader"; +import { NavUser } from "./components/NavUser"; +import { SearchForm } from "./components/SearchForm"; + +const navigation = [ + { + title: "Overview", + items: [ + { + title: "Dashboard", + url: "/", + icon: Home, + }, + ], + }, + { + title: "User Management", + items: [ + { + title: "All Users", + url: "/users", + icon: Users, + }, + { + title: "Deleted Users", + url: "/users/deleted", + }, + { + title: "Permissions", + url: "/users/permissions", + icon: Shield, + }, + ], + }, + { + title: "Analytics", + items: [ + { + title: "Overview", + url: "/analytics", + icon: BarChart3, + }, + { + title: "User Activity", + url: "/analytics/activity", + }, + { + title: "Performance", + url: "/analytics/performance", + }, + ], + }, + { + title: "AI Lab", + items: [ + { + title: "Plan Testing", + url: "/ai-lab", + icon: Bot, + }, + { + title: "Model Config", + url: "/ai-lab/models", + }, + ], + }, + { + title: "System", + items: [ + { + title: "Database", + url: "/system/database", + icon: Database, + }, + { + title: "Webhooks", + url: "/system/webhooks", + icon: Webhook, + }, + { + title: "Settings", + url: "/settings", + icon: Settings, + }, + ], + }, +]; + +export interface AppSidebarProps extends React.ComponentProps { + user: User; +} + +export function AppSidebar({ user, ...props }: AppSidebarProps) { + return ( + + + + + + + {navigation.map((section) => ( + + + + + {section.title} + + + + + + + {section.items.map((item) => ( + + + + {item.icon && } + {item.title} + + + + ))} + + + + + + ))} + + + + + + + ); +} diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/AppSidebarHeader.tsx b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/AppSidebarHeader.tsx new file mode 100644 index 00000000000..6220ee10fae --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/AppSidebarHeader.tsx @@ -0,0 +1,30 @@ +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@superset/ui/sidebar"; +import Image from "next/image"; + +export function AppSidebarHeader() { + return ( + + + + + Superset +
+ Superset + Admin Panel +
+
+
+
+
+ ); +} diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/index.ts b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/index.ts new file mode 100644 index 00000000000..42dc3e84d78 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/AppSidebarHeader/index.ts @@ -0,0 +1 @@ +export { AppSidebarHeader } from "./AppSidebarHeader"; diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx new file mode 100644 index 00000000000..79210f75b33 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { Avatar, AvatarFallback, AvatarImage } from "@superset/ui/avatar"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@superset/ui/dropdown-menu"; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "@superset/ui/sidebar"; +import { + BadgeCheck, + Bell, + ChevronsUpDown, + LogOut, + Settings, +} from "lucide-react"; +import { useSignOut } from "@/lib/auth/client"; +import type { User } from "@/lib/auth/types"; + +export interface NavUserProps { + user: User; +} + +export function NavUser({ user }: NavUserProps) { + const { isMobile } = useSidebar(); + const { signOut } = useSignOut(); + + const userInitials = user.name + .split(" ") + .map((name) => name[0]) + .join(""); + + return ( + + + + + + + + + {userInitials} + + +
+ {user.name} + {user.email} +
+ +
+
+ + +
+ + + + {userInitials} + + +
+ {user.name} + {user.email} +
+
+
+ + + + + Account + + + + Settings + + + + Notifications + + + + + + Log out + +
+
+
+
+ ); +} diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/index.ts b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/index.ts new file mode 100644 index 00000000000..943338b5943 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/index.ts @@ -0,0 +1 @@ +export { NavUser } from "./NavUser"; diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/SearchForm.tsx b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/SearchForm.tsx new file mode 100644 index 00000000000..e8d9b036a46 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/SearchForm.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { Label } from "@superset/ui/label"; +import { + SidebarGroup, + SidebarGroupContent, + SidebarInput, +} from "@superset/ui/sidebar"; +import { Search } from "lucide-react"; + +export function SearchForm({ ...props }: React.ComponentProps<"form">) { + return ( +
+ + + + + + + +
+ ); +} diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/index.ts b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/index.ts new file mode 100644 index 00000000000..04887529f16 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/components/SearchForm/index.ts @@ -0,0 +1 @@ +export { SearchForm } from "./SearchForm"; diff --git a/apps/admin/src/app/(dashboard)/components/AppSidebar/index.ts b/apps/admin/src/app/(dashboard)/components/AppSidebar/index.ts new file mode 100644 index 00000000000..e7a86c45f97 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/components/AppSidebar/index.ts @@ -0,0 +1 @@ +export { AppSidebar } from "./AppSidebar"; diff --git a/apps/admin/src/app/(dashboard)/layout.tsx b/apps/admin/src/app/(dashboard)/layout.tsx new file mode 100644 index 00000000000..56f8522c366 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/layout.tsx @@ -0,0 +1,57 @@ +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@superset/ui/breadcrumb"; +import { Separator } from "@superset/ui/separator"; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@superset/ui/sidebar"; +import { redirect } from "next/navigation"; + +import { env } from "@/env"; +import { currentUser } from "@/lib/auth/server"; + +import { AppSidebar } from "./components/AppSidebar"; + +export default async function DashboardLayout({ + children, +}: { + children: React.ReactNode; +}) { + const user = await currentUser(); + + // Redirect unauthorized users to web app + if (!user) { + redirect(env.NEXT_PUBLIC_WEB_URL); + } + + return ( + + + +
+ + + + + + Admin Panel + + + + Dashboard + + + +
+
{children}
+
+
+ ); +} diff --git a/apps/admin/src/app/(dashboard)/page.tsx b/apps/admin/src/app/(dashboard)/page.tsx new file mode 100644 index 00000000000..a015f501e17 --- /dev/null +++ b/apps/admin/src/app/(dashboard)/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardPage() { + return null; +} diff --git a/apps/admin/public/favicon.ico b/apps/admin/src/app/favicon.ico similarity index 100% rename from apps/admin/public/favicon.ico rename to apps/admin/src/app/favicon.ico diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx deleted file mode 100644 index 00c034e5bf3..00000000000 --- a/apps/admin/src/app/page.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { api } from "@/trpc/server"; - -export default async function Home() { - const users = await (await api()).user.all.query(); - - return ( -
-

Superset Admin

-

Admin dashboard

-
-

tRPC Test Query

-

- Users in database: {users.length} -

- {users.length > 0 && ( -
    - {users.slice(0, 5).map((user) => ( -
  • {user.email}
  • - ))} -
- )} -
-
- ); -} diff --git a/apps/admin/src/env.ts b/apps/admin/src/env.ts index d24b66775de..95217b2c608 100644 --- a/apps/admin/src/env.ts +++ b/apps/admin/src/env.ts @@ -14,6 +14,8 @@ export const env = createEnv({ // Database (needed by @superset/trpc dependency) DATABASE_URL: z.string().url(), DATABASE_URL_UNPOOLED: z.string().url(), + // Mock auth - optional, if not set user is unauthenticated + MOCK_USER_ID: z.string().uuid().optional(), }, client: { diff --git a/apps/admin/src/lib/auth/client.tsx b/apps/admin/src/lib/auth/client.tsx new file mode 100644 index 00000000000..2548156f5ec --- /dev/null +++ b/apps/admin/src/lib/auth/client.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { + createContext, + type ReactNode, + useCallback, + useContext, + useMemo, +} from "react"; + +import type { AuthState, User } from "./types"; + +const AuthContext = createContext(null); + +interface AuthProviderProps { + children: ReactNode; + user: User; +} + +/** + * Mock auth provider for client components. + * Receives user from server component and provides it to children. + * + * When real auth (Clerk) is added, replace with ClerkProvider. + */ +export function AuthProvider({ children, user }: AuthProviderProps) { + const value = useMemo( + () => ({ + user, + isLoaded: true, + isSignedIn: true, + }), + [user], + ); + + return {children}; +} + +/** + * Hook to access auth state in client components. + * + * When real auth (Clerk) is added, replace with useAuth() from @clerk/nextjs. + */ +export function useAuth(): AuthState { + const context = useContext(AuthContext); + + if (!context) { + throw new Error("useAuth must be used within an AuthProvider"); + } + + return context; +} + +/** + * Hook to access the current user in client components. + * Returns null if not signed in. + */ +export function useUser(): User | null { + const { user } = useAuth(); + return user; +} + +/** + * Mock sign out function. + * When real auth is added, this will call Clerk's signOut. + */ +export function useSignOut() { + const signOut = useCallback(() => { + // Mock: redirect to web app + window.location.href = process.env.NEXT_PUBLIC_WEB_URL || "/"; + }, []); + + return { signOut }; +} diff --git a/apps/admin/src/lib/auth/index.ts b/apps/admin/src/lib/auth/index.ts new file mode 100644 index 00000000000..71b3347d5b4 --- /dev/null +++ b/apps/admin/src/lib/auth/index.ts @@ -0,0 +1,8 @@ +// Re-export types (safe for both client and server) + +// Re-export client utilities (safe for client components) +export { AuthProvider, useAuth, useSignOut, useUser } from "./client"; +export type { AuthState, User } from "./types"; + +// NOTE: Server utilities must be imported directly: +// import { currentUser } from "@/lib/auth/server"; diff --git a/apps/admin/src/lib/auth/server.ts b/apps/admin/src/lib/auth/server.ts new file mode 100644 index 00000000000..9391f718ae1 --- /dev/null +++ b/apps/admin/src/lib/auth/server.ts @@ -0,0 +1,33 @@ +import "server-only"; + +import { createCaller, createTRPCContext } from "@superset/trpc"; + +import type { User } from "./types"; + +/** + * Get the current user on the server. + * Uses tRPC caller to fetch user from DB. + * + * Note: The proxy already validates auth and domain access, + * so this primarily exists to get user data for display. + * + * Returns null if not authenticated. + */ +export async function currentUser(): Promise { + try { + const ctx = await createTRPCContext({ headers: new Headers() }); + const caller = createCaller(ctx); + const user = await caller.user.me(); + + if (!user) return null; + + return { + id: user.id, + email: user.email, + name: user.name, + imageUrl: user.avatarUrl ?? undefined, + }; + } catch { + return null; + } +} diff --git a/apps/admin/src/lib/auth/types.ts b/apps/admin/src/lib/auth/types.ts new file mode 100644 index 00000000000..cf45f6068dc --- /dev/null +++ b/apps/admin/src/lib/auth/types.ts @@ -0,0 +1,12 @@ +export interface User { + id: string; + email: string; + name: string; + imageUrl?: string; +} + +export interface AuthState { + user: User | null; + isLoaded: boolean; + isSignedIn: boolean; +} diff --git a/apps/admin/src/proxy.ts b/apps/admin/src/proxy.ts new file mode 100644 index 00000000000..7733976197e --- /dev/null +++ b/apps/admin/src/proxy.ts @@ -0,0 +1,57 @@ +import { COMPANY } from "@superset/shared/constants"; +import { createCaller, createTRPCContext } from "@superset/trpc"; +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import { env } from "./env"; + +/** + * Public routes that bypass auth + */ +const PUBLIC_ROUTES = ["/ingest", "/monitoring"]; + +function isPublicRoute(pathname: string): boolean { + return PUBLIC_ROUTES.some( + (route) => pathname === route || pathname.startsWith(`${route}/`), + ); +} + +/** + * Auth proxy - validates user authentication and domain access. + * + * When real auth (Clerk) is added, replace with Clerk's proxy/middleware. + */ +export async function proxy(request: NextRequest) { + const { pathname } = request.nextUrl; + + // Allow public routes + if (isPublicRoute(pathname)) { + return NextResponse.next(); + } + + try { + // Create tRPC caller with current session context + const ctx = await createTRPCContext({ headers: request.headers }); + const caller = createCaller(ctx); + + // Get current user (throws if not authenticated) + const user = await caller.user.me(); + + // Validate domain access + if (!user?.email.endsWith(COMPANY.emailDomain)) { + return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); + } + + return NextResponse.next(); + } catch { + // Not authenticated - redirect to web app + return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); + } +} + +export const config = { + matcher: [ + "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", + "/(api|trpc)(.*)", + ], +}; diff --git a/apps/api/next-env.d.ts b/apps/api/next-env.d.ts deleted file mode 100644 index 830fb594ca2..00000000000 --- a/apps/api/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/api/next.config.ts b/apps/api/next.config.ts index 8ea4db5a0d2..814a700cce8 100644 --- a/apps/api/next.config.ts +++ b/apps/api/next.config.ts @@ -9,9 +9,7 @@ const allowedOrigins = [ ].filter(Boolean) as string[]; const config: NextConfig = { - experimental: { - reactCompiler: true, - }, + reactCompiler: true, typescript: { ignoreBuildErrors: true }, async headers() { // Generate CORS headers for each allowed origin diff --git a/apps/api/package.json b/apps/api/package.json index e83fde1b612..9b266ec4af5 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -17,16 +17,16 @@ "@t3-oss/env-nextjs": "^0.13.8", "@trpc/server": "^11.7.1", "drizzle-orm": "0.45.1", - "next": "^15.5.7", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "next": "^16.0.10", + "react": "^19.2.3", + "react-dom": "^19.2.3", "zod": "^4.1.13" }, "devDependencies": { "@superset/typescript": "workspace:*", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "typescript": "^5.9.3" } diff --git a/apps/api/public/favicon.ico b/apps/api/src/app/favicon.ico similarity index 100% rename from apps/api/public/favicon.ico rename to apps/api/src/app/favicon.ico diff --git a/apps/cli/package.json b/apps/cli/package.json index 896eb999d2f..01e7b2447b3 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -33,13 +33,13 @@ "ink-text-input": "^6.0.0", "lowdb": "^7.0.1", "meow": "^11.0.0", - "react": "^19.1.1", + "react": "^19.2.3", "react-devtools-core": "^7.0.1", "string-width": "^8.1.0" }, "devDependencies": { "@superset/typescript": "workspace:*", - "@types/react": "^19.1.11", + "@types/react": "^19.2.7", "bun-types": "^1.3.1", "chalk": "^5.6.2", "chokidar": "^3.5.3", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 6dbd407426d..c9a62ed4250 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -74,11 +74,11 @@ "nanoid": "^5.1.6", "node-pty": "1.1.0-beta30", "os-locale": "^6.0.2", - "react": "^19.1.1", + "react": "^19.2.3", "react-arborist": "^3.4.3", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^19.1.1", + "react-dom": "^19.2.3", "react-hotkeys-hook": "^5.2.1", "react-icons": "^5.5.0", "react-mosaic-component": "^6.1.1", @@ -91,6 +91,7 @@ "superjson": "^2.2.5", "tailwind-merge": "^3.4.0", "trpc-electron": "^0.1.2", + "tw-animate-css": "^1.4.0", "unique-names-generator": "^4.7.1", "zod": "^4.1.13", "zustand": "^5.0.8" @@ -103,8 +104,8 @@ "@types/http-proxy": "^1.17.17", "@types/lodash": "^4.17.20", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/react-syntax-highlighter": "^15.5.13", "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", @@ -119,7 +120,6 @@ "rimraf": "^6.0.1", "rollup-plugin-inject-process-env": "^1.3.1", "tailwindcss": "^4.0.9", - "tailwindcss-animate": "^1.0.7", "tsx": "^4.19.3", "typescript": "^5.9.3", "vite": "^7.1.3", diff --git a/apps/desktop/src/renderer/globals.css b/apps/desktop/src/renderer/globals.css index 6aa961ca75a..aaeac15edbb 100644 --- a/apps/desktop/src/renderer/globals.css +++ b/apps/desktop/src/renderer/globals.css @@ -1,11 +1,8 @@ @import "tailwindcss"; -@import "@superset/ui/globals.css"; - -@plugin "tailwindcss-animate"; +@import "tw-animate-css"; @source "./**/*.{ts,tsx}"; - -@custom-variant dark (&:is(.dark *)); +@source "../../../../packages/ui/src/**/*.{ts,tsx}"; /** * Theme CSS Variables @@ -135,8 +132,7 @@ @layer base { * { - @apply border-border outline-ring/50; - @apply antialiased; + @apply border-border antialiased; } body { diff --git a/apps/docs/next.config.ts b/apps/docs/next.config.ts index 3856fd85073..dfe5e3d6b7e 100644 --- a/apps/docs/next.config.ts +++ b/apps/docs/next.config.ts @@ -7,7 +7,6 @@ const withNextra = nextra({ const nextConfig: NextConfig = { reactStrictMode: true, - transpilePackages: ["@superset/ui"], }; export default withNextra(nextConfig); diff --git a/apps/docs/package.json b/apps/docs/package.json index 7b17da44ed8..b546c221168 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -5,7 +5,7 @@ "scripts": { "clean": "git clean -xdf .cache .next .turbo node_modules", "dev": "next dev --port 3004", - "build": "next build", + "build": "next build --webpack", "start": "next start", "typecheck": "tsc --noEmit" }, @@ -15,12 +15,12 @@ "@superset/ui": "workspace:*", "framer-motion": "^12.23.24", "geist": "^1.5.1", - "next": "^15.5.7", - "nextra": "^4.6.0", - "nextra-theme-blog": "^4.6.0", - "nextra-theme-docs": "^4.6.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "next": "^16.0.10", + "nextra": "^4.6.1", + "nextra-theme-blog": "^4.6.1", + "nextra-theme-docs": "^4.6.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "three": "^0.181.2" }, "devDependencies": { @@ -28,8 +28,8 @@ "@tailwindcss/postcss": "^4.0.9", "@types/mdx": "^2.0.13", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/three": "^0.181.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3" diff --git a/apps/docs/src/app/favicon.ico b/apps/docs/src/app/favicon.ico index 6922b9cfbce..9c3517c0d4e 100644 Binary files a/apps/docs/src/app/favicon.ico and b/apps/docs/src/app/favicon.ico differ diff --git a/apps/marketing/next-env.d.ts b/apps/marketing/next-env.d.ts deleted file mode 100644 index 830fb594ca2..00000000000 --- a/apps/marketing/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/marketing/next.config.ts b/apps/marketing/next.config.ts index 3b3de1463ba..bf7a6e6b292 100644 --- a/apps/marketing/next.config.ts +++ b/apps/marketing/next.config.ts @@ -2,9 +2,7 @@ import type { NextConfig } from "next"; const config: NextConfig = { reactStrictMode: true, - experimental: { - reactCompiler: true, - }, + reactCompiler: true, typescript: { ignoreBuildErrors: true }, }; diff --git a/apps/marketing/package.json b/apps/marketing/package.json index f843bfa025b..7f6d0f86a75 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -18,10 +18,10 @@ "framer-motion": "^12.23.24", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-fast-marquee": "^1.6.5", "react-icons": "^5.5.0", "three": "^0.181.2", @@ -32,8 +32,8 @@ "@tailwindcss/postcss": "^4.0.9", "@types/mdx": "^2.0.13", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/three": "^0.181.0", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", diff --git a/apps/docs/public/favicon.ico b/apps/marketing/src/app/favicon.ico similarity index 100% rename from apps/docs/public/favicon.ico rename to apps/marketing/src/app/favicon.ico diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts deleted file mode 100644 index 830fb594ca2..00000000000 --- a/apps/web/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 737f1468308..d741b07ad86 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,9 +1,7 @@ import type { NextConfig } from "next"; const config: NextConfig = { - experimental: { - reactCompiler: true, - }, + reactCompiler: true, typescript: { ignoreBuildErrors: true }, }; diff --git a/apps/web/package.json b/apps/web/package.json index 8de561eb4a4..764a40beabc 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -24,10 +24,10 @@ "@trpc/tanstack-react-query": "^11.7.1", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "server-only": "^0.0.1", "superjson": "^2.2.5", "zod": "^4.1.13" @@ -36,8 +36,8 @@ "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3" diff --git a/apps/web/public/favicon.ico b/apps/web/public/favicon.ico deleted file mode 100644 index 9c3517c0d4e..00000000000 Binary files a/apps/web/public/favicon.ico and /dev/null differ diff --git a/apps/marketing/public/favicon.ico b/apps/web/src/app/favicon.ico similarity index 100% rename from apps/marketing/public/favicon.ico rename to apps/web/src/app/favicon.ico diff --git a/bun.lock b/bun.lock index 116de928bc9..d42f1fcf870 100644 --- a/bun.lock +++ b/bun.lock @@ -27,10 +27,10 @@ "@trpc/tanstack-react-query": "^11.7.1", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "server-only": "^0.0.1", "superjson": "^2.2.5", "zod": "^4.1.13", @@ -39,8 +39,8 @@ "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", @@ -56,16 +56,16 @@ "@t3-oss/env-nextjs": "^0.13.8", "@trpc/server": "^11.7.1", "drizzle-orm": "0.45.1", - "next": "^15.5.7", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "next": "^16.0.10", + "react": "^19.2.3", + "react-dom": "^19.2.3", "zod": "^4.1.13", }, "devDependencies": { "@superset/typescript": "workspace:*", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "typescript": "^5.9.3", }, @@ -84,13 +84,13 @@ "ink-text-input": "^6.0.0", "lowdb": "^7.0.1", "meow": "^11.0.0", - "react": "^19.1.1", + "react": "^19.2.3", "react-devtools-core": "^7.0.1", "string-width": "^8.1.0", }, "devDependencies": { "@superset/typescript": "workspace:*", - "@types/react": "^19.1.11", + "@types/react": "^19.2.7", "bun-types": "^1.3.1", "chalk": "^5.6.2", "chokidar": "^3.5.3", @@ -145,11 +145,11 @@ "nanoid": "^5.1.6", "node-pty": "1.1.0-beta30", "os-locale": "^6.0.2", - "react": "^19.1.1", + "react": "^19.2.3", "react-arborist": "^3.4.3", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^19.1.1", + "react-dom": "^19.2.3", "react-hotkeys-hook": "^5.2.1", "react-icons": "^5.5.0", "react-mosaic-component": "^6.1.1", @@ -162,6 +162,7 @@ "superjson": "^2.2.5", "tailwind-merge": "^3.4.0", "trpc-electron": "^0.1.2", + "tw-animate-css": "^1.4.0", "unique-names-generator": "^4.7.1", "zod": "^4.1.13", "zustand": "^5.0.8", @@ -174,8 +175,8 @@ "@types/http-proxy": "^1.17.17", "@types/lodash": "^4.17.20", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/react-syntax-highlighter": "^15.5.13", "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", @@ -190,7 +191,6 @@ "rimraf": "^6.0.1", "rollup-plugin-inject-process-env": "^1.3.1", "tailwindcss": "^4.0.9", - "tailwindcss-animate": "^1.0.7", "tsx": "^4.19.3", "typescript": "^5.9.3", "vite": "^7.1.3", @@ -206,12 +206,12 @@ "@superset/ui": "workspace:*", "framer-motion": "^12.23.24", "geist": "^1.5.1", - "next": "^15.5.7", - "nextra": "^4.6.0", - "nextra-theme-blog": "^4.6.0", - "nextra-theme-docs": "^4.6.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "next": "^16.0.10", + "nextra": "^4.6.1", + "nextra-theme-blog": "^4.6.1", + "nextra-theme-docs": "^4.6.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "three": "^0.181.2", }, "devDependencies": { @@ -219,8 +219,8 @@ "@tailwindcss/postcss": "^4.0.9", "@types/mdx": "^2.0.13", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/three": "^0.181.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", @@ -237,10 +237,10 @@ "framer-motion": "^12.23.24", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "react-fast-marquee": "^1.6.5", "react-icons": "^5.5.0", "three": "^0.181.2", @@ -251,8 +251,8 @@ "@tailwindcss/postcss": "^4.0.9", "@types/mdx": "^2.0.13", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/three": "^0.181.0", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", @@ -276,10 +276,10 @@ "@trpc/tanstack-react-query": "^11.7.1", "geist": "^1.5.1", "lucide-react": "^0.560.0", - "next": "^15.5.7", + "next": "^16.0.10", "next-themes": "^0.4.6", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "server-only": "^0.0.1", "superjson": "^2.2.5", "zod": "^4.1.13", @@ -288,8 +288,8 @@ "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", "@types/node": "^24.9.1", - "@types/react": "^19.1.11", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "babel-plugin-react-compiler": "^1.0.0", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", @@ -322,12 +322,12 @@ }, "devDependencies": { "@superset/typescript": "workspace:*", - "@types/react": "^19.1.11", - "react": "^19.1.1", + "@types/react": "^19.2.7", + "react": "^19.2.3", "typescript": "^5.9.3", }, "peerDependencies": { - "react": "^19.1.1", + "react": "^19.2.3", }, }, "packages/shared": { @@ -346,6 +346,7 @@ "version": "0.1.0", "dependencies": { "@superset/db": "workspace:*", + "@superset/shared": "workspace:*", "@t3-oss/env-core": "^0.13.8", "@trpc/server": "^11.7.1", "drizzle-orm": "0.45.1", @@ -419,13 +420,13 @@ "devDependencies": { "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", - "@types/react": "^19.1.11", - "react": "^19.1.1", + "@types/react": "^19.2.7", + "react": "^19.2.3", "tailwindcss": "^4.0.9", "typescript": "^5.9.3", }, "peerDependencies": { - "react": "^19.1.1", + "react": "^19.2.3", }, }, "tooling/typescript": { @@ -768,23 +769,23 @@ "@neondatabase/serverless": ["@neondatabase/serverless@1.0.2", "", { "dependencies": { "@types/node": "^22.15.30", "@types/pg": "^8.8.0" } }, "sha512-I5sbpSIAHiB+b6UttofhrN/UJXII+4tZPAq1qugzwCwLIL8EZLV7F/JyHUrEIiGgQpEXzpnjlJ+zwcEhheGvCw=="], - "@next/env": ["@next/env@15.5.9", "", {}, "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg=="], + "@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.7", "", { "os": "linux", "cpu": "x64" }, "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.7", "", { "os": "win32", "cpu": "x64" }, "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -2394,7 +2395,7 @@ "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], - "next": ["next@15.5.9", "", { "dependencies": { "@next/env": "15.5.9", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.7", "@next/swc-darwin-x64": "15.5.7", "@next/swc-linux-arm64-gnu": "15.5.7", "@next/swc-linux-arm64-musl": "15.5.7", "@next/swc-linux-x64-gnu": "15.5.7", "@next/swc-linux-x64-musl": "15.5.7", "@next/swc-win32-arm64-msvc": "15.5.7", "@next/swc-win32-x64-msvc": "15.5.7", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg=="], + "next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="], "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], @@ -2922,8 +2923,6 @@ "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], - "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], - "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], diff --git a/packages/queries/package.json b/packages/queries/package.json index e40f0fb9f45..1435e074494 100644 --- a/packages/queries/package.json +++ b/packages/queries/package.json @@ -18,11 +18,11 @@ }, "devDependencies": { "@superset/typescript": "workspace:*", - "@types/react": "^19.1.11", - "react": "^19.1.1", + "@types/react": "^19.2.7", + "react": "^19.2.3", "typescript": "^5.9.3" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.3" } } diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts new file mode 100644 index 00000000000..2e8d2ac0f9b --- /dev/null +++ b/packages/shared/src/constants.ts @@ -0,0 +1,6 @@ +// Company constants +export const COMPANY = { + name: "Superset", + domain: "superset.sh", + emailDomain: "@superset.sh", +} as const; diff --git a/packages/trpc/package.json b/packages/trpc/package.json index 6b5bdf65cb3..74e7d22839e 100644 --- a/packages/trpc/package.json +++ b/packages/trpc/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@superset/db": "workspace:*", + "@superset/shared": "workspace:*", "@t3-oss/env-core": "^0.13.8", "@trpc/server": "^11.7.1", "drizzle-orm": "0.45.1", diff --git a/packages/trpc/src/env.ts b/packages/trpc/src/env.ts index 31d24ce54ca..f049168f27c 100644 --- a/packages/trpc/src/env.ts +++ b/packages/trpc/src/env.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const env = createEnv({ server: { - MOCK_USER_ID: z.string().uuid(), + MOCK_USER_ID: z.string().uuid().optional(), }, clientPrefix: "PUBLIC_", client: {}, diff --git a/packages/trpc/src/index.ts b/packages/trpc/src/index.ts index ab5c88227f8..cd0ec942f61 100644 --- a/packages/trpc/src/index.ts +++ b/packages/trpc/src/index.ts @@ -4,6 +4,7 @@ export { appRouter, createCaller } from "./root"; // tRPC utilities export { + adminProcedure, createCallerFactory, createTRPCContext, createTRPCRouter, diff --git a/packages/trpc/src/router/organization.ts b/packages/trpc/src/router/organization.ts index 83ea46d8d32..80bba306f77 100644 --- a/packages/trpc/src/router/organization.ts +++ b/packages/trpc/src/router/organization.ts @@ -62,10 +62,10 @@ export const organizationRouter = { .values(input) .returning(); - if (ctx.session?.user.id && organization) { + if (ctx.session.userId && organization) { await db.insert(organizationMembers).values({ organizationId: organization.id, - userId: ctx.session.user.id, + userId: ctx.session.userId, }); } diff --git a/packages/trpc/src/router/task.ts b/packages/trpc/src/router/task.ts index ee6baf1bea4..7fe53587191 100644 --- a/packages/trpc/src/router/task.ts +++ b/packages/trpc/src/router/task.ts @@ -76,7 +76,7 @@ export const taskRouter = { .insert(tasks) .values({ ...input, - creatorId: ctx.session.user.id, + creatorId: ctx.session.userId, }) .returning(); return task; diff --git a/packages/trpc/src/router/user.ts b/packages/trpc/src/router/user.ts index 5d1d6cafadd..5b8acb736b1 100644 --- a/packages/trpc/src/router/user.ts +++ b/packages/trpc/src/router/user.ts @@ -8,7 +8,7 @@ import { protectedProcedure, publicProcedure } from "../trpc"; export const userRouter = { me: protectedProcedure.query(async ({ ctx }) => { return db.query.users.findFirst({ - where: eq(users.id, ctx.session.user.id), + where: eq(users.id, ctx.session.userId), }); }), diff --git a/packages/trpc/src/trpc.ts b/packages/trpc/src/trpc.ts index 31d15cc6b69..ad5a4902fee 100644 --- a/packages/trpc/src/trpc.ts +++ b/packages/trpc/src/trpc.ts @@ -1,13 +1,18 @@ +import { db } from "@superset/db/client"; +import { users } from "@superset/db/schema"; +import { COMPANY } from "@superset/shared/constants"; import { initTRPC, TRPCError } from "@trpc/server"; +import { eq } from "drizzle-orm"; import superjson from "superjson"; import { ZodError } from "zod"; + import { env } from "./env"; /** * Context passed to every tRPC procedure */ export type TRPCContext = { - session: { user: { id: string } } | null; + session: { userId: string } | null; headers: Headers; }; @@ -20,7 +25,7 @@ export const createTRPCContext = async (opts: { const mockUserId = env.MOCK_USER_ID; return { - session: mockUserId ? { user: { id: mockUserId } } : null, + session: mockUserId ? { userId: mockUserId } : null, headers: opts.headers, }; }; @@ -45,8 +50,12 @@ export const createCallerFactory = t.createCallerFactory; export const publicProcedure = t.procedure; +/** + * Protected procedure - requires authenticated session + * Just validates session exists, no DB fetch + */ export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => { - if (!ctx.session?.user) { + if (!ctx.session?.userId) { throw new TRPCError({ code: "UNAUTHORIZED", message: @@ -60,3 +69,33 @@ export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => { }, }); }); + +/** + * Admin procedure - requires authenticated user with @superset.sh email + * Fetches user from DB and validates domain for API security + */ +export const adminProcedure = protectedProcedure.use(async ({ ctx, next }) => { + const user = await db.query.users.findFirst({ + where: eq(users.id, ctx.session.userId), + }); + + if (!user) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "User not found in database.", + }); + } + + if (!user.email.endsWith(COMPANY.emailDomain)) { + throw new TRPCError({ + code: "FORBIDDEN", + message: `Admin access requires ${COMPANY.emailDomain} email.`, + }); + } + + return next({ + ctx: { + user, + }, + }); +}); diff --git a/packages/ui/package.json b/packages/ui/package.json index a33c43caf1c..a2463ac0aec 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -75,12 +75,12 @@ "devDependencies": { "@superset/typescript": "workspace:*", "@tailwindcss/postcss": "^4.0.9", - "@types/react": "^19.1.11", - "react": "^19.1.1", + "@types/react": "^19.2.7", + "react": "^19.2.3", "tailwindcss": "^4.0.9", "typescript": "^5.9.3" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.3" } } diff --git a/packages/ui/src/globals.css b/packages/ui/src/globals.css index 7e5d9949681..0c05255f579 100644 --- a/packages/ui/src/globals.css +++ b/packages/ui/src/globals.css @@ -3,87 +3,34 @@ @custom-variant dark (&:is(.dark *)); -@source "./**/*.{ts,tsx}"; - -@theme { - --color-background: 0 0% 100%; - --color-foreground: 240 10% 3.9%; - --color-card: 0 0% 100%; - --color-card-foreground: 240 10% 3.9%; - --color-popover: 0 0% 100%; - --color-popover-foreground: 240 10% 3.9%; - --color-primary: 240 5.9% 10%; - --color-primary-foreground: 0 0% 98%; - --color-secondary: 240 4.8% 95.9%; - --color-secondary-foreground: 240 5.9% 10%; - --color-muted: 240 4.8% 95.9%; - --color-muted-foreground: 240 3.8% 46.1%; - --color-accent: 240 4.8% 95.9%; - --color-accent-foreground: 240 5.9% 10%; - --color-destructive: 0 84.2% 60.2%; - --color-destructive-foreground: 0 0% 98%; - --color-border: 240 5.9% 90%; - --color-input: 240 5.9% 90%; - --color-ring: 240 5.9% 10%; - --radius: 0.5rem; -} - -@media (prefers-color-scheme: dark) { - @theme { - --color-background: 240 10% 3.9%; - --color-foreground: 0 0% 98%; - --color-card: 240 10% 3.9%; - --color-card-foreground: 0 0% 98%; - --color-popover: 240 10% 3.9%; - --color-popover-foreground: 0 0% 98%; - --color-primary: 0 0% 98%; - --color-primary-foreground: 240 5.9% 10%; - --color-secondary: 240 3.7% 15.9%; - --color-secondary-foreground: 0 0% 98%; - --color-muted: 240 3.7% 15.9%; - --color-muted-foreground: 240 5% 64.9%; - --color-accent: 240 3.7% 15.9%; - --color-accent-foreground: 0 0% 98%; - --color-destructive: 0 62.8% 30.6%; - --color-destructive-foreground: 0 0% 98%; - --color-border: 240 3.7% 15.9%; - --color-input: 240 3.7% 15.9%; - --color-ring: 240 4.9% 83.9%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -:root { - --sidebar: hsl(0 0% 98%); - --sidebar-foreground: hsl(240 5.3% 26.1%); - --sidebar-primary: hsl(240 5.9% 10%); - --sidebar-primary-foreground: hsl(0 0% 98%); - --sidebar-accent: hsl(240 4.8% 95.9%); - --sidebar-accent-foreground: hsl(240 5.9% 10%); - --sidebar-border: hsl(220 13% 91%); - --sidebar-ring: hsl(217.2 91.2% 59.8%); -} - -.dark { - --sidebar: hsl(240 5.9% 10%); - --sidebar-foreground: hsl(240 4.8% 95.9%); - --sidebar-primary: hsl(224.3 76.3% 48%); - --sidebar-primary-foreground: hsl(0 0% 100%); - --sidebar-accent: hsl(240 3.7% 15.9%); - --sidebar-accent-foreground: hsl(240 4.8% 95.9%); - --sidebar-border: hsl(240 3.7% 15.9%); - --sidebar-ring: hsl(217.2 91.2% 59.8%); -} - @theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); @@ -94,9 +41,78 @@ --color-sidebar-ring: var(--sidebar-ring); } +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + @layer base { * { - @apply border-border outline-ring/50; + @apply border-border; } body { @apply bg-background text-foreground;