@shun-js/aibaiban-server 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +9 -9
- package/assets/sw.js +2 -2
- package/package.json +3 -2
- package/server/controller/IndexController.js +2 -2
- package/server/controller/LLMController.js +8 -2
- package/server/controller/SEOController.js +8 -8
- package/server/log-options.js +7 -7
- package/server/service/CCService.js +94 -0
- package/server/service/IndexService.js +1 -1
- package/server/service/LLMService.js +309 -135
- package/server/service/SEOService.js +10 -10
- package/server/util/check.js +2 -2
- package/server/util/feishu.js +10 -5
- package/server/util/prompt-agent.js +137 -93
- package/server/util/relay-client.js +70 -0
- package/views/index.html +52 -16
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// path
|
|
2
|
-
const path = require(
|
|
2
|
+
const path = require("path");
|
|
3
3
|
|
|
4
4
|
// mime
|
|
5
|
-
const mime = require(
|
|
5
|
+
const mime = require("mime-types");
|
|
6
6
|
|
|
7
7
|
// qiao
|
|
8
|
-
const { readFile } = require(
|
|
8
|
+
const { readFile } = require("qiao-file");
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* robots
|
|
@@ -13,7 +13,7 @@ const { readFile } = require('qiao-file');
|
|
|
13
13
|
* @param {*} res
|
|
14
14
|
*/
|
|
15
15
|
exports.robots = async (req, res) => {
|
|
16
|
-
const filePath = path.resolve(__dirname,
|
|
16
|
+
const filePath = path.resolve(__dirname, "../../assets/robots.txt");
|
|
17
17
|
const content = await readFile(filePath);
|
|
18
18
|
res.send(content, mime.lookup(filePath));
|
|
19
19
|
};
|
|
@@ -24,7 +24,7 @@ exports.robots = async (req, res) => {
|
|
|
24
24
|
* @param {*} res
|
|
25
25
|
*/
|
|
26
26
|
exports.sitemap = async (req, res) => {
|
|
27
|
-
const filePath = path.resolve(__dirname,
|
|
27
|
+
const filePath = path.resolve(__dirname, "../../assets/sitemap.xml");
|
|
28
28
|
const content = await readFile(filePath);
|
|
29
29
|
res.send(content, mime.lookup(filePath));
|
|
30
30
|
};
|
|
@@ -35,7 +35,7 @@ exports.sitemap = async (req, res) => {
|
|
|
35
35
|
* @param {*} res
|
|
36
36
|
*/
|
|
37
37
|
exports.bingIndexNow = async (req, res) => {
|
|
38
|
-
const filePath = path.resolve(__dirname,
|
|
38
|
+
const filePath = path.resolve(__dirname, "../../assets/bing.txt");
|
|
39
39
|
const content = await readFile(filePath);
|
|
40
40
|
res.send(content, mime.lookup(filePath));
|
|
41
41
|
};
|
|
@@ -46,7 +46,7 @@ exports.bingIndexNow = async (req, res) => {
|
|
|
46
46
|
* @param {*} res
|
|
47
47
|
*/
|
|
48
48
|
exports.manifestJson = async (req, res) => {
|
|
49
|
-
const filePath = path.resolve(__dirname,
|
|
49
|
+
const filePath = path.resolve(__dirname, "../../assets/manifest.json");
|
|
50
50
|
const content = await readFile(filePath);
|
|
51
51
|
res.send(content, mime.lookup(filePath));
|
|
52
52
|
};
|
|
@@ -57,7 +57,7 @@ exports.manifestJson = async (req, res) => {
|
|
|
57
57
|
* @param {*} res
|
|
58
58
|
*/
|
|
59
59
|
exports.swJs = async (req, res) => {
|
|
60
|
-
const filePath = path.resolve(__dirname,
|
|
60
|
+
const filePath = path.resolve(__dirname, "../../assets/sw.js");
|
|
61
61
|
const content = await readFile(filePath);
|
|
62
62
|
res.send(content, mime.lookup(filePath));
|
|
63
63
|
};
|
|
@@ -68,7 +68,7 @@ exports.swJs = async (req, res) => {
|
|
|
68
68
|
* @param {*} res
|
|
69
69
|
*/
|
|
70
70
|
exports.icon192Png = async (req, res) => {
|
|
71
|
-
const filePath = path.resolve(__dirname,
|
|
71
|
+
const filePath = path.resolve(__dirname, "../../assets/icon-192.png");
|
|
72
72
|
const content = await readFile(filePath, { encoding: null });
|
|
73
73
|
res.send(content, mime.lookup(filePath));
|
|
74
74
|
};
|
|
@@ -79,7 +79,7 @@ exports.icon192Png = async (req, res) => {
|
|
|
79
79
|
* @param {*} res
|
|
80
80
|
*/
|
|
81
81
|
exports.icon512Png = async (req, res) => {
|
|
82
|
-
const filePath = path.resolve(__dirname,
|
|
82
|
+
const filePath = path.resolve(__dirname, "../../assets/icon-512.png");
|
|
83
83
|
const content = await readFile(filePath, { encoding: null });
|
|
84
84
|
res.send(content, mime.lookup(filePath));
|
|
85
85
|
};
|
package/server/util/check.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// qiao
|
|
2
|
-
const { userCheck } = require(
|
|
2
|
+
const { userCheck } = require("qiao-z-service");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* checkUserAuth
|
|
@@ -16,7 +16,7 @@ exports.checkUserAuth = async function (req, res) {
|
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
// pass
|
|
19
|
-
if (userCheckRes && userCheckRes.type ===
|
|
19
|
+
if (userCheckRes && userCheckRes.type === "success") return true;
|
|
20
20
|
|
|
21
21
|
// r
|
|
22
22
|
res.json(userCheckRes);
|
package/server/util/feishu.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// services
|
|
2
|
-
const { feishuBot } = require(
|
|
2
|
+
const { feishuBot } = require("@shun-js/shun-service");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* feishuMsg
|
|
6
6
|
* @param {*} msg
|
|
7
7
|
*/
|
|
8
8
|
exports.feishuMsg = (msg) => {
|
|
9
|
-
if (global.QZ_CONFIG.env !==
|
|
9
|
+
if (global.QZ_CONFIG.env !== "production") return;
|
|
10
10
|
|
|
11
11
|
feishuBot({
|
|
12
12
|
url: global.QZ_CONFIG.feishu.url,
|
|
@@ -19,9 +19,14 @@ exports.feishuMsg = (msg) => {
|
|
|
19
19
|
function isBot(req) {
|
|
20
20
|
const ua = req.useragent;
|
|
21
21
|
const hasOSName = ua && ua.os && ua.os.name;
|
|
22
|
-
const isGoogleBot = ua && ua.browser && ua.browser.name ===
|
|
23
|
-
const isMetaBot =
|
|
24
|
-
|
|
22
|
+
const isGoogleBot = ua && ua.browser && ua.browser.name === "Googlebot";
|
|
23
|
+
const isMetaBot =
|
|
24
|
+
ua && ua.browser && ua.browser.name === "meta-externalagent";
|
|
25
|
+
const isSafariBot =
|
|
26
|
+
ua &&
|
|
27
|
+
ua.engine &&
|
|
28
|
+
ua.engine.name === "WebKit" &&
|
|
29
|
+
ua.engine.version === "605.1.15";
|
|
25
30
|
return !hasOSName || isGoogleBot || isMetaBot || isSafariBot;
|
|
26
31
|
}
|
|
27
32
|
|
|
@@ -145,167 +145,211 @@ flowchart TD
|
|
|
145
145
|
*/
|
|
146
146
|
CANVAS_TOOLS: [
|
|
147
147
|
{
|
|
148
|
-
type:
|
|
148
|
+
type: "function",
|
|
149
149
|
function: {
|
|
150
|
-
name:
|
|
151
|
-
description:
|
|
150
|
+
name: "draw_shape",
|
|
151
|
+
description:
|
|
152
|
+
"在白板上绘制基础形状(矩形、椭圆、菱形),可带文字标签。需要被箭头连接时必须指定 id。",
|
|
152
153
|
parameters: {
|
|
153
|
-
type:
|
|
154
|
+
type: "object",
|
|
154
155
|
properties: {
|
|
155
|
-
id: { type:
|
|
156
|
-
shape: {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
156
|
+
id: { type: "string", description: "元素ID,箭头绑定时需要引用" },
|
|
157
|
+
shape: {
|
|
158
|
+
type: "string",
|
|
159
|
+
enum: ["rectangle", "ellipse", "diamond"],
|
|
160
|
+
},
|
|
161
|
+
x: { type: "number", description: "左上角X坐标" },
|
|
162
|
+
y: { type: "number", description: "左上角Y坐标" },
|
|
163
|
+
width: { type: "number", description: "宽度,默认100" },
|
|
164
|
+
height: { type: "number", description: "高度,默认100" },
|
|
165
|
+
label: { type: "string", description: "形状内的文字标签" },
|
|
166
|
+
strokeColor: {
|
|
167
|
+
type: "string",
|
|
168
|
+
description: "边框颜色,默认#1e1e1e",
|
|
169
|
+
},
|
|
170
|
+
backgroundColor: {
|
|
171
|
+
type: "string",
|
|
172
|
+
description: "填充颜色,默认transparent",
|
|
173
|
+
},
|
|
174
|
+
fillStyle: {
|
|
175
|
+
type: "string",
|
|
176
|
+
enum: ["hachure", "cross-hatch", "solid", "zigzag"],
|
|
177
|
+
},
|
|
178
|
+
strokeWidth: {
|
|
179
|
+
type: "number",
|
|
180
|
+
enum: [1, 2, 4],
|
|
181
|
+
description: "1=细,2=中,4=粗",
|
|
182
|
+
},
|
|
183
|
+
strokeStyle: {
|
|
184
|
+
type: "string",
|
|
185
|
+
enum: ["solid", "dashed", "dotted"],
|
|
186
|
+
},
|
|
187
|
+
roughness: {
|
|
188
|
+
type: "number",
|
|
189
|
+
enum: [0, 1, 2],
|
|
190
|
+
description: "0=精确,1=手绘,2=夸张",
|
|
191
|
+
},
|
|
192
|
+
roundness: { type: "boolean", description: "是否圆角,默认true" },
|
|
193
|
+
opacity: { type: "number", description: "不透明度0-100,默认100" },
|
|
170
194
|
},
|
|
171
|
-
required: [
|
|
195
|
+
required: ["shape", "x", "y"],
|
|
172
196
|
},
|
|
173
197
|
},
|
|
174
198
|
},
|
|
175
199
|
{
|
|
176
|
-
type:
|
|
200
|
+
type: "function",
|
|
177
201
|
function: {
|
|
178
|
-
name:
|
|
179
|
-
description:
|
|
202
|
+
name: "draw_arrow",
|
|
203
|
+
description:
|
|
204
|
+
"绘制箭头。可通过 startId/endId 绑定到已有形状(箭头自动吸附),也可直接指定坐标点。",
|
|
180
205
|
parameters: {
|
|
181
|
-
type:
|
|
206
|
+
type: "object",
|
|
182
207
|
properties: {
|
|
183
|
-
startX: { type:
|
|
184
|
-
startY: { type:
|
|
185
|
-
endX: { type:
|
|
186
|
-
endY: { type:
|
|
187
|
-
startId: { type:
|
|
188
|
-
endId: { type:
|
|
189
|
-
label: { type:
|
|
208
|
+
startX: { type: "number", description: "起点X(无startId时必填)" },
|
|
209
|
+
startY: { type: "number", description: "起点Y(无startId时必填)" },
|
|
210
|
+
endX: { type: "number", description: "终点X(无endId时必填)" },
|
|
211
|
+
endY: { type: "number", description: "终点Y(无endId时必填)" },
|
|
212
|
+
startId: { type: "string", description: "起点绑定的形状ID" },
|
|
213
|
+
endId: { type: "string", description: "终点绑定的形状ID" },
|
|
214
|
+
label: { type: "string", description: "箭头上的文字标签" },
|
|
190
215
|
startArrowhead: {
|
|
191
|
-
type:
|
|
192
|
-
enum: [
|
|
193
|
-
description:
|
|
216
|
+
type: "string",
|
|
217
|
+
enum: ["arrow", "bar", "dot", "triangle", "diamond"],
|
|
218
|
+
description: "起点箭头样式,默认无",
|
|
194
219
|
},
|
|
195
220
|
endArrowhead: {
|
|
196
|
-
type:
|
|
197
|
-
enum: [
|
|
198
|
-
description:
|
|
221
|
+
type: "string",
|
|
222
|
+
enum: ["arrow", "bar", "dot", "triangle", "diamond"],
|
|
223
|
+
description: "终点箭头样式,默认arrow",
|
|
224
|
+
},
|
|
225
|
+
strokeColor: { type: "string" },
|
|
226
|
+
strokeWidth: { type: "number", enum: [1, 2, 4] },
|
|
227
|
+
strokeStyle: {
|
|
228
|
+
type: "string",
|
|
229
|
+
enum: ["solid", "dashed", "dotted"],
|
|
230
|
+
},
|
|
231
|
+
elbowed: {
|
|
232
|
+
type: "boolean",
|
|
233
|
+
description: "是否使用直角折线,默认false",
|
|
199
234
|
},
|
|
200
|
-
strokeColor: { type: 'string' },
|
|
201
|
-
strokeWidth: { type: 'number', enum: [1, 2, 4] },
|
|
202
|
-
strokeStyle: { type: 'string', enum: ['solid', 'dashed', 'dotted'] },
|
|
203
|
-
elbowed: { type: 'boolean', description: '是否使用直角折线,默认false' },
|
|
204
235
|
},
|
|
205
236
|
required: [],
|
|
206
237
|
},
|
|
207
238
|
},
|
|
208
239
|
},
|
|
209
240
|
{
|
|
210
|
-
type:
|
|
241
|
+
type: "function",
|
|
211
242
|
function: {
|
|
212
|
-
name:
|
|
213
|
-
description:
|
|
243
|
+
name: "draw_line",
|
|
244
|
+
description: "绘制线条(无箭头),支持多个折点",
|
|
214
245
|
parameters: {
|
|
215
|
-
type:
|
|
246
|
+
type: "object",
|
|
216
247
|
properties: {
|
|
217
248
|
points: {
|
|
218
|
-
type:
|
|
219
|
-
items: { type:
|
|
220
|
-
description:
|
|
249
|
+
type: "array",
|
|
250
|
+
items: { type: "array", items: { type: "number" } },
|
|
251
|
+
description: "折点坐标数组,如 [[0,0],[100,50],[200,0]]",
|
|
252
|
+
},
|
|
253
|
+
strokeColor: { type: "string" },
|
|
254
|
+
strokeWidth: { type: "number", enum: [1, 2, 4] },
|
|
255
|
+
strokeStyle: {
|
|
256
|
+
type: "string",
|
|
257
|
+
enum: ["solid", "dashed", "dotted"],
|
|
221
258
|
},
|
|
222
|
-
strokeColor: { type: 'string' },
|
|
223
|
-
strokeWidth: { type: 'number', enum: [1, 2, 4] },
|
|
224
|
-
strokeStyle: { type: 'string', enum: ['solid', 'dashed', 'dotted'] },
|
|
225
259
|
},
|
|
226
|
-
required: [
|
|
260
|
+
required: ["points"],
|
|
227
261
|
},
|
|
228
262
|
},
|
|
229
263
|
},
|
|
230
264
|
{
|
|
231
|
-
type:
|
|
265
|
+
type: "function",
|
|
232
266
|
function: {
|
|
233
|
-
name:
|
|
234
|
-
description:
|
|
267
|
+
name: "draw_text",
|
|
268
|
+
description: "在白板上添加独立文本",
|
|
235
269
|
parameters: {
|
|
236
|
-
type:
|
|
270
|
+
type: "object",
|
|
237
271
|
properties: {
|
|
238
|
-
id: { type:
|
|
239
|
-
x: { type:
|
|
240
|
-
y: { type:
|
|
241
|
-
text: { type:
|
|
242
|
-
fontSize: { type:
|
|
272
|
+
id: { type: "string" },
|
|
273
|
+
x: { type: "number" },
|
|
274
|
+
y: { type: "number" },
|
|
275
|
+
text: { type: "string" },
|
|
276
|
+
fontSize: { type: "number", description: "字号,默认20" },
|
|
243
277
|
fontFamily: {
|
|
244
|
-
type:
|
|
278
|
+
type: "number",
|
|
245
279
|
enum: [1, 2, 3, 5],
|
|
246
|
-
description:
|
|
280
|
+
description: "1=手写,2=常规,3=等宽,5=圆体",
|
|
247
281
|
},
|
|
248
|
-
textAlign: { type:
|
|
249
|
-
strokeColor: { type:
|
|
282
|
+
textAlign: { type: "string", enum: ["left", "center", "right"] },
|
|
283
|
+
strokeColor: { type: "string" },
|
|
250
284
|
},
|
|
251
|
-
required: [
|
|
285
|
+
required: ["x", "y", "text"],
|
|
252
286
|
},
|
|
253
287
|
},
|
|
254
288
|
},
|
|
255
289
|
{
|
|
256
|
-
type:
|
|
290
|
+
type: "function",
|
|
257
291
|
function: {
|
|
258
|
-
name:
|
|
259
|
-
description:
|
|
292
|
+
name: "draw_group",
|
|
293
|
+
description:
|
|
294
|
+
"批量创建多个元素并自动编组(移动时一起移动)。适合画一组相关图形。",
|
|
260
295
|
parameters: {
|
|
261
|
-
type:
|
|
296
|
+
type: "object",
|
|
262
297
|
properties: {
|
|
263
298
|
elements: {
|
|
264
|
-
type:
|
|
265
|
-
description:
|
|
299
|
+
type: "array",
|
|
300
|
+
description: "元素数组,每个元素格式同其他 draw 工具的参数",
|
|
266
301
|
items: {
|
|
267
|
-
type:
|
|
302
|
+
type: "object",
|
|
268
303
|
properties: {
|
|
269
|
-
tool: {
|
|
304
|
+
tool: {
|
|
305
|
+
type: "string",
|
|
306
|
+
enum: ["shape", "arrow", "line", "text"],
|
|
307
|
+
},
|
|
270
308
|
args: {
|
|
271
|
-
type:
|
|
272
|
-
description:
|
|
309
|
+
type: "object",
|
|
310
|
+
description:
|
|
311
|
+
"对应 draw_shape/draw_arrow/draw_line/draw_text 的参数",
|
|
273
312
|
},
|
|
274
313
|
},
|
|
275
|
-
required: [
|
|
314
|
+
required: ["tool", "args"],
|
|
276
315
|
},
|
|
277
316
|
},
|
|
278
317
|
},
|
|
279
|
-
required: [
|
|
318
|
+
required: ["elements"],
|
|
280
319
|
},
|
|
281
320
|
},
|
|
282
321
|
},
|
|
283
322
|
{
|
|
284
|
-
type:
|
|
323
|
+
type: "function",
|
|
285
324
|
function: {
|
|
286
|
-
name:
|
|
287
|
-
description:
|
|
325
|
+
name: "draw_frame",
|
|
326
|
+
description:
|
|
327
|
+
"创建一个 Frame 容器,包含指定的子元素(子元素会被框在一起)",
|
|
288
328
|
parameters: {
|
|
289
|
-
type:
|
|
329
|
+
type: "object",
|
|
290
330
|
properties: {
|
|
291
|
-
name: { type:
|
|
292
|
-
childIds: {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
331
|
+
name: { type: "string", description: "Frame 名称" },
|
|
332
|
+
childIds: {
|
|
333
|
+
type: "array",
|
|
334
|
+
items: { type: "string" },
|
|
335
|
+
description: "子元素ID数组",
|
|
336
|
+
},
|
|
337
|
+
x: { type: "number" },
|
|
338
|
+
y: { type: "number" },
|
|
339
|
+
width: { type: "number" },
|
|
340
|
+
height: { type: "number" },
|
|
297
341
|
},
|
|
298
|
-
required: [
|
|
342
|
+
required: ["childIds"],
|
|
299
343
|
},
|
|
300
344
|
},
|
|
301
345
|
},
|
|
302
346
|
{
|
|
303
|
-
type:
|
|
347
|
+
type: "function",
|
|
304
348
|
function: {
|
|
305
|
-
name:
|
|
306
|
-
description:
|
|
349
|
+
name: "clear_canvas",
|
|
350
|
+
description: "清空白板上的所有内容",
|
|
307
351
|
parameters: {
|
|
308
|
-
type:
|
|
352
|
+
type: "object",
|
|
309
353
|
properties: {},
|
|
310
354
|
required: [],
|
|
311
355
|
},
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const WebSocket = require("ws");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Send a prompt through the WS relay and return the result
|
|
5
|
+
* @param {object} options
|
|
6
|
+
* @param {string} options.wsUrl - relay client URL (wss://ws.aibaiban.com/ws/client)
|
|
7
|
+
* @param {string} options.authSecret - RELAY_AUTH_SECRET
|
|
8
|
+
* @param {string} options.userId - user ID for relay auth
|
|
9
|
+
* @param {string} options.prompt - prompt text
|
|
10
|
+
* @param {number} [options.timeout=300000] - timeout in ms
|
|
11
|
+
* @returns {Promise<string>} result text
|
|
12
|
+
*/
|
|
13
|
+
exports.sendPromptViaRelay = function (options) {
|
|
14
|
+
const { wsUrl, authSecret, userId, prompt, timeout = 300000 } = options;
|
|
15
|
+
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const ws = new WebSocket(wsUrl);
|
|
18
|
+
let settled = false;
|
|
19
|
+
|
|
20
|
+
const timer = setTimeout(() => {
|
|
21
|
+
if (!settled) {
|
|
22
|
+
settled = true;
|
|
23
|
+
ws.close();
|
|
24
|
+
reject(new Error("Relay timeout"));
|
|
25
|
+
}
|
|
26
|
+
}, timeout);
|
|
27
|
+
|
|
28
|
+
function done(err, result) {
|
|
29
|
+
if (settled) return;
|
|
30
|
+
settled = true;
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
ws.close();
|
|
33
|
+
if (err) reject(err);
|
|
34
|
+
else resolve(result);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
ws.on("open", () => {
|
|
38
|
+
ws.send(JSON.stringify({ type: "auth", userId, token: authSecret }));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
ws.on("message", (data) => {
|
|
42
|
+
let msg;
|
|
43
|
+
try {
|
|
44
|
+
msg = JSON.parse(data.toString());
|
|
45
|
+
} catch {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (msg.type === "auth-ok") {
|
|
50
|
+
ws.send(JSON.stringify({ type: "prompt", prompt }));
|
|
51
|
+
} else if (msg.type === "auth-fail") {
|
|
52
|
+
done(new Error(`Auth failed: ${msg.reason}`));
|
|
53
|
+
} else if (msg.type === "response") {
|
|
54
|
+
done(null, msg.result);
|
|
55
|
+
} else if (msg.type === "error") {
|
|
56
|
+
done(new Error(msg.message || "Relay error"));
|
|
57
|
+
} else if (msg.type === "agent-offline") {
|
|
58
|
+
done(new Error("AI agent is offline"));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
ws.on("error", (err) => {
|
|
63
|
+
done(new Error(`WebSocket error: ${err.message}`));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
ws.on("close", () => {
|
|
67
|
+
done(new Error("WebSocket closed unexpectedly"));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
package/views/index.html
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
<!-- Primary Meta Tags -->
|
|
8
8
|
<title>AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图</title>
|
|
9
|
-
<meta
|
|
9
|
+
<meta
|
|
10
|
+
name="title"
|
|
11
|
+
content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图"
|
|
12
|
+
/>
|
|
10
13
|
<meta
|
|
11
14
|
name="description"
|
|
12
15
|
content="AI白板是专业的在线协作白板软件,AI驱动自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公、团队协同。Miro、FigJam、boardmix优质替代方案,一句话完成专业图表绘制。"
|
|
@@ -26,34 +29,60 @@
|
|
|
26
29
|
<!-- Open Graph / Facebook -->
|
|
27
30
|
<meta property="og:type" content="website" />
|
|
28
31
|
<meta property="og:url" content="https://aibaiban.com/" />
|
|
29
|
-
<meta
|
|
32
|
+
<meta
|
|
33
|
+
property="og:title"
|
|
34
|
+
content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图"
|
|
35
|
+
/>
|
|
30
36
|
<meta
|
|
31
37
|
property="og:description"
|
|
32
38
|
content="专业的在线协作白板软件,AI自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公。Miro/FigJam优质替代方案。"
|
|
33
39
|
/>
|
|
34
|
-
<meta
|
|
40
|
+
<meta
|
|
41
|
+
property="og:image"
|
|
42
|
+
content="https://static-small.vincentqiao.com/aibaiban/static/og-image.png"
|
|
43
|
+
/>
|
|
35
44
|
<meta property="og:site_name" content="AI白板" />
|
|
36
45
|
<meta property="og:locale" content="zh_CN" />
|
|
37
46
|
|
|
38
47
|
<!-- Twitter Card -->
|
|
39
48
|
<meta name="twitter:card" content="summary_large_image" />
|
|
40
49
|
<meta name="twitter:url" content="https://aibaiban.com/" />
|
|
41
|
-
<meta
|
|
50
|
+
<meta
|
|
51
|
+
name="twitter:title"
|
|
52
|
+
content="AI白板 - 在线协作白板工具 | AI生成流程图时序图类图ER图"
|
|
53
|
+
/>
|
|
42
54
|
<meta
|
|
43
55
|
name="twitter:description"
|
|
44
56
|
content="专业的在线协作白板软件,AI自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公。"
|
|
45
57
|
/>
|
|
46
|
-
<meta
|
|
58
|
+
<meta
|
|
59
|
+
name="twitter:image"
|
|
60
|
+
content="https://static-small.vincentqiao.com/aibaiban/static/og-image.png"
|
|
61
|
+
/>
|
|
47
62
|
|
|
48
63
|
<!-- Apple Mobile Web App -->
|
|
49
64
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
50
|
-
<meta
|
|
65
|
+
<meta
|
|
66
|
+
name="apple-mobile-web-app-status-bar-style"
|
|
67
|
+
content="black-translucent"
|
|
68
|
+
/>
|
|
51
69
|
<meta name="apple-mobile-web-app-title" content="AI白板" />
|
|
52
70
|
|
|
53
71
|
<!-- Favicon -->
|
|
54
|
-
<link
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
<link
|
|
73
|
+
rel="icon"
|
|
74
|
+
type="image/x-icon"
|
|
75
|
+
href="https://static-small.vincentqiao.com/aibaiban/static/favicon.ico"
|
|
76
|
+
/>
|
|
77
|
+
<link
|
|
78
|
+
rel="icon"
|
|
79
|
+
type="image/svg+xml"
|
|
80
|
+
href="https://static-small.vincentqiao.com/aibaiban/static/logo.svg"
|
|
81
|
+
/>
|
|
82
|
+
<link
|
|
83
|
+
rel="apple-touch-icon"
|
|
84
|
+
href="https://static-small.vincentqiao.com/aibaiban/static/apple-touch-icon.png"
|
|
85
|
+
/>
|
|
57
86
|
|
|
58
87
|
<!-- Manifest -->
|
|
59
88
|
<link rel="manifest" href="https://aibaiban.com/manifest.json" />
|
|
@@ -81,13 +110,20 @@
|
|
|
81
110
|
"description": "专业的在线协作白板软件,AI驱动自动生成流程图、时序图、类图、ER图。支持在线协作、远程办公、团队协同。",
|
|
82
111
|
"url": "https://aibaiban.com",
|
|
83
112
|
"image": "https://static-small.vincentqiao.com/aibaiban/static/og-image.png",
|
|
84
|
-
"featureList": [
|
|
113
|
+
"featureList": [
|
|
114
|
+
"AI生成流程图",
|
|
115
|
+
"AI生成时序图",
|
|
116
|
+
"AI生成类图",
|
|
117
|
+
"AI生成ER图",
|
|
118
|
+
"在线协作白板",
|
|
119
|
+
"远程办公支持"
|
|
120
|
+
]
|
|
85
121
|
}
|
|
86
122
|
]
|
|
87
123
|
</script>
|
|
88
124
|
<!-- Microsoft Clarity (production only) -->
|
|
89
125
|
<script type="text/javascript">
|
|
90
|
-
if (location.hostname.includes(
|
|
126
|
+
if (location.hostname.includes("aibaiban.com")) {
|
|
91
127
|
(function (c, l, a, r, i, t, y) {
|
|
92
128
|
c[a] =
|
|
93
129
|
c[a] ||
|
|
@@ -96,16 +132,16 @@
|
|
|
96
132
|
};
|
|
97
133
|
t = l.createElement(r);
|
|
98
134
|
t.async = 1;
|
|
99
|
-
t.src =
|
|
135
|
+
t.src = "https://www.clarity.ms/tag/" + i;
|
|
100
136
|
y = l.getElementsByTagName(r)[0];
|
|
101
137
|
y.parentNode.insertBefore(t, y);
|
|
102
|
-
})(window, document,
|
|
138
|
+
})(window, document, "clarity", "script", "t5b230u2zp");
|
|
103
139
|
}
|
|
104
140
|
</script>
|
|
105
141
|
<script
|
|
106
142
|
type="module"
|
|
107
143
|
crossorigin
|
|
108
|
-
src="https://static-small.vincentqiao.com/aibaiban/static/index-
|
|
144
|
+
src="https://static-small.vincentqiao.com/aibaiban/static/index-CtuN3dTq.js"
|
|
109
145
|
></script>
|
|
110
146
|
<link
|
|
111
147
|
rel="modulepreload"
|
|
@@ -141,8 +177,8 @@
|
|
|
141
177
|
<body>
|
|
142
178
|
<div id="root"></div>
|
|
143
179
|
<script>
|
|
144
|
-
if (
|
|
145
|
-
navigator.serviceWorker.register(
|
|
180
|
+
if ("serviceWorker" in navigator) {
|
|
181
|
+
navigator.serviceWorker.register("/sw.js");
|
|
146
182
|
}
|
|
147
183
|
</script>
|
|
148
184
|
</body>
|