322 lines
7.9 KiB
TypeScript
322 lines
7.9 KiB
TypeScript
// src/controllers/api/v1/chat-session.ts
|
|
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
|
|
// All Rights Reserved
|
|
|
|
import { Request, Response } from "express";
|
|
|
|
import { DtpController } from "../../../lib/controller.js";
|
|
import ChatSessionService from "../../../services/chat-session.js";
|
|
import { ChatSessionMode } from "@gadget/api";
|
|
|
|
class ChatSessionController extends DtpController {
|
|
get name(): string {
|
|
return "ChatSessionController";
|
|
}
|
|
get slug(): string {
|
|
return "ctrl:api:v1:chat-session";
|
|
}
|
|
get route(): string {
|
|
return "/chat-sessions";
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
this.router.get("/", this.requireUser(), this.listSessions.bind(this));
|
|
this.router.post("/", this.requireUser(), this.createSession.bind(this));
|
|
this.router.get("/:id", this.requireUser(), this.getSession.bind(this));
|
|
this.router.put("/:id", this.requireUser(), this.updateSession.bind(this));
|
|
this.router.delete(
|
|
"/:id",
|
|
this.requireUser(),
|
|
this.deleteSession.bind(this),
|
|
);
|
|
this.router.get(
|
|
"/:id/turns",
|
|
this.requireUser(),
|
|
this.getSessionTurns.bind(this),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/chat-sessions?projectId=:projectId
|
|
* List chat sessions for a project or user.
|
|
*/
|
|
private async listSessions(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const user = req.user;
|
|
if (!user) {
|
|
res.status(401).json({ success: false, message: "Unauthorized" });
|
|
return;
|
|
}
|
|
|
|
const projectId = req.query.projectId as string | undefined;
|
|
|
|
let sessions;
|
|
if (projectId) {
|
|
sessions = await ChatSessionService.getByProject(projectId);
|
|
} else {
|
|
sessions = await ChatSessionService.getByUser(user._id);
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
data: sessions,
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to list chat sessions", { error: err.message });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/chat-sessions
|
|
* Create a new chat session.
|
|
*/
|
|
private async createSession(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const user = req.user;
|
|
if (!user) {
|
|
res.status(401).json({ success: false, message: "Unauthorized" });
|
|
return;
|
|
}
|
|
|
|
const { projectId, providerId, selectedModel, mode, name } = req.body;
|
|
|
|
if (!projectId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "projectId is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!providerId) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "providerId is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!selectedModel) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "selectedModel is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const sessionMode = mode
|
|
? ChatSessionMode[mode as keyof typeof ChatSessionMode]
|
|
: ChatSessionMode.Build;
|
|
|
|
const session = await ChatSessionService.create(
|
|
user._id,
|
|
projectId,
|
|
providerId,
|
|
selectedModel,
|
|
sessionMode,
|
|
name,
|
|
);
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
data: session,
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to create chat session", { error: err.message });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/chat-sessions/:id
|
|
* Get a specific chat session.
|
|
*/
|
|
private async getSession(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const id = Array.isArray(req.params.id)
|
|
? req.params.id[0]
|
|
: req.params.id;
|
|
if (!id) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "Session ID is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const session = await ChatSessionService.getById(id);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: session,
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to get chat session", { error: err.message });
|
|
|
|
if (err.message.includes("not found")) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
} else {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PUT /api/v1/chat-sessions/:id
|
|
* Update a chat session.
|
|
*/
|
|
private async updateSession(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const id = Array.isArray(req.params.id)
|
|
? req.params.id[0]
|
|
: req.params.id;
|
|
if (!id) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "Session ID is required",
|
|
});
|
|
return;
|
|
}
|
|
const updates = req.body;
|
|
|
|
// Validate allowed updates
|
|
const allowedUpdates: Partial<{
|
|
name: string;
|
|
provider: string;
|
|
selectedModel: string;
|
|
mode: ChatSessionMode;
|
|
}> = {};
|
|
|
|
if (updates.name !== undefined) {
|
|
allowedUpdates.name = updates.name;
|
|
}
|
|
if (updates.provider !== undefined) {
|
|
allowedUpdates.provider = updates.provider;
|
|
}
|
|
if (updates.selectedModel !== undefined) {
|
|
allowedUpdates.selectedModel = updates.selectedModel;
|
|
}
|
|
if (updates.mode !== undefined) {
|
|
allowedUpdates.mode =
|
|
ChatSessionMode[updates.mode as keyof typeof ChatSessionMode];
|
|
}
|
|
|
|
const session = await ChatSessionService.update(id, allowedUpdates);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: session,
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to update chat session", { error: err.message });
|
|
|
|
if (err.message.includes("not found")) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
} else {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/v1/chat-sessions/:id
|
|
* Delete a chat session.
|
|
*/
|
|
private async deleteSession(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const id = Array.isArray(req.params.id)
|
|
? req.params.id[0]
|
|
: req.params.id;
|
|
if (!id) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "Session ID is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await ChatSessionService.delete(id);
|
|
|
|
res.json({
|
|
success: true,
|
|
message: "Chat session deleted",
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to delete chat session", { error: err.message });
|
|
|
|
if (err.message.includes("not found")) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
} else {
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/chat-sessions/:id/turns
|
|
* Get all turns for a chat session.
|
|
*/
|
|
private async getSessionTurns(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const id = Array.isArray(req.params.id)
|
|
? req.params.id[0]
|
|
: req.params.id;
|
|
if (!id) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "Session ID is required",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const turns = await ChatSessionService.getTurns(id);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: turns,
|
|
});
|
|
} catch (error) {
|
|
const err = error as Error;
|
|
this.log.error("failed to get chat session turns", {
|
|
error: err.message,
|
|
});
|
|
res.status(500).json({
|
|
success: false,
|
|
message: err.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
export default ChatSessionController;
|