Initial commit
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Bot API Routes
|
||||
*
|
||||
* POST /api/bots - Create a new bot
|
||||
* GET /api/bots - List user's bots
|
||||
*
|
||||
* Requirements: 1.1, 1.3
|
||||
*/
|
||||
|
||||
import { NextResponse } from 'next/server';
|
||||
import { requireAuth } from '@/lib/auth';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
createBot,
|
||||
getBotsByUser,
|
||||
BotLimitExceededError,
|
||||
BotHandleTakenError,
|
||||
BotValidationError,
|
||||
} from '@/lib/bots/botManager';
|
||||
|
||||
// Schema for creating a bot
|
||||
const createBotSchema = z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
handle: z.string().min(3).max(30).regex(/^[a-zA-Z0-9_]+$/, 'Handle must be alphanumeric and underscores only'),
|
||||
bio: z.string().max(500).optional(),
|
||||
avatarUrl: z.string().url().optional(),
|
||||
headerUrl: z.string().url().optional(),
|
||||
personality: z.object({
|
||||
systemPrompt: z.string().min(1).max(10000),
|
||||
temperature: z.number().min(0).max(2),
|
||||
maxTokens: z.number().int().min(1).max(100000),
|
||||
responseStyle: z.string().optional(),
|
||||
}),
|
||||
llmProvider: z.enum(['openrouter', 'openai', 'anthropic']),
|
||||
llmModel: z.string().min(1),
|
||||
llmApiKey: z.string().min(1),
|
||||
schedule: z.object({
|
||||
type: z.enum(['interval', 'times', 'cron']),
|
||||
intervalMinutes: z.number().int().min(5).optional(),
|
||||
times: z.array(z.string().regex(/^([01][0-9]|2[0-3]):[0-5][0-9]$/)).optional(),
|
||||
cronExpression: z.string().optional(),
|
||||
timezone: z.string().optional(),
|
||||
}).optional(),
|
||||
autonomousMode: z.boolean().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/bots - Create a new bot
|
||||
*
|
||||
* Requires authentication.
|
||||
* Creates a new bot linked to the authenticated user's account.
|
||||
*
|
||||
* Validates: Requirements 1.1, 1.2
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const user = await requireAuth();
|
||||
const body = await request.json();
|
||||
const data = createBotSchema.parse(body);
|
||||
|
||||
const bot = await createBot(user.id, {
|
||||
name: data.name,
|
||||
handle: data.handle,
|
||||
bio: data.bio,
|
||||
avatarUrl: data.avatarUrl,
|
||||
headerUrl: data.headerUrl,
|
||||
personality: data.personality,
|
||||
llmProvider: data.llmProvider,
|
||||
llmModel: data.llmModel,
|
||||
llmApiKey: data.llmApiKey,
|
||||
schedule: data.schedule,
|
||||
autonomousMode: data.autonomousMode,
|
||||
});
|
||||
|
||||
// Return bot without sensitive data
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
bot: {
|
||||
id: bot.id,
|
||||
userId: bot.userId,
|
||||
name: bot.name,
|
||||
handle: bot.handle,
|
||||
bio: bot.bio,
|
||||
avatarUrl: bot.avatarUrl,
|
||||
personalityConfig: bot.personalityConfig,
|
||||
llmProvider: bot.llmProvider,
|
||||
llmModel: bot.llmModel,
|
||||
scheduleConfig: bot.scheduleConfig,
|
||||
autonomousMode: bot.autonomousMode,
|
||||
isActive: bot.isActive,
|
||||
isSuspended: bot.isSuspended,
|
||||
lastPostAt: bot.lastPostAt,
|
||||
createdAt: bot.createdAt,
|
||||
updatedAt: bot.updatedAt,
|
||||
},
|
||||
}, { status: 201 });
|
||||
} catch (error) {
|
||||
console.error('Create bot error:', error);
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid input', details: error.issues },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof Error && error.message === 'Authentication required') {
|
||||
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
|
||||
}
|
||||
|
||||
if (error instanceof BotLimitExceededError) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, code: error.code },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof BotHandleTakenError) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, code: error.code },
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof BotValidationError) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, code: error.code },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to create bot' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/bots - List user's bots
|
||||
*
|
||||
* Requires authentication.
|
||||
* Returns all bots belonging to the authenticated user.
|
||||
*
|
||||
* Validates: Requirements 1.3
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const user = await requireAuth();
|
||||
const userBots = await getBotsByUser(user.id);
|
||||
|
||||
// Return bots without sensitive data
|
||||
const sanitizedBots = userBots.map(bot => ({
|
||||
id: bot.id,
|
||||
userId: bot.userId,
|
||||
name: bot.name,
|
||||
handle: bot.handle,
|
||||
bio: bot.bio,
|
||||
avatarUrl: bot.avatarUrl,
|
||||
personalityConfig: bot.personalityConfig,
|
||||
llmProvider: bot.llmProvider,
|
||||
llmModel: bot.llmModel,
|
||||
scheduleConfig: bot.scheduleConfig,
|
||||
autonomousMode: bot.autonomousMode,
|
||||
isActive: bot.isActive,
|
||||
isSuspended: bot.isSuspended,
|
||||
suspensionReason: bot.suspensionReason,
|
||||
lastPostAt: bot.lastPostAt,
|
||||
createdAt: bot.createdAt,
|
||||
updatedAt: bot.updatedAt,
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
bots: sanitizedBots,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('List bots error:', error);
|
||||
|
||||
if (error instanceof Error && error.message === 'Authentication required') {
|
||||
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: 'Failed to list bots' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user