collabdocchat 1.0.0 → 1.0.3
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/README.md +521 -501
- package/index.html +14 -0
- package/package.json +22 -15
- package/scripts/open-browser.js +37 -0
- package/scripts/postinstall.js +168 -0
- package/scripts/start-app.js +164 -0
- package/vite.config.js +21 -0
package/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>CollabDocChat - 协作文档聊天平台</title>
|
|
7
|
+
<link rel="stylesheet" href="/src/styles/main.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
14
|
+
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "collabdocchat",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "开源的实时协作文档聊天平台 - 集成任务管理、多人文档编辑、智能点名功能",
|
|
5
5
|
"main": "./server/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
8
8
|
"server",
|
|
9
9
|
"src",
|
|
10
|
+
"scripts",
|
|
10
11
|
"README.md",
|
|
11
12
|
"LICENSE",
|
|
12
|
-
"package.json"
|
|
13
|
+
"package.json",
|
|
14
|
+
"vite.config.js",
|
|
15
|
+
"index.html"
|
|
13
16
|
],
|
|
14
17
|
"keywords": [
|
|
15
18
|
"collaboration",
|
|
@@ -40,31 +43,35 @@
|
|
|
40
43
|
"npm": ">=7.0.0"
|
|
41
44
|
},
|
|
42
45
|
"scripts": {
|
|
43
|
-
"
|
|
46
|
+
"postinstall": "node scripts/postinstall.js",
|
|
47
|
+
"dev": "concurrently \"npm run server\" \"npm run client\" \"npm run open-browser\"",
|
|
48
|
+
"start": "node scripts/start-app.js",
|
|
44
49
|
"server": "nodemon server/index.js",
|
|
45
50
|
"client": "vite",
|
|
51
|
+
"open-browser": "node scripts/open-browser.js",
|
|
46
52
|
"build": "vite build",
|
|
47
53
|
"preview": "vite preview",
|
|
48
|
-
"
|
|
49
|
-
"prepublishOnly": "
|
|
54
|
+
"serve": "node server/index.js",
|
|
55
|
+
"prepublishOnly": "echo 'Skipping build for source package'"
|
|
50
56
|
},
|
|
51
57
|
"dependencies": {
|
|
52
|
-
"express": "^4.18.2",
|
|
53
|
-
"ws": "^8.14.2",
|
|
54
|
-
"mongoose": "^8.0.3",
|
|
55
|
-
"jsonwebtoken": "^9.0.2",
|
|
56
58
|
"bcryptjs": "^2.4.3",
|
|
59
|
+
"collabdocchat": "^1.0.0",
|
|
57
60
|
"cors": "^2.8.5",
|
|
58
61
|
"dotenv": "^16.3.1",
|
|
59
|
-
"
|
|
62
|
+
"express": "^4.18.2",
|
|
63
|
+
"jsonwebtoken": "^9.0.2",
|
|
64
|
+
"mongoose": "^8.0.3",
|
|
65
|
+
"quill": "^2.0.3",
|
|
66
|
+
"quill-cursors": "^4.0.2",
|
|
67
|
+
"ws": "^8.14.2",
|
|
60
68
|
"y-websocket": "^1.5.0",
|
|
61
|
-
"
|
|
62
|
-
"quill-cursors": "^4.0.2"
|
|
69
|
+
"yjs": "^13.6.10"
|
|
63
70
|
},
|
|
64
71
|
"devDependencies": {
|
|
65
|
-
"
|
|
72
|
+
"concurrently": "^8.2.2",
|
|
66
73
|
"nodemon": "^3.0.2",
|
|
67
|
-
"
|
|
74
|
+
"open": "^11.0.0",
|
|
75
|
+
"vite": "^7.3.1"
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
|
-
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { platform } from 'os';
|
|
3
|
+
|
|
4
|
+
const url = 'http://localhost:5173';
|
|
5
|
+
const delay = 3000; // 等待 3 秒让服务器启动
|
|
6
|
+
|
|
7
|
+
// 根据操作系统选择打开命令
|
|
8
|
+
function openBrowser(url) {
|
|
9
|
+
const platformName = platform();
|
|
10
|
+
let command;
|
|
11
|
+
|
|
12
|
+
switch (platformName) {
|
|
13
|
+
case 'win32':
|
|
14
|
+
command = `start ${url}`;
|
|
15
|
+
break;
|
|
16
|
+
case 'darwin':
|
|
17
|
+
command = `open ${url}`;
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
command = `xdg-open ${url}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exec(command, (error) => {
|
|
24
|
+
if (error) {
|
|
25
|
+
console.error(`无法打开浏览器: ${error.message}`);
|
|
26
|
+
console.log(`\n请手动访问: ${url}`);
|
|
27
|
+
} else {
|
|
28
|
+
console.log(`\n🌐 正在打开浏览器: ${url}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 延迟打开,确保服务器已启动
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
openBrowser(url);
|
|
36
|
+
}, delay);
|
|
37
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { platform } from 'os';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const rootDir = join(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
const PORT = process.env.PORT || 3000;
|
|
12
|
+
const CLIENT_PORT = 5173;
|
|
13
|
+
const CLIENT_URL = `http://localhost:${CLIENT_PORT}`;
|
|
14
|
+
|
|
15
|
+
// 打开浏览器的函数
|
|
16
|
+
function openBrowser(url) {
|
|
17
|
+
const platformName = platform();
|
|
18
|
+
let command;
|
|
19
|
+
let args;
|
|
20
|
+
|
|
21
|
+
switch (platformName) {
|
|
22
|
+
case 'win32':
|
|
23
|
+
command = 'cmd';
|
|
24
|
+
args = ['/c', 'start', url];
|
|
25
|
+
break;
|
|
26
|
+
case 'darwin':
|
|
27
|
+
command = 'open';
|
|
28
|
+
args = [url];
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
command = 'xdg-open';
|
|
32
|
+
args = [url];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const browser = spawn(command, args, { stdio: 'ignore', detached: true });
|
|
36
|
+
browser.unref();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 检查服务器是否就绪
|
|
40
|
+
function waitForServer(url, maxAttempts = 30, delay = 1000) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
let attempts = 0;
|
|
43
|
+
const urlObj = new URL(url);
|
|
44
|
+
|
|
45
|
+
const check = () => {
|
|
46
|
+
attempts++;
|
|
47
|
+
|
|
48
|
+
const req = http.get({
|
|
49
|
+
hostname: urlObj.hostname,
|
|
50
|
+
port: urlObj.port,
|
|
51
|
+
path: urlObj.pathname,
|
|
52
|
+
timeout: 1000
|
|
53
|
+
}, (res) => {
|
|
54
|
+
if (res.statusCode === 200) {
|
|
55
|
+
resolve();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (attempts < maxAttempts) {
|
|
59
|
+
setTimeout(check, delay);
|
|
60
|
+
} else {
|
|
61
|
+
reject(new Error('服务器启动超时'));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
req.on('error', () => {
|
|
66
|
+
if (attempts < maxAttempts) {
|
|
67
|
+
setTimeout(check, delay);
|
|
68
|
+
} else {
|
|
69
|
+
reject(new Error('服务器启动超时'));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
req.on('timeout', () => {
|
|
74
|
+
req.destroy();
|
|
75
|
+
if (attempts < maxAttempts) {
|
|
76
|
+
setTimeout(check, delay);
|
|
77
|
+
} else {
|
|
78
|
+
reject(new Error('服务器启动超时'));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
check();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 启动服务器
|
|
88
|
+
function startServer() {
|
|
89
|
+
console.log('\n🚀 正在启动 CollabDocChat 服务器...');
|
|
90
|
+
const server = spawn('node', ['server/index.js'], {
|
|
91
|
+
cwd: rootDir,
|
|
92
|
+
stdio: 'inherit',
|
|
93
|
+
detached: true,
|
|
94
|
+
windowsHide: true
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
server.unref();
|
|
98
|
+
return server;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 启动客户端
|
|
102
|
+
function startClient() {
|
|
103
|
+
console.log('🎨 正在启动客户端...');
|
|
104
|
+
const isWindows = platform() === 'win32';
|
|
105
|
+
const client = spawn(isWindows ? 'npm.cmd' : 'npm', ['run', 'client'], {
|
|
106
|
+
cwd: rootDir,
|
|
107
|
+
stdio: 'inherit',
|
|
108
|
+
detached: true,
|
|
109
|
+
windowsHide: true
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
client.unref();
|
|
113
|
+
return client;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 主函数 - 在后台异步运行,不阻塞安装
|
|
117
|
+
async function main() {
|
|
118
|
+
// 延迟启动,确保安装过程完成
|
|
119
|
+
setTimeout(async () => {
|
|
120
|
+
console.log('\n📦 CollabDocChat 安装完成!');
|
|
121
|
+
console.log('🚀 正在自动启动应用...\n');
|
|
122
|
+
|
|
123
|
+
// 启动服务器
|
|
124
|
+
const server = startServer();
|
|
125
|
+
|
|
126
|
+
// 等待服务器就绪
|
|
127
|
+
try {
|
|
128
|
+
await waitForServer(`http://localhost:${PORT}/health`, 30, 1000);
|
|
129
|
+
console.log(`✅ 服务器已就绪: http://localhost:${PORT}\n`);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('❌ 服务器启动失败:', error.message);
|
|
132
|
+
console.log('\n💡 请确保:');
|
|
133
|
+
console.log(' 1. MongoDB 已启动');
|
|
134
|
+
console.log(' 2. 端口 3000 和 5173 未被占用');
|
|
135
|
+
console.log(' 3. 运行 npm start 手动启动');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 启动客户端
|
|
140
|
+
const client = startClient();
|
|
141
|
+
|
|
142
|
+
// 等待客户端就绪后打开浏览器
|
|
143
|
+
setTimeout(async () => {
|
|
144
|
+
try {
|
|
145
|
+
await waitForServer(CLIENT_URL, 30, 1000);
|
|
146
|
+
console.log(`\n🌐 正在打开浏览器: ${CLIENT_URL}`);
|
|
147
|
+
openBrowser(CLIENT_URL);
|
|
148
|
+
console.log('\n✅ 应用已启动!浏览器应该已经自动打开。');
|
|
149
|
+
} catch (error) {
|
|
150
|
+
// 即使检查失败也尝试打开浏览器(客户端可能正在启动)
|
|
151
|
+
console.log(`\n🌐 正在打开浏览器: ${CLIENT_URL}`);
|
|
152
|
+
openBrowser(CLIENT_URL);
|
|
153
|
+
console.log('\n✅ 应用正在启动中,浏览器已打开。');
|
|
154
|
+
console.log(' 如果页面未加载,请稍等片刻后刷新页面。');
|
|
155
|
+
}
|
|
156
|
+
}, 5000);
|
|
157
|
+
}, 1000);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 启动后台进程,不阻塞安装
|
|
161
|
+
main().catch(error => {
|
|
162
|
+
// 静默处理错误,不影响安装过程
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// 立即退出,让安装过程完成
|
|
166
|
+
// 服务器和客户端会在后台运行
|
|
167
|
+
process.exit(0);
|
|
168
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { platform } from 'os';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const rootDir = join(__dirname, '..');
|
|
10
|
+
|
|
11
|
+
const PORT = process.env.PORT || 3000;
|
|
12
|
+
const CLIENT_PORT = 5173;
|
|
13
|
+
const CLIENT_URL = `http://localhost:${CLIENT_PORT}`;
|
|
14
|
+
|
|
15
|
+
// 打开浏览器的函数
|
|
16
|
+
function openBrowser(url) {
|
|
17
|
+
const platformName = platform();
|
|
18
|
+
let command;
|
|
19
|
+
let args;
|
|
20
|
+
|
|
21
|
+
switch (platformName) {
|
|
22
|
+
case 'win32':
|
|
23
|
+
command = 'cmd';
|
|
24
|
+
args = ['/c', 'start', url];
|
|
25
|
+
break;
|
|
26
|
+
case 'darwin':
|
|
27
|
+
command = 'open';
|
|
28
|
+
args = [url];
|
|
29
|
+
break;
|
|
30
|
+
default:
|
|
31
|
+
command = 'xdg-open';
|
|
32
|
+
args = [url];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const browser = spawn(command, args, { stdio: 'ignore', detached: true });
|
|
36
|
+
browser.unref();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 检查服务器是否就绪
|
|
40
|
+
function waitForServer(url, maxAttempts = 30, delay = 1000) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
let attempts = 0;
|
|
43
|
+
const urlObj = new URL(url);
|
|
44
|
+
|
|
45
|
+
const check = () => {
|
|
46
|
+
attempts++;
|
|
47
|
+
|
|
48
|
+
const req = http.get({
|
|
49
|
+
hostname: urlObj.hostname,
|
|
50
|
+
port: urlObj.port,
|
|
51
|
+
path: urlObj.pathname,
|
|
52
|
+
timeout: 1000
|
|
53
|
+
}, (res) => {
|
|
54
|
+
if (res.statusCode === 200) {
|
|
55
|
+
resolve();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// 继续等待
|
|
59
|
+
if (attempts < maxAttempts) {
|
|
60
|
+
setTimeout(check, delay);
|
|
61
|
+
} else {
|
|
62
|
+
reject(new Error('服务器启动超时'));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
req.on('error', () => {
|
|
67
|
+
// 服务器未就绪,继续等待
|
|
68
|
+
if (attempts < maxAttempts) {
|
|
69
|
+
setTimeout(check, delay);
|
|
70
|
+
} else {
|
|
71
|
+
reject(new Error('服务器启动超时'));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
req.on('timeout', () => {
|
|
76
|
+
req.destroy();
|
|
77
|
+
if (attempts < maxAttempts) {
|
|
78
|
+
setTimeout(check, delay);
|
|
79
|
+
} else {
|
|
80
|
+
reject(new Error('服务器启动超时'));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
check();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 启动服务器
|
|
90
|
+
function startServer() {
|
|
91
|
+
console.log('🚀 正在启动服务器...');
|
|
92
|
+
const server = spawn('node', ['server/index.js'], {
|
|
93
|
+
cwd: rootDir,
|
|
94
|
+
stdio: 'inherit',
|
|
95
|
+
shell: true
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return server;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 启动客户端(如果使用 Vite)
|
|
102
|
+
function startClient() {
|
|
103
|
+
console.log('🎨 正在启动客户端...');
|
|
104
|
+
const client = spawn('npm', ['run', 'client'], {
|
|
105
|
+
cwd: rootDir,
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
shell: true
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return client;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 主函数
|
|
114
|
+
async function main() {
|
|
115
|
+
console.log('📦 CollabDocChat 正在启动...\n');
|
|
116
|
+
|
|
117
|
+
// 启动服务器
|
|
118
|
+
const server = startServer();
|
|
119
|
+
|
|
120
|
+
// 等待服务器就绪
|
|
121
|
+
try {
|
|
122
|
+
await waitForServer(`http://localhost:${PORT}/health`, 30, 1000);
|
|
123
|
+
console.log(`✅ 服务器已就绪: http://localhost:${PORT}\n`);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('❌ 服务器启动失败:', error.message);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 启动客户端
|
|
130
|
+
const client = startClient();
|
|
131
|
+
|
|
132
|
+
// 等待客户端就绪后打开浏览器
|
|
133
|
+
setTimeout(async () => {
|
|
134
|
+
try {
|
|
135
|
+
await waitForServer(CLIENT_URL, 20, 500);
|
|
136
|
+
console.log(`\n🌐 正在打开浏览器: ${CLIENT_URL}`);
|
|
137
|
+
openBrowser(CLIENT_URL);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.log(`\n⚠️ 客户端可能还在启动中,请手动访问: ${CLIENT_URL}`);
|
|
140
|
+
// 即使检查失败也尝试打开浏览器
|
|
141
|
+
openBrowser(CLIENT_URL);
|
|
142
|
+
}
|
|
143
|
+
}, 2000);
|
|
144
|
+
|
|
145
|
+
// 处理退出信号
|
|
146
|
+
process.on('SIGINT', () => {
|
|
147
|
+
console.log('\n\n🛑 正在关闭服务器...');
|
|
148
|
+
server.kill();
|
|
149
|
+
client.kill();
|
|
150
|
+
process.exit(0);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
process.on('SIGTERM', () => {
|
|
154
|
+
server.kill();
|
|
155
|
+
client.kill();
|
|
156
|
+
process.exit(0);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main().catch(error => {
|
|
161
|
+
console.error('❌ 启动失败:', error);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
});
|
|
164
|
+
|
package/vite.config.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
server: {
|
|
5
|
+
port: 5173,
|
|
6
|
+
open: true, // 自动打开浏览器
|
|
7
|
+
proxy: {
|
|
8
|
+
'/api': {
|
|
9
|
+
target: 'http://localhost:3000',
|
|
10
|
+
changeOrigin: true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
build: {
|
|
15
|
+
rollupOptions: {
|
|
16
|
+
external: ['emoji-picker-element'] // 外部化依赖
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
|