MVP-1: helpdesk core with dashboard, Telegram channel, customer portal

Next.js 16 + Drizzle/SQLite (matching ~/project-claude conventions), SSE-based
realtime updates, admin dashboard with status columns, ticket thread view,
Telegram long-polling integration (inbound + outbound), token-based customer
portal, Docker Compose packaging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QcXH24ky6zjk2UyK5oZUPH
This commit is contained in:
ogrechkoandClaude Sonnet 5 committed 2026-07-26 12:40:56 +00:00
1 parent 888b7b82ba
commit cf0b6cf58e
59 files changed
+12556

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { z } from "zod";
import { requireSession } from "@/lib/auth/require";
import { configureTelegramBot, disableTelegramBot, getTelegramStatus } from "@/lib/telegram/bot";
export async function GET() {
const { session, response } = await requireSession();
if (!session) return response;
return NextResponse.json(await getTelegramStatus());
}
const configureSchema = z.object({ botToken: z.string().min(1) });
export async function POST(request: Request) {
const { session, response } = await requireSession();
if (!session) return response;
const body = await request.json().catch(() => null);
const parsed = configureSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json({ error: "Invalid input" }, { status: 400 });
}
try {
const { username } = await configureTelegramBot(parsed.data.botToken);
return NextResponse.json({ ok: true, botUsername: username });
} catch {
return NextResponse.json(
{ error: "Telegram rejected this token — check it and try again" },
{ status: 400 },
);
}
}
export async function DELETE() {
const { session, response } = await requireSession();
if (!session) return response;
await disableTelegramBot();
return NextResponse.json({ ok: true });
}