Files
Alfred/src/server/server.ts
lanyuanxiaoyao 21b557c255 feat(inbox): 素材持久化 CRUD — 数据库表 + API + 前端接入
- 新增 materials 表(id/projectId/description/associatedDate/status/createdAt/updatedAt)
- 新增 4 个后端 API 路由(list/create/get/delete)+ 13 个测试
- 新增 use-materials hooks(TanStack Query)
- 收集箱页面重构为三层架构(InboxPage + MaterialSidebar + MaterialDetailPanel)
- MaterialCard: Popconfirm 删除确认 + 粗粒度时间格式
- MaterialContent: 展示状态标签 + createdAt
- 更新开发文档 backend.md / frontend.md
2026-06-03 14:53:23 +08:00

340 lines
10 KiB
TypeScript

import type Database from "bun:sqlite";
import type { RuntimeMode } from "../shared/api";
import type { Logger } from "./logger";
import type { StaticAssets } from "./static";
import { createApiError, jsonResponse } from "./helpers";
import { withErrorHandler } from "./middleware";
import { handleMeta } from "./routes/meta";
import { serveStaticAsset } from "./static";
import { readAppVersion } from "./version";
export interface StartServerOptions {
config: { host: string; port: number };
db: Database;
logger: Logger;
mode: RuntimeMode;
staticAssets?: StaticAssets;
version?: string;
}
export function startServer(options: StartServerOptions) {
const { config, db, logger, mode, staticAssets, version } = options;
const resolveVersion = (): Promise<string> => {
if (version) return Promise.resolve(version);
return readAppVersion();
};
const server = Bun.serve({
fetch(req) {
if (staticAssets) {
return serveStaticAsset(new URL(req.url).pathname, staticAssets);
}
return new Response("Frontend is served by Vite dev server on :5173", { status: 404 });
},
hostname: config.host,
port: config.port,
routes: {
"/api/*": () => jsonResponse(createApiError("API route not found", 404), { mode, status: 404 }),
"/api/meta": {
GET: withErrorHandler(
async () => {
const resolvedVersion = await resolveVersion();
return handleMeta(mode, resolvedVersion, logger);
},
mode,
logger,
),
},
"/api/models": {
GET: withErrorHandler(
async (req) => {
const { handleListModels } = await import("./routes/models/list");
return handleListModels(req, db, mode, logger);
},
mode,
logger,
),
POST: withErrorHandler(
async (req) => {
const { handleCreateModel } = await import("./routes/models/create");
return handleCreateModel(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/models/:id": {
DELETE: withErrorHandler(
async (req) => {
const { handleDeleteModel } = await import("./routes/models/delete");
return handleDeleteModel(req, db, mode, logger);
},
mode,
logger,
),
GET: withErrorHandler(
async (req) => {
const { handleGetModel } = await import("./routes/models/get");
return handleGetModel(req, db, mode, logger);
},
mode,
logger,
),
PATCH: withErrorHandler(
async (req) => {
const { handleUpdateModel } = await import("./routes/models/update");
return handleUpdateModel(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/models/test": {
POST: withErrorHandler(
async (req) => {
const { handleTestModelConfig } = await import("./routes/models/test");
return handleTestModelConfig(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects": {
GET: withErrorHandler(
async (req) => {
const { handleListProjects } = await import("./routes/projects/list");
return handleListProjects(req, db, mode, logger);
},
mode,
logger,
),
POST: withErrorHandler(
async (req) => {
const { handleCreateProject } = await import("./routes/projects/create");
return handleCreateProject(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id": {
DELETE: withErrorHandler(
async (req) => {
const { handleDeleteProject } = await import("./routes/projects/delete");
return handleDeleteProject(req, db, mode, logger);
},
mode,
logger,
),
GET: withErrorHandler(
async (req) => {
const { handleGetProject } = await import("./routes/projects/get");
return handleGetProject(req, db, mode, logger);
},
mode,
logger,
),
PATCH: withErrorHandler(
async (req) => {
const { handleUpdateProject } = await import("./routes/projects/update");
return handleUpdateProject(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/archive": {
POST: withErrorHandler(
async (req) => {
const { handleArchiveProject } = await import("./routes/projects/archive");
return handleArchiveProject(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/chat": {
POST: withErrorHandler(
async (req) => {
const { handleSendChat } = await import("./routes/chat/send");
return handleSendChat(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/conversations": {
GET: withErrorHandler(
async (req) => {
const { handleListConversations } = await import("./routes/chat/list");
return handleListConversations(req, db, mode, logger);
},
mode,
logger,
),
POST: withErrorHandler(
async (req) => {
const { handleCreateConversation } = await import("./routes/chat/create");
return handleCreateConversation(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/conversations/:cid": {
DELETE: withErrorHandler(
async (req) => {
const { handleDeleteConversation } = await import("./routes/chat/delete");
return handleDeleteConversation(req, db, mode, logger);
},
mode,
logger,
),
GET: withErrorHandler(
async (req) => {
const { handleGetConversation } = await import("./routes/chat/get");
return handleGetConversation(req, db, mode, logger);
},
mode,
logger,
),
PATCH: withErrorHandler(
async (req) => {
const { handleUpdateConversation } = await import("./routes/chat/update");
return handleUpdateConversation(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/conversations/:cid/messages": {
GET: withErrorHandler(
async (req) => {
const { handleListMessages } = await import("./routes/chat/messages");
return handleListMessages(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/materials": {
GET: withErrorHandler(
async (req) => {
const { handleListMaterials } = await import("./routes/materials/list");
return handleListMaterials(req, db, mode, logger);
},
mode,
logger,
),
POST: withErrorHandler(
async (req) => {
const { handleCreateMaterial } = await import("./routes/materials/create");
return handleCreateMaterial(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/materials/:mid": {
DELETE: withErrorHandler(
async (req) => {
const { handleDeleteMaterial } = await import("./routes/materials/delete");
return handleDeleteMaterial(req, db, mode, logger);
},
mode,
logger,
),
GET: withErrorHandler(
async (req) => {
const { handleGetMaterial } = await import("./routes/materials/get");
return handleGetMaterial(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/projects/:id/restore": {
POST: withErrorHandler(
async (req) => {
const { handleRestoreProject } = await import("./routes/projects/restore");
return handleRestoreProject(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/providers": {
GET: withErrorHandler(
async (req) => {
const { handleListProviders } = await import("./routes/providers/list");
return handleListProviders(req, db, mode, logger);
},
mode,
logger,
),
POST: withErrorHandler(
async (req) => {
const { handleCreateProvider } = await import("./routes/providers/create");
return handleCreateProvider(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/providers/:id": {
DELETE: withErrorHandler(
async (req) => {
const { handleDeleteProvider } = await import("./routes/providers/delete");
return handleDeleteProvider(req, db, mode, logger);
},
mode,
logger,
),
GET: withErrorHandler(
async (req) => {
const { handleGetProvider } = await import("./routes/providers/get");
return handleGetProvider(req, db, mode, logger);
},
mode,
logger,
),
PATCH: withErrorHandler(
async (req) => {
const { handleUpdateProvider } = await import("./routes/providers/update");
return handleUpdateProvider(req, db, mode, logger);
},
mode,
logger,
),
},
"/api/providers/options": {
GET: withErrorHandler(
async () => {
const { handleListProviderOptions } = await import("./routes/providers/options");
return handleListProviderOptions(db, mode, logger);
},
mode,
logger,
),
},
"/api/providers/test": {
POST: withErrorHandler(
async (req) => {
const { handleTestProviderConfig } = await import("./routes/providers/test");
return handleTestProviderConfig(req, db, mode, logger);
},
mode,
logger,
),
},
},
});
logger.info({ host: config.host, port: config.port, url: server.url.toString() }, "服务启动");
return server;
}