Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions apps/dbagent/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
DATABASE_URL='postgres://dbagent:changeme@localhost:5432/dbagent'
# Public URL of the app
PUBLIC_URL=http://localhost:4001

# The URL of the database that we use to store data
DATABASE_URL='postgres://...'

# The OpenID client settings
AUTH_SECRET="secret-key-for-jwt"
AUTH_OPENID_ID=
AUTH_OPENID_SECRET=
AUTH_OPENID_ISSUER=

# LLM API credentials
OPENAI_API_KEY=
DEEPSEEK_API_KEY=
ANTHROPIC_API_KEY=
PUBLIC_URL='http://localhost:4001'
5 changes: 3 additions & 2 deletions apps/dbagent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dev-scheduler": "tsx scripts/scheduler.ts"
},
"dependencies": {
"@ai-sdk/anthropic": "^1.1.10",
"@ai-sdk/anthropic": "^1.1.11",
"@ai-sdk/deepseek": "^0.1.12",
"@ai-sdk/openai": "^1.1.14",
"@aws-sdk/client-cloudwatch": "^3.750.0",
Expand All @@ -39,7 +39,8 @@
"framer-motion": "^12.4.7",
"kysely": "^0.27.5",
"lucide-react": "^0.476.0",
"next": "^15.1.7",
"next": "^15.2.0",
"next-auth": "5.0.0-beta.25",
"next-themes": "^0.4.4",
"pg": "^8.13.3",
"react": "19.0.0",
Expand Down
1 change: 1 addition & 0 deletions apps/dbagent/src/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SideNav } from '~/components/ui/side-nav';

export default async function Layout({ children }: { children: React.ReactNode }) {
const onboardingComplete = await getCompletedTaskPercentage();

return (
<>
<div className="flex h-full overflow-hidden">
Expand Down
2 changes: 2 additions & 0 deletions apps/dbagent/src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { handlers } from '~/auth';
export const { GET, POST } = handlers;
27 changes: 27 additions & 0 deletions apps/dbagent/src/app/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

import { signIn, useSession } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

export default function Page() {
const session = useSession();
const router = useRouter();

const params = new URLSearchParams(window.location.search);
const callbackUrl = params.get('callbackUrl');

useEffect(() => {
if (session.status === 'unauthenticated') {
void signIn('default').then(() => {
if (callbackUrl) {
router.push(callbackUrl);
}
});
} else if (session.status === 'authenticated' && callbackUrl) {
router.push(callbackUrl);
}
}, [session, router, callbackUrl]);

return null;
}
49 changes: 49 additions & 0 deletions apps/dbagent/src/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { env } from './lib/env/server';

function getProviders() {
if (env.AUTH_OPENID_ID && env.AUTH_OPENID_SECRET && env.AUTH_OPENID_ISSUER) {
return [
{
id: 'default',
name: 'OpenID',
type: 'oidc',
options: {
clientId: env.AUTH_OPENID_ID,
clientSecret: env.AUTH_OPENID_SECRET,
issuer: env.AUTH_OPENID_ISSUER
}
} as const
];
}

return [
Credentials({
id: 'default',
name: 'Local auth',
async authorize() {
return { name: 'User', email: 'user@localhost' };
}
})
];
}

const { handlers, signIn, signOut, auth } = NextAuth({
pages: {
signIn: '/signin'
},
providers: getProviders(),
secret: env.AUTH_SECRET,
session: {
strategy: 'jwt'
},
callbacks: {
authorized: async ({ auth }) => {
// Logged in users are authenticated, otherwise redirect to login page
return !!auth;
}
}
});

export { auth, handlers, signIn, signOut };
19 changes: 11 additions & 8 deletions apps/dbagent/src/components/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import { Toaster, TooltipProvider } from '@internal/components';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { SessionProvider } from 'next-auth/react';
import { ThemeProvider } from 'next-themes';

export const queryClient = new QueryClient();

export const Providers = ({ children }: { children: React.ReactNode }) => {
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider attribute="class">
<TooltipProvider>
<Toaster />
{children}
</TooltipProvider>
</ThemeProvider>
</QueryClientProvider>
<SessionProvider>
<QueryClientProvider client={queryClient}>
<ThemeProvider attribute="class">
<TooltipProvider>
<Toaster />
{children}
</TooltipProvider>
</ThemeProvider>
</QueryClientProvider>
</SessionProvider>
);
};
64 changes: 63 additions & 1 deletion apps/dbagent/src/components/ui/header-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
'use client';

import { Button, MakiLogoSymbol } from '@internal/components';
import { DarkTheme20Filled } from '@fluentui/react-icons';
import {
Avatar,
AvatarImage,
AvatarInitials,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
MakiLogoSymbol,
useIsMobile
} from '@internal/components';
import { useSession } from 'next-auth/react';
import { useTheme } from 'next-themes';
import Link from 'next/link';
import { PropsWithChildren } from 'react';

Expand All @@ -11,6 +28,8 @@ export type User = {
};

export const HeaderBar = ({ children }: PropsWithChildren<{ user?: User }>) => {
const { data: session } = useSession();

return (
<header className="bg-background fixed left-0 right-0 top-0 z-50 border-b">
<div className="flex h-14 items-center gap-4 px-4">
Expand All @@ -36,6 +55,7 @@ export const HeaderBar = ({ children }: PropsWithChildren<{ user?: User }>) => {
<Button variant="ghost" size="sm">
Docs
</Button>
<UserAvatar user={session?.user} />
</div>
</div>
</header>
Expand All @@ -45,3 +65,45 @@ export const HeaderBar = ({ children }: PropsWithChildren<{ user?: User }>) => {
export const BelowHeaderBar = ({ children }: PropsWithChildren) => {
return <div className="h-[calc(100vh-53px)]">{children}</div>;
};

function UserAvatar({ user }: { user?: User }) {
const { theme, setTheme } = useTheme();
const isMobile = useIsMobile();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Avatar className="h-8 w-8 cursor-pointer">
<AvatarImage src={user?.image ?? ''} alt={user?.name ?? 'User'} />
<AvatarInitials className="rounded-lg">{user?.name}</AvatarInitials>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent
className="-mr-8 mt-12 w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? 'bottom' : 'right'}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user?.image ?? ''} alt={user?.name ?? 'User'} />
<AvatarInitials className="rounded-lg">{user?.name}</AvatarInitials>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user?.name}</span>
<span className="truncate text-xs">{user?.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
<DropdownMenuItem>
<DarkTheme20Filled />
{theme === 'dark' ? 'Light' : 'Dark'} theme
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}
2 changes: 1 addition & 1 deletion apps/dbagent/src/lib/db/db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { env } from '../env/db';
import { env } from '../env/server';

export const pool = new Pool({
connectionString: env.DATABASE_URL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable no-process-env */

import { z } from 'zod';

const schema = z.object({
Expand Down
12 changes: 0 additions & 12 deletions apps/dbagent/src/lib/env/db.ts

This file was deleted.

16 changes: 0 additions & 16 deletions apps/dbagent/src/lib/env/scheduler.ts

This file was deleted.

28 changes: 28 additions & 0 deletions apps/dbagent/src/lib/env/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable no-process-env */
import 'server-only';
import { z } from 'zod';
import { env as clientEnv } from './client';

const schema = z.object({
// The URL of the database that we use to store data
DATABASE_URL: z.string(),

// The OpenID client settings
AUTH_SECRET: z.string().optional(),
AUTH_OPENID_ID: z.string().optional(),
AUTH_OPENID_SECRET: z.string().optional(),
AUTH_OPENID_ISSUER: z.string().optional(),

// LLM API credentials
OPENAI_API_KEY: z.string(),
DEEPSEEK_API_KEY: z.string(),
ANTHROPIC_API_KEY: z.string(),

// Scheduler
MAX_PARALLEL_RUNS: z.number().default(20), // How many schedules can be run in parallel
TIMEOUT_FOR_RUNNING_SCHEDULE_SECS: z.number().default(15 * 60) // How long to wait before assuming it's dead and restart
});

const serverEnv = schema.parse(process.env);

export const env = { ...clientEnv, ...serverEnv };
2 changes: 1 addition & 1 deletion apps/dbagent/src/lib/monitoring/scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
setScheduleStatusRunning,
updateScheduleRunData
} from '~/lib/db/schedules';
import { env } from '../env/scheduler';
import { env } from '../env/server';
import { runSchedule } from './runner';

export function shouldRunSchedule(schedule: Schedule, now: Date): boolean {
Expand Down
2 changes: 1 addition & 1 deletion apps/dbagent/src/lib/notifications/slack-webhook.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DbConnection } from '../db/connections';
import { getIntegration } from '../db/integrations';
import { Schedule } from '../db/schedules';
import { env } from '../env/general';
import { env } from '../env/client';

export type NotificationLevel = 'info' | 'warning' | 'alert';

Expand Down
6 changes: 6 additions & 0 deletions apps/dbagent/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { auth as middleware } from '~/auth';

export const config = {
// Don't run middleware on paths that don't require authentication
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
};
2 changes: 1 addition & 1 deletion configs/eslint-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"@eslint/compat": "^1.2.7",
"@eslint/js": "^9.21.0",
"@next/eslint-plugin-next": "^15.1.7",
"@next/eslint-plugin-next": "^15.2.0",
"eslint-plugin-check-file": "^3.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
Expand Down
Loading
Loading