collabdocchat 1.0.0 → 1.0.1
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 +23 -3
- package/index.html +14 -0
- package/package.json +22 -15
- package/scripts/open-browser.js +37 -0
- package/scripts/postinstall.js +159 -0
- package/scripts/start-app.js +164 -0
- package/vite.config.js +21 -0
package/README.md
CHANGED
|
@@ -111,10 +111,25 @@
|
|
|
111
111
|
|
|
112
112
|
### 安装步骤
|
|
113
113
|
|
|
114
|
+
#### 方式一:通过 npm 安装(推荐)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 安装包
|
|
118
|
+
npm install collabdocchat
|
|
119
|
+
|
|
120
|
+
# 进入安装目录
|
|
121
|
+
cd node_modules/collabdocchat
|
|
122
|
+
|
|
123
|
+
# 启动应用(自动打开浏览器)
|
|
124
|
+
npm start
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### 方式二:从源码安装
|
|
128
|
+
|
|
114
129
|
1. **克隆项目**
|
|
115
130
|
|
|
116
131
|
```bash
|
|
117
|
-
git clone https://github.com/
|
|
132
|
+
git clone https://github.com/shijinghao/collabdocchat.git
|
|
118
133
|
cd collabdocchat
|
|
119
134
|
```
|
|
120
135
|
|
|
@@ -154,7 +169,10 @@ sudo systemctl start mongod
|
|
|
154
169
|
5. **启动项目**
|
|
155
170
|
|
|
156
171
|
```bash
|
|
157
|
-
#
|
|
172
|
+
# 一键启动(推荐)- 自动启动服务器和客户端,并打开浏览器
|
|
173
|
+
npm start
|
|
174
|
+
|
|
175
|
+
# 开发模式(同时启动前后端,使用 nodemon 自动重启)
|
|
158
176
|
npm run dev
|
|
159
177
|
|
|
160
178
|
# 或分别启动
|
|
@@ -164,7 +182,9 @@ npm run client # 前端开发服务器
|
|
|
164
182
|
|
|
165
183
|
6. **访问应用**
|
|
166
184
|
|
|
167
|
-
|
|
185
|
+
运行 `npm start` 后,浏览器会自动打开并访问 `http://localhost:5173`
|
|
186
|
+
|
|
187
|
+
或手动访问:`http://localhost:5173`
|
|
168
188
|
|
|
169
189
|
---
|
|
170
190
|
|
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.1",
|
|
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,159 @@
|
|
|
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
|
+
shell: true,
|
|
94
|
+
detached: true
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
server.unref();
|
|
98
|
+
return server;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 启动客户端
|
|
102
|
+
function startClient() {
|
|
103
|
+
console.log('🎨 正在启动客户端...');
|
|
104
|
+
const client = spawn('npm', ['run', 'client'], {
|
|
105
|
+
cwd: rootDir,
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
shell: true,
|
|
108
|
+
detached: true
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
client.unref();
|
|
112
|
+
return client;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 主函数
|
|
116
|
+
async function main() {
|
|
117
|
+
console.log('\n📦 CollabDocChat 安装完成!');
|
|
118
|
+
console.log('🚀 正在自动启动应用...\n');
|
|
119
|
+
|
|
120
|
+
// 启动服务器
|
|
121
|
+
const server = startServer();
|
|
122
|
+
|
|
123
|
+
// 等待服务器就绪
|
|
124
|
+
try {
|
|
125
|
+
await waitForServer(`http://localhost:${PORT}/health`, 30, 1000);
|
|
126
|
+
console.log(`✅ 服务器已就绪: http://localhost:${PORT}\n`);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('❌ 服务器启动失败:', error.message);
|
|
129
|
+
console.log('\n💡 请确保:');
|
|
130
|
+
console.log(' 1. MongoDB 已启动');
|
|
131
|
+
console.log(' 2. 端口 3000 和 5173 未被占用');
|
|
132
|
+
console.log(' 3. 运行 npm start 手动启动');
|
|
133
|
+
process.exit(0);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 启动客户端
|
|
138
|
+
const client = startClient();
|
|
139
|
+
|
|
140
|
+
// 等待客户端就绪后打开浏览器
|
|
141
|
+
setTimeout(async () => {
|
|
142
|
+
try {
|
|
143
|
+
await waitForServer(CLIENT_URL, 20, 500);
|
|
144
|
+
console.log(`\n🌐 正在打开浏览器: ${CLIENT_URL}`);
|
|
145
|
+
openBrowser(CLIENT_URL);
|
|
146
|
+
console.log('\n✅ 应用已启动!浏览器应该已经自动打开。');
|
|
147
|
+
console.log('\n💡 提示:按 Ctrl+C 可以停止服务器');
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.log(`\n⚠️ 客户端可能还在启动中,请手动访问: ${CLIENT_URL}`);
|
|
150
|
+
openBrowser(CLIENT_URL);
|
|
151
|
+
}
|
|
152
|
+
}, 2000);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
main().catch(error => {
|
|
156
|
+
console.error('❌ 启动失败:', error.message);
|
|
157
|
+
console.log('\n💡 可以运行 npm start 手动启动应用');
|
|
158
|
+
});
|
|
159
|
+
|
|
@@ -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
|
+
|