486CHD commited on
Commit
3abb4df
·
verified ·
1 Parent(s): c044cd3

Upload 13 files (#12)

Browse files

- Upload 13 files (ca29530d9fe01f4b3bd43d8e66bdf1b7398c2e83)

Files changed (7) hide show
  1. README.md +151 -10
  2. data/public-gallery.json +23 -1
  3. index.html +19 -3
  4. mock-api.js +93 -0
  5. server.js +41 -3
  6. start.sh +50 -0
  7. test-system.sh +42 -0
README.md CHANGED
@@ -1,10 +1,151 @@
1
- ---
2
- title: Banber
3
- emoji: 📈
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Banana Pro AI - 创意画板
2
+
3
+ 一个基于 Node.js + Express 的 AI 图片生成平台,支持多图上传、社区画廊分享等功能。
4
+
5
+ ## 🚀 快速开始
6
+
7
+ ### 方法1: 使用启动脚本(推荐)
8
+
9
+ ```bash
10
+ ./start.sh
11
+ ```
12
+
13
+ 这会同时启动主应用和模拟API服务器。
14
+
15
+ ### 方法2: 手动启动
16
+
17
+ 1. 启动模拟API服务器:
18
+ ```bash
19
+ node mock-api.js
20
+ ```
21
+
22
+ 2. 在新终端启动主应用:
23
+ ```bash
24
+ OPENAI_API_URL=http://127.0.0.1:8000/v1/chat/completions node server.js
25
+ ```
26
+
27
+ 3. 访问 http://localhost:3000
28
+
29
+ ## 🔐 登录信息
30
+
31
+ - **密码**: `123456`
32
+
33
+ ## 🎨 功能特性
34
+
35
+ - ✅ **AI图片生成**: 支持文本提示词生成图片
36
+ - ✅ **多图上传**: 可上传最多16张参考图片
37
+ - ✅ **拖拽上传**: 支持拖拽文件到输入区域
38
+ - ✅ **个人画廊**: 本地存储历史作品
39
+ - ✅ **社区画廊**: 分享作品到公共画廊
40
+ - ✅ **响应式设计**: 支持移动端和桌面端
41
+ - ✅ **图片下载**: 支持下载生成的图片
42
+ - ✅ **参数复用**: 可复用历史作品的参数和图片
43
+
44
+ ## 📁 项目结构
45
+
46
+ ```
47
+ banana-pro-ai/
48
+ ├── index.html # 前端主页面
49
+ ├── style.css # 样式文件
50
+ ├── server.js # 后端服务器
51
+ ├── mock-api.js # 模拟API服务器
52
+ ├── start.sh # 启动脚本
53
+ ├── .env # 环境配置
54
+ ├── data/ # 数据目录
55
+ │ └── public-gallery.json # 公共画廊数据
56
+ └── package.json # 项目配置
57
+ ```
58
+
59
+ ## ⚙️ 配置说明
60
+
61
+ ### 环境变量 (.env)
62
+
63
+ ```env
64
+ # API Configuration
65
+ OPENAI_API_KEY=sk-123456
66
+ OPENAI_API_URL=http://127.0.0.1:8000/v1/chat/completions
67
+ MODEL_NAME=banana-pro
68
+
69
+ # Site Configuration
70
+ SITE_PASSWORD=123456
71
+ PORT=3000
72
+
73
+ # Gallery Configuration
74
+ PUBLIC_GALLERY_LIMIT=80
75
+ ```
76
+
77
+ ## 🛠️ 开发说明
78
+
79
+ ### 模拟API
80
+
81
+ 项目包含一个模拟API服务器 (`mock-api.js`),用于演示和测试:
82
+
83
+ - 生成彩色SVG图片作为响应
84
+ - 模拟2-5秒的处理延迟
85
+ - 兼容OpenAI API格式
86
+
87
+ ### 真实API配置
88
+
89
+ 要使用真实的图片生成API,请修改 `.env` 文件:
90
+
91
+ ```env
92
+ OPENAI_API_KEY=your_actual_api_key
93
+ OPENAI_API_URL=https://your-api-provider.com/v1/chat/completions
94
+ ```
95
+
96
+ ### API响应格式
97
+
98
+ API应返回OpenAI兼容格式:
99
+
100
+ ```json
101
+ {
102
+ "choices": [{
103
+ "message": {
104
+ "content": "![Generated Image](data:image/png;base64,...)"
105
+ }
106
+ }]
107
+ }
108
+ ```
109
+
110
+ ## 🐛 故障排除
111
+
112
+ ### 常见问题
113
+
114
+ 1. **生图失败: 无法从 API 响应中提取图片数据**
115
+ - 检查API地址配置是否正确
116
+ - 确认API密钥有效
117
+ - 查看服务器日志了解详细错误
118
+
119
+ 2. **社区创意画廊加载很慢**
120
+ - 检查网络连接
121
+ - 查看浏览器控制台错误信息
122
+ - 确认服务器正常运行
123
+
124
+ 3. **端口占用**
125
+ ```bash
126
+ # 查看端口占用
127
+ lsof -i :3000
128
+ lsof -i :8000
129
+
130
+ # 停止相关进程
131
+ pkill -f "node server.js"
132
+ pkill -f "node mock-api.js"
133
+ ```
134
+
135
+ ## 📱 移动端优化
136
+
137
+ - 单列布局,便于触摸操作
138
+ - 减少动画复杂度,提升性能
139
+ - 图片自动压缩,减少网络负载
140
+ - 响应式设计,适配各种屏幕
141
+
142
+ ## 🔄 版本历史
143
+
144
+ - v1.0.0: 初始版本,支持基础生图功能
145
+ - v1.1.0: 添加多图上传、社区画廊
146
+ - v1.2.0: 移动端优化、性能改进
147
+ - v1.3.0: 错误处理改进、模拟API
148
+
149
+ ## 📄 许可证
150
+
151
+ MIT License
data/public-gallery.json CHANGED
@@ -1 +1,23 @@
1
- []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "demo-1",
4
+ "prompt": "一只可爱的橙色小猫坐在阳光下的窗台上,背景是模糊的花园景色",
5
+ "image": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300'%3E%3Crect fill='%23FFE4B5' width='400' height='300'/%3E%3Ccircle cx='200' cy='150' r='30' fill='%23FF8C00'/%3E%3Ccircle cx='190' cy='140' r='3' fill='%23000'/%3E%3Ccircle cx='210' cy='140' r='3' fill='%23000'/%3E%3Cpath d='M 190 160 Q 200 170 210 160' stroke='%23000' fill='none'/%3E%3Ctext x='200' y='250' text-anchor='middle' fill='%23666' font-family='Arial' font-size='14'%3E示例图片 1%3C/text%3E%3C/svg%3E",
6
+ "inputImages": [],
7
+ "timestamp": "2024-12-01T10:00:00.000Z"
8
+ },
9
+ {
10
+ "id": "demo-2",
11
+ "prompt": "未来科技城市的夜景,霓虹灯闪烁,飞行汽车在空中穿梭",
12
+ "image": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300'%3E%3Crect fill='%23001a33' width='400' height='300'/%3E%3Crect x='50' y='100' width='30' height='150' fill='%23444'/%3E%3Crect x='120' y='50' width='40' height='200' fill='%23555'/%3E%3Crect x='200' y='80' width='35' height='170' fill='%23444'/%3E%3Crect x='280' y='60' width='45' height='190' fill='%23555'/%3E%3Ccircle cx='100' cy='50' r='3' fill='%23ff0'/%3E%3Ccircle cx='150' cy='30' r='2' fill='%23f0f'/%3E%3Ccircle cx='250' cy='40' r='3' fill='%230ff'/%3E%3Ccircle cx='320' cy='25' r='2' fill='%23ff0'/%3E%3Ctext x='200' y='250' text-anchor='middle' fill='%23666' font-family='Arial' font-size='14'%3E示例图片 2%3C/text%3E%3C/svg%3E",
13
+ "inputImages": [],
14
+ "timestamp": "2024-12-01T14:30:00.000Z"
15
+ },
16
+ {
17
+ "id": "demo-3",
18
+ "prompt": "中国传统山水画风格的山峦,云雾缭绕,古松挺立",
19
+ "image": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300'%3E%3Crect fill='%23f5f5dc' width='400' height='300'/%3E%3Cpath d='M 0 200 Q 100 150 200 180 T 400 160 L 400 300 L 0 300 Z' fill='%238B7355'/%3E%3Cpath d='M 50 100 Q 80 80 120 100' stroke='%23654321' fill='none' stroke-width='3'/%3E%3Ccircle cx='100' cy='70' r='20' fill='%23fff' opacity='0.7'/%3E%3Ccircle cx='300' cy='50' r='15' fill='%23fff' opacity='0.6'/%3E%3Ctext x='200' y='250' text-anchor='middle' fill='%23666' font-family='Arial' font-size='14'%3E示例图片 3%3C/text%3E%3C/svg%3E",
20
+ "inputImages": [],
21
+ "timestamp": "2024-12-01T18:45:00.000Z"
22
+ }
23
+ ]
index.html CHANGED
@@ -739,13 +739,17 @@
739
  this.setHint(this.defaultHint);
740
  } catch (error) {
741
  console.error('公共画廊加载失败:', error);
 
 
742
  if (error.name === 'AbortError') {
 
743
  this.setHint('加载超时,请检查网络', 'error');
744
  } else {
745
- this.setHint('加载失败,请稍后重试', 'error');
746
  }
 
747
  if (AppState.publicGalleryData.length === 0) {
748
- this.showEmpty('无法加载公共画廊,请稍后重试');
749
  }
750
  } finally {
751
  this.setLoading(false);
@@ -1038,12 +1042,24 @@
1038
 
1039
  } catch (err) {
1040
  console.error('生成失败:', err);
 
 
 
 
 
 
 
 
 
 
 
 
1041
  if (err.name === 'AbortError') {
1042
  this.statusBar.textContent = '⚠️ 生成超时';
1043
  alert('生成超时,请检查网络后重试');
1044
  } else {
1045
  this.statusBar.textContent = '❌ 生成失败';
1046
- alert('生成失败: ' + err.message);
1047
  }
1048
  } finally {
1049
  this.setLoading(false);
 
739
  this.setHint(this.defaultHint);
740
  } catch (error) {
741
  console.error('公共画廊加载失败:', error);
742
+
743
+ let errorMessage = '加载失败,请稍后重试';
744
  if (error.name === 'AbortError') {
745
+ errorMessage = '加载超时,请检查网络';
746
  this.setHint('加载超时,请检查网络', 'error');
747
  } else {
748
+ this.setHint(errorMessage, 'error');
749
  }
750
+
751
  if (AppState.publicGalleryData.length === 0) {
752
+ this.showEmpty(errorMessage);
753
  }
754
  } finally {
755
  this.setLoading(false);
 
1042
 
1043
  } catch (err) {
1044
  console.error('生成失败:', err);
1045
+
1046
+ let errorMessage = '生成失败,请重试';
1047
+ if (err.message.includes('API认证失败')) {
1048
+ errorMessage = 'API认证失败,请联系管理员检查API配置';
1049
+ } else if (err.message.includes('无法连接到API服务器')) {
1050
+ errorMessage = '无法连接到API服务器,请检查网络连接';
1051
+ } else if (err.message.includes('API请求超时')) {
1052
+ errorMessage = 'API请求超时,请稍后重试';
1053
+ } else if (err.message) {
1054
+ errorMessage = err.message;
1055
+ }
1056
+
1057
  if (err.name === 'AbortError') {
1058
  this.statusBar.textContent = '⚠️ 生成超时';
1059
  alert('生成超时,请检查网络后重试');
1060
  } else {
1061
  this.statusBar.textContent = '❌ 生成失败';
1062
+ alert(errorMessage);
1063
  }
1064
  } finally {
1065
  this.setLoading(false);
mock-api.js ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * mock-api.js
3
+ * 模拟图片生成API服务器,用于测试和演示
4
+ */
5
+
6
+ import express from 'express';
7
+ import { randomBytes } from 'crypto';
8
+
9
+ const app = express();
10
+ const port = 8000;
11
+
12
+ app.use(express.json());
13
+
14
+ // 生成简单的SVG图片作为响应
15
+ function generateMockImage(prompt) {
16
+ const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD'];
17
+ const color = colors[Math.floor(Math.random() * colors.length)];
18
+ const seed = randomBytes(4).toString('hex');
19
+
20
+ const svg = `
21
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
22
+ <defs>
23
+ <linearGradient id="grad${seed}" x1="0%" y1="0%" x2="100%" y2="100%">
24
+ <stop offset="0%" style="stop-color:${color};stop-opacity:1" />
25
+ <stop offset="100%" style="stop-color:#FFF;stop-opacity:1" />
26
+ </linearGradient>
27
+ </defs>
28
+ <rect width="512" height="512" fill="url(#grad${seed})"/>
29
+ <circle cx="256" cy="256" r="100" fill="${color}" opacity="0.8"/>
30
+ <text x="256" y="280" text-anchor="middle" fill="white" font-family="Arial" font-size="16" font-weight="bold">
31
+ AI Generated
32
+ </text>
33
+ <text x="256" y="300" text-anchor="middle" fill="white" font-family="Arial" font-size="12">
34
+ ${prompt.substring(0, 20)}${prompt.length > 20 ? '...' : ''}
35
+ </text>
36
+ </svg>`;
37
+
38
+ return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
39
+ }
40
+
41
+ // OpenAI兼容的聊天完成端点
42
+ app.post('/v1/chat/completions', (req, res) => {
43
+ const { messages, model } = req.body;
44
+
45
+ console.log(`[Mock API] 收到生成请求:`, {
46
+ model,
47
+ messageCount: messages?.length,
48
+ lastMessage: messages?.[messages.length - 1]?.content?.substring(0, 100) + '...'
49
+ });
50
+
51
+ const userPrompt = messages?.[messages.length - 1]?.content || '';
52
+
53
+ // 模拟处理延迟
54
+ setTimeout(() => {
55
+ const mockImage = generateMockImage(userPrompt);
56
+
57
+ const response = {
58
+ id: `chatcmpl-${randomBytes(8).toString('hex')}`,
59
+ object: 'chat.completion',
60
+ created: Math.floor(Date.now() / 1000),
61
+ model: model || 'banana-pro',
62
+ choices: [{
63
+ index: 0,
64
+ message: {
65
+ role: 'assistant',
66
+ content: `![Generated Image](${mockImage})`
67
+ },
68
+ finish_reason: 'stop'
69
+ }],
70
+ usage: {
71
+ prompt_tokens: 50,
72
+ completion_tokens: 10,
73
+ total_tokens: 60
74
+ }
75
+ };
76
+
77
+ res.json(response);
78
+ }, 2000 + Math.random() * 3000); // 2-5秒随机延迟
79
+ });
80
+
81
+ // 健康检查
82
+ app.get('/health', (req, res) => {
83
+ res.json({ status: 'ok', message: 'Mock API is running' });
84
+ });
85
+
86
+ app.listen(port, () => {
87
+ console.log('==========================================');
88
+ console.log('🤖 Mock API 服务器已启动');
89
+ console.log('==========================================');
90
+ console.log(`📡 服务地址: http://localhost:${port}`);
91
+ console.log('🎨 模拟图片生成功能');
92
+ console.log('==========================================');
93
+ });
server.js CHANGED
@@ -35,6 +35,10 @@ const CONFIG = {
35
  maxPublicGalleryItems: parsePositiveInt(process.env.PUBLIC_GALLERY_LIMIT, 80)
36
  };
37
 
 
 
 
 
38
  const DATA_DIR = path.join(__dirname, 'data');
39
  const PUBLIC_GALLERY_FILE = path.join(DATA_DIR, 'public-gallery.json');
40
 
@@ -199,29 +203,45 @@ const APIService = {
199
  * 从响应中提取图片
200
  */
201
  extractImageFromResponse(data) {
 
 
 
 
 
 
 
 
202
  if (data.choices && data.choices[0] && data.choices[0].message) {
203
  const content = data.choices[0].message.content;
204
 
205
  console.log(`[${new Date().toISOString()}] 收到响应内容长度: ${content?.length || 0}`);
 
206
 
207
  const imageData = ImageParser.extractBase64FromMarkdown(content);
208
 
209
  if (imageData && ImageParser.isValidBase64Image(imageData)) {
210
- console.log(`[${new Date().toISOString()}] 成功提取图片数据`);
211
  return imageData;
 
 
212
  }
213
  }
214
 
 
215
  if (data.data && data.data[0]) {
216
  if (data.data[0].b64_json) {
 
217
  return `data:image/png;base64,${data.data[0].b64_json}`;
218
  }
219
  if (data.data[0].url) {
 
220
  return data.data[0].url;
221
  }
222
  }
223
 
224
- throw new Error('无法从 API 响应中提取图片数据');
 
 
225
  }
226
  };
227
 
@@ -386,6 +406,8 @@ app.post('/api/generate', authMiddleware, async (req, res) => {
386
 
387
  try {
388
  console.log(`[${new Date().toISOString()}] 开始生成图片...`);
 
 
389
 
390
  const apiResponse = await APIService.generateImage(trimmedPrompt, uploadedImages);
391
  const imageData = APIService.extractImageFromResponse(apiResponse);
@@ -402,10 +424,21 @@ app.post('/api/generate', authMiddleware, async (req, res) => {
402
 
403
  } catch (error) {
404
  console.error(`[${new Date().toISOString()}] 生成失败:`, error.message);
 
 
 
 
 
 
 
 
 
 
 
405
 
406
  res.status(500).json({
407
  success: false,
408
- message: error.message || '图片生成失败,请稍后重试'
409
  });
410
  }
411
  });
@@ -413,14 +446,19 @@ app.post('/api/generate', authMiddleware, async (req, res) => {
413
  // 公共画廊 - 获取列表
414
  app.get('/api/public-gallery', async (req, res) => {
415
  try {
 
416
  const items = await PublicGalleryStore.getAll();
417
  const sanitized = items.map(({ deleteToken, ...rest }) => rest);
 
 
 
418
  res.json({
419
  success: true,
420
  items: sanitized
421
  });
422
  } catch (error) {
423
  console.error(`[${new Date().toISOString()}] 加载公��画廊失败:`, error);
 
424
  res.status(500).json({
425
  success: false,
426
  message: '无法加载公共画廊,请稍后重试'
 
35
  maxPublicGalleryItems: parsePositiveInt(process.env.PUBLIC_GALLERY_LIMIT, 80)
36
  };
37
 
38
+ // 调试输出环境变量
39
+ console.log(`[DEBUG] 环境变量 OPENAI_API_URL:`, process.env.OPENAI_API_URL);
40
+ console.log(`[DEBUG] 最终 CONFIG.apiUrl:`, CONFIG.apiUrl);
41
+
42
  const DATA_DIR = path.join(__dirname, 'data');
43
  const PUBLIC_GALLERY_FILE = path.join(DATA_DIR, 'public-gallery.json');
44
 
 
203
  * 从响应中提取图片
204
  */
205
  extractImageFromResponse(data) {
206
+ console.log(`[${new Date().toISOString()}] API响应类型:`, typeof data);
207
+ console.log(`[${new Date().toISOString()}] API响应结构:`, Object.keys(data || {}));
208
+
209
+ // 检查错误响应
210
+ if (data.error) {
211
+ throw new Error(`API返回错误: ${data.error}`);
212
+ }
213
+
214
  if (data.choices && data.choices[0] && data.choices[0].message) {
215
  const content = data.choices[0].message.content;
216
 
217
  console.log(`[${new Date().toISOString()}] 收到响应内容长度: ${content?.length || 0}`);
218
+ console.log(`[${new Date().toISOString()}] 响应内容预览:`, content?.substring(0, 200) + (content?.length > 200 ? '...' : ''));
219
 
220
  const imageData = ImageParser.extractBase64FromMarkdown(content);
221
 
222
  if (imageData && ImageParser.isValidBase64Image(imageData)) {
223
+ console.log(`[${new Date().toISOString()}] 成功提取图片数据,长度: ${imageData.length}`);
224
  return imageData;
225
+ } else {
226
+ console.log(`[${new Date().toISOString()}] 未能从内容中提取有效图片数据`);
227
  }
228
  }
229
 
230
+ // 检查 DALL-E 格式
231
  if (data.data && data.data[0]) {
232
  if (data.data[0].b64_json) {
233
+ console.log(`[${new Date().toISOString()}] 使用 DALL-E b64_json 格式`);
234
  return `data:image/png;base64,${data.data[0].b64_json}`;
235
  }
236
  if (data.data[0].url) {
237
+ console.log(`[${new Date().toISOString()}] 使用 DALL-E URL 格式`);
238
  return data.data[0].url;
239
  }
240
  }
241
 
242
+ // 如果到这里还没有找到图片,输出完整的响应用于调试
243
+ console.log(`[${new Date().toISOString()}] 完整API响应:`, JSON.stringify(data, null, 2));
244
+ throw new Error('无法从 API 响应中提取图片数据。请检查API配置和模型响应格式。');
245
  }
246
  };
247
 
 
406
 
407
  try {
408
  console.log(`[${new Date().toISOString()}] 开始生成图片...`);
409
+ console.log(`[${new Date().toISOString()}] API地址: ${CONFIG.apiUrl}`);
410
+ console.log(`[${new Date().toISOString()}] API密钥: ${CONFIG.apiKey.substring(0, 10)}...`);
411
 
412
  const apiResponse = await APIService.generateImage(trimmedPrompt, uploadedImages);
413
  const imageData = APIService.extractImageFromResponse(apiResponse);
 
424
 
425
  } catch (error) {
426
  console.error(`[${new Date().toISOString()}] 生成失败:`, error.message);
427
+ console.error(`[${new Date().toISOString()}] 错误堆栈:`, error.stack);
428
+
429
+ // 根据错误类型提供更具体的错误信息
430
+ let errorMessage = error.message;
431
+ if (error.message.includes('API返回错误: 未授权') || error.message.includes('401') || error.message.includes('Unauthorized')) {
432
+ errorMessage = 'API认证失败,请检查API密钥配置';
433
+ } else if (error.message.includes('ECONNREFUSED') || error.message.includes('ENOTFOUND')) {
434
+ errorMessage = '无法连接到API服务器,请检查网络连接和API地址配置';
435
+ } else if (error.message.includes('timeout')) {
436
+ errorMessage = 'API请求超时,请稍后重试';
437
+ }
438
 
439
  res.status(500).json({
440
  success: false,
441
+ message: errorMessage || '图片生成失败,请稍后重试'
442
  });
443
  }
444
  });
 
446
  // 公共画廊 - 获取列表
447
  app.get('/api/public-gallery', async (req, res) => {
448
  try {
449
+ console.log(`[${new Date().toISOString()}] 开始加载公共画廊...`);
450
  const items = await PublicGalleryStore.getAll();
451
  const sanitized = items.map(({ deleteToken, ...rest }) => rest);
452
+
453
+ console.log(`[${new Date().toISOString()}] 公共画廊加载完成,项目数: ${sanitized.length}`);
454
+
455
  res.json({
456
  success: true,
457
  items: sanitized
458
  });
459
  } catch (error) {
460
  console.error(`[${new Date().toISOString()}] 加载公��画廊失败:`, error);
461
+ console.error(`[${new Date().toISOString()}] 错误详情:`, error.stack);
462
  res.status(500).json({
463
  success: false,
464
  message: '无法加载公共画廊,请稍后重试'
start.sh ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Banana Pro AI 启动脚本
4
+ # 同时启动主应用和模拟API服务器
5
+
6
+ echo "🍌 启动 Banana Pro AI 应用..."
7
+
8
+ # 检查是否已有进程在运行
9
+ if pgrep -f "node server.js" > /dev/null; then
10
+ echo "⚠️ 检测到主应用已在运行,先停止..."
11
+ pkill -f "node server.js"
12
+ sleep 2
13
+ fi
14
+
15
+ if pgrep -f "node mock-api.js" > /dev/null; then
16
+ echo "⚠️ 检测到模拟API已在运行,先停止..."
17
+ pkill -f "node mock-api.js"
18
+ sleep 2
19
+ fi
20
+
21
+ # 启动模拟API服务器
22
+ echo "🤖 启动模拟API服务器 (端口8000)..."
23
+ OPENAI_API_URL=http://127.0.0.1:8000/v1/chat/completions node mock-api.js &
24
+ API_PID=$!
25
+
26
+ # 等待API服务器启动
27
+ sleep 3
28
+
29
+ # 启动主应用
30
+ echo "🍌 启动主应用服务器 (端口3000)..."
31
+ OPENAI_API_URL=http://127.0.0.1:8000/v1/chat/completions node server.js &
32
+ MAIN_PID=$!
33
+
34
+ echo ""
35
+ echo "=========================================="
36
+ echo "✅ Banana Pro AI 已成功启动!"
37
+ echo "=========================================="
38
+ echo "🎨 Web应用地址: http://localhost:3000"
39
+ echo "🤖 模拟API地址: http://localhost:8000"
40
+ echo "🔑 登录密码: 123456"
41
+ echo ""
42
+ echo "💡 提示:"
43
+ echo " - 模拟API会生成彩色SVG图片作为演示"
44
+ echo " - 社区画廊已包含示例作品"
45
+ echo " - 按 Ctrl+C 停止所有服务"
46
+ echo "=========================================="
47
+
48
+ # 等待用户中断
49
+ trap "echo '🛑 正在停止服务...'; kill $API_PID $MAIN_PID 2>/dev/null; exit" INT
50
+ wait
test-system.sh ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "🍌 测试 Banana Pro AI 系统..."
4
+
5
+ # 检查端口
6
+ echo "📡 检查端口状态..."
7
+ if ! lsof -i :3000 > /dev/null 2>&1; then
8
+ echo "❌ 端口3000未被占用"
9
+ else
10
+ echo "✅ 端口3000已被占用"
11
+ fi
12
+
13
+ if ! lsof -i :8000 > /dev/null 2>&1; then
14
+ echo "❌ 端口8000未被占用"
15
+ else
16
+ echo "✅ 端口8000已被占用"
17
+ fi
18
+
19
+ # 测试API
20
+ echo ""
21
+ echo "🔍 测试API连接..."
22
+
23
+ echo "测试主应用健康检查..."
24
+ if curl -s http://localhost:3000/api/health > /dev/null 2>&1; then
25
+ echo "✅ 主应用API正常"
26
+ else
27
+ echo "❌ 主应用API无法访问"
28
+ fi
29
+
30
+ echo "测试模拟API健康检查..."
31
+ if curl -s http://localhost:8000/health > /dev/null 2>&1; then
32
+ echo "✅ 模拟API正常"
33
+ else
34
+ echo "❌ 模拟API无法访问"
35
+ fi
36
+
37
+ echo ""
38
+ echo "📊 当前运行的Node进程:"
39
+ ps aux | grep -E "(node.*server|node.*mock)" | grep -v grep || echo "无相关进程"
40
+
41
+ echo ""
42
+ echo "🌐 测试完成!"