Initial commit — AI-powered coding tutor (Professor)
Next.js 16, React 19, Monaco editor, Anthropic SDK, multi-provider AI, Wandbox Python execution, iframe HTML preview, SQLite auth + session persistence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
23
app/api/auth/login/route.ts
Normal file
23
app/api/auth/login/route.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/db';
|
||||
import { users } from '@/db/schema';
|
||||
import { verifyPassword, signToken, setAuthCookie } from '@/lib/auth';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { email, password } = await req.json();
|
||||
|
||||
if (!email || !password) {
|
||||
return NextResponse.json({ error: 'Email and password are required' }, { status: 400 });
|
||||
}
|
||||
|
||||
const user = await db.select().from(users).where(eq(users.email, email)).get();
|
||||
if (!user || !(await verifyPassword(password, user.passwordHash))) {
|
||||
return NextResponse.json({ error: 'Invalid email or password' }, { status: 401 });
|
||||
}
|
||||
|
||||
const token = await signToken({ userId: user.id, email: user.email });
|
||||
await setAuthCookie(token);
|
||||
|
||||
return NextResponse.json({ user: { id: user.id, email: user.email } });
|
||||
}
|
||||
7
app/api/auth/logout/route.ts
Normal file
7
app/api/auth/logout/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { clearAuthCookie } from '@/lib/auth';
|
||||
|
||||
export async function POST() {
|
||||
await clearAuthCookie();
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
8
app/api/auth/me/route.ts
Normal file
8
app/api/auth/me/route.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getAuthUser } from '@/lib/auth';
|
||||
|
||||
export async function GET() {
|
||||
const user = await getAuthUser();
|
||||
if (!user) return NextResponse.json({ user: null });
|
||||
return NextResponse.json({ user: { id: user.userId, email: user.email } });
|
||||
}
|
||||
30
app/api/auth/register/route.ts
Normal file
30
app/api/auth/register/route.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/db';
|
||||
import { users } from '@/db/schema';
|
||||
import { hashPassword, signToken, setAuthCookie } from '@/lib/auth';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { email, password } = await req.json();
|
||||
|
||||
if (!email || typeof email !== 'string' || !email.includes('@')) {
|
||||
return NextResponse.json({ error: 'Valid email is required' }, { status: 400 });
|
||||
}
|
||||
if (!password || typeof password !== 'string' || password.length < 8) {
|
||||
return NextResponse.json({ error: 'Password must be at least 8 characters' }, { status: 400 });
|
||||
}
|
||||
|
||||
const existing = await db.select().from(users).where(eq(users.email, email)).get();
|
||||
if (existing) {
|
||||
return NextResponse.json({ error: 'Email already registered' }, { status: 409 });
|
||||
}
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
const passwordHash = await hashPassword(password);
|
||||
await db.insert(users).values({ id, email, passwordHash, createdAt: new Date().toISOString() });
|
||||
|
||||
const token = await signToken({ userId: id, email });
|
||||
await setAuthCookie(token);
|
||||
|
||||
return NextResponse.json({ user: { id, email } });
|
||||
}
|
||||
Reference in New Issue
Block a user