- apps/web: Vite + React 19 + TanStack Router/Query + shadcn/ui - apps/api: Hono + Bun on :3001 with /api/health, /api/auth/*, /mcp stubs - apps/worker: Bun worker stub, DB connection, graceful SIGTERM - apps/web-legacy/: old Next.js code moved aside (preserved for T2-T8 reference) - Dockerfiles: api (Bun), worker (Bun), spa (multi-stage Caddy) - Caddyfile: serves dist + reverse-proxies /api/* + /mcp to api - docker-compose.yml: 4-service target (api, spa, db, worker) - packages/db/src/client.ts: shared Drizzle client for api + worker - db/client.ts: root-level alias for convenience Parent: t_e1cbd87d -> t_24c9c3fd (T0)
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
import bcrypt from 'bcryptjs';
|
|
import { count, eq } from 'drizzle-orm';
|
|
import type { NextAuthOptions } from 'next-auth';
|
|
import CredentialsProvider from 'next-auth/providers/credentials';
|
|
import { db, users } from '@project-e/db';
|
|
import { seedDefaultData } from './seed';
|
|
|
|
export const authOptions: NextAuthOptions = {
|
|
session: { strategy: 'jwt' },
|
|
providers: [
|
|
CredentialsProvider({
|
|
name: 'Credentials',
|
|
credentials: {
|
|
email: { label: 'Email', type: 'email' },
|
|
password: { label: 'Password', type: 'password' },
|
|
},
|
|
async authorize(credentials) {
|
|
const email = credentials?.email?.trim().toLowerCase();
|
|
const password = credentials?.password;
|
|
if (!email || !password) return null;
|
|
|
|
let [user] = await db.select().from(users).where(eq(users.email, email)).limit(1);
|
|
|
|
if (!user) {
|
|
const [{ total }] = await db.select({ total: count() }).from(users);
|
|
const initialEmail = process.env.INITIAL_ADMIN_EMAIL?.trim().toLowerCase();
|
|
const initialPassword = process.env.INITIAL_ADMIN_PASSWORD;
|
|
if (total === 0 && email === initialEmail && password === initialPassword) {
|
|
[user] = await db.insert(users).values({
|
|
email,
|
|
name: process.env.INITIAL_ADMIN_NAME || email,
|
|
passwordHash: await bcrypt.hash(password, 12),
|
|
}).returning();
|
|
|
|
// Seed default workspace + welcome note on first user creation
|
|
await seedDefaultData();
|
|
}
|
|
}
|
|
|
|
if (!user || !(await bcrypt.compare(password, user.passwordHash))) return null;
|
|
return { id: user.id, email: user.email, name: user.name };
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
jwt({ token, user }) {
|
|
if (user) token.id = user.id;
|
|
return token;
|
|
},
|
|
session({ session, token }) {
|
|
if (session.user) session.user.id = token.id as string;
|
|
return session;
|
|
},
|
|
},
|
|
pages: { signIn: '/login' },
|
|
};
|