From ab0154bcf99184aa7a18b1db1901da65e12b1ef9 Mon Sep 17 00:00:00 2001 From: xiaohei Date: Sun, 12 Apr 2026 02:08:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=89=8D=E5=90=8E=E7=AB=AF+=E9=A3=9E?= =?UTF-8?q?=E4=B9=A6=E5=85=A8=E9=93=BE=E8=B7=AF=E8=81=94=E8=B0=83=E6=88=90?= =?UTF-8?q?=E5=8A=9F=20-=20=E5=88=9B=E5=BB=BA=E9=A1=B9=E7=9B=AE=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8F=91=E9=80=81=E9=A3=9E=E4=B9=A6=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 13 ++++++++ package.json | 1 + src/pages/WizardPage.tsx | 25 +++++++++++--- src/server/dev.ts | 72 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 src/server/dev.ts diff --git a/package-lock.json b/package-lock.json index 084e592..875acb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.5.0", "dependencies": { "@arco-design/web-react": "^2.66.0", + "@hono/node-server": "^1.19.13", "@larksuiteoapi/node-sdk": "^1.60.0", "hono": "^4.7.0", "react": "^18.3.0", @@ -1020,6 +1021,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@hono/node-server": { + "version": "1.19.13", + "resolved": "https://registry.npmmirror.com/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", diff --git a/package.json b/package.json index 6f665ab..d6bbc69 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@arco-design/web-react": "^2.66.0", + "@hono/node-server": "^1.19.13", "@larksuiteoapi/node-sdk": "^1.60.0", "hono": "^4.7.0", "react": "^18.3.0", diff --git a/src/pages/WizardPage.tsx b/src/pages/WizardPage.tsx index 5270e10..90b0b56 100644 --- a/src/pages/WizardPage.tsx +++ b/src/pages/WizardPage.tsx @@ -185,13 +185,29 @@ const WizardPage: React.FC = () => { const [project, setProject] = useState(createEmptyProject()); const [submitted, setSubmitted] = useState(false); + const [submitting, setSubmitting] = useState(false); + const next = () => setCurrent(Math.min(current + 1, STEPS.length - 1)); const prev = () => setCurrent(Math.max(current - 1, 0)); - const handleSubmit = () => { - setSubmitted(true); - Message.success(`项目「${project.name}」已创建!`); - console.log('Project created:', project); + const handleSubmit = async () => { + setSubmitting(true); + try { + const res = await fetch('/api/projects', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(project), + }); + const data = await res.json(); + setSubmitted(true); + Message.success(`项目「${project.name}」已创建!飞书通知已发送。`); + console.log('Project created:', data); + } catch (err) { + Message.error('创建失败,请重试'); + console.error('Failed to create project:', err); + } finally { + setSubmitting(false); + } }; const renderStep = () => { @@ -328,6 +344,7 @@ const WizardPage: React.FC = () => { type="primary" onClick={handleSubmit} disabled={!project.name || !project.goal} + loading={submitting} > 创建项目 diff --git a/src/server/dev.ts b/src/server/dev.ts new file mode 100644 index 0000000..1027445 --- /dev/null +++ b/src/server/dev.ts @@ -0,0 +1,72 @@ +// Dev server entry - runs Hono on Node.js +import { Hono } from 'hono'; +import { cors } from 'hono/cors'; +import { logger } from 'hono/logger'; +import { executionApiHandlers } from './execution-api'; +import { handleDecompose, handleFeishuCallback } from './index'; +import { sendFeishuMessage, notifyProjectCreated, notifyMilestoneReminder, notifyRiskAlert, sendDecisionCard } from './feishu'; +import { handleDecisionCallback, createDecision, getPendingDecisions, DECISION_TEMPLATES } from '../lib/decision-cards'; + +const projects: Record = {}; + +const app = new Hono(); + +app.use('*', cors()); +app.use('*', logger()); + +app.get('/api/health', (c) => c.json({ status: 'ok', version: '0.5.0', message: 'FlowPilot API is running' })); + +// Create project + send Feishu notification +app.post('/api/projects', async (c) => { + const body = await c.req.json(); + const projectId = `proj-${Date.now()}`; + const project = { id: projectId, ...body, status: 'active', createdAt: new Date().toISOString() }; + projects[projectId] = project; + + // Send Feishu notification + try { + await notifyProjectCreated(project.name || '未命名项目', project.goal || '', 'ou_41d14aca8278e605d98e33b1221777e4', 'open_id'); + console.log('✅ Feishu notification sent for project:', project.name); + } catch (e: any) { + console.error('❌ Feishu notification failed:', e.message); + } + + return c.json(project); +}); + +app.get('/api/projects/:id', (c) => c.json(projects[c.req.param('id')] || { error: 'not found' })); +app.get('/api/projects/:id/tasks', (c) => c.json({ tasks: [], projectId: c.req.param('id') })); + +app.post('/api/projects/:id/tasks', async (c) => { + const body = await c.req.json(); + return c.json({ id: `task-${Date.now()}`, projectId: c.req.param('id'), ...body, status: 'todo', createdAt: new Date().toISOString() }); +}); + +app.patch('/api/projects/:id/tasks/:taskId', async (c) => { + const body = await c.req.json(); + return c.json({ id: c.req.param('taskId'), ...body, updatedAt: new Date().toISOString() }); +}); + +app.get('/api/projects/:id/executions', (c) => c.json({ executions: executionApiHandlers.getExecutions(c.req.param('id')) })); +app.post('/api/projects/:id/executions', async (c) => { const b = await c.req.json(); return c.json(executionApiHandlers.createExecution(c.req.param('id'), b), 201); }); +app.get('/api/projects/:id/stats', (c) => c.json(executionApiHandlers.getStats(c.req.param('id')))); + +app.post('/api/projects/:id/decompose', async (c) => { + const b = await c.req.json(); + return c.json(handleDecompose(b.task, b.context)); +}); + +app.get('/api/projects/:id/decisions', (c) => c.json({ decisions: executionApiHandlers.getDecisions(c.req.param('id')) })); +app.post('/api/projects/:id/decisions', async (c) => { const b = await c.req.json(); return c.json(executionApiHandlers.createDecision(c.req.param('id'), b), 201); }); + +app.post('/api/feishu/webhook', async (c) => { const e = await c.req.json(); return c.json(handleFeishuCallback(e)); }); +app.post('/api/feishu/decision/callback', async (c) => { const b = await c.req.json(); console.log('Decision callback:', JSON.stringify(b)); return c.json({ ok: true }); }); + +// Start with Node.js native http +const port = Number(process.env.PORT) || 3001; +import { createServer } from 'http'; +import { serve } from '@hono/node-server'; + +serve({ fetch: app.fetch, port }, () => { + console.log(`🚀 FlowPilot API running on http://localhost:${port}`); +});