@zzp123/mcp-zentao 1.18.8 → 1.18.10
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/CHANGELOG.md +1018 -1018
- package/LICENSE +20 -20
- package/README.md +527 -527
- package/dist/api/zentaoApi.js +19 -18
- package/dist/index-dev.js +43 -36
- package/dist/index-pm.js +69 -710
- package/dist/index-qa.js +29 -34
- package/dist/index.js +61 -41
- package/dist/roleConfig.d.ts +0 -4
- package/dist/roleConfig.js +24 -33
- package/dist/types/zentao.d.ts +1 -0
- package/json-args.js +26 -26
- package/package.json +75 -75
- package/scripts/MCP-INTEGRATION.md +298 -298
- package/scripts/README.md +315 -315
- package/scripts/generate-role-versions.cjs +212 -209
- package/scripts/get-clipboard-base64.js +93 -93
- package/scripts/get_clipboard.ps1 +13 -13
- package/scripts/upload-clipboard-image.js +200 -200
- package/scripts/upload-clipboard-image.ps1 +128 -128
- package/scripts/upload-clipboard-image.py +196 -196
- package/scripts/upload.bat.example +41 -41
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 从剪贴板获取图片并上传到禅道
|
|
5
|
-
* 使用方法: node upload-clipboard-image.js
|
|
6
|
-
*
|
|
7
|
-
* 环境变量:
|
|
8
|
-
* - ZENTAO_URL: 禅道系统URL
|
|
9
|
-
* - ZENTAO_USERNAME: 用户名
|
|
10
|
-
* - ZENTAO_PASSWORD: 密码
|
|
11
|
-
* - ZENTAO_UID: 可选的UID参数
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import clipboardy from 'clipboardy';
|
|
15
|
-
import fs from 'fs';
|
|
16
|
-
import path from 'path';
|
|
17
|
-
import { fileURLToPath } from 'url';
|
|
18
|
-
import { dirname } from 'path';
|
|
19
|
-
|
|
20
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
-
const __dirname = dirname(__filename);
|
|
22
|
-
|
|
23
|
-
// 动态导入 ZentaoAPI
|
|
24
|
-
async function uploadClipboardImage() {
|
|
25
|
-
try {
|
|
26
|
-
console.log('正在读取剪贴板内容...');
|
|
27
|
-
|
|
28
|
-
// 检查环境变量
|
|
29
|
-
const zentaoUrl = process.env.ZENTAO_URL;
|
|
30
|
-
const username = process.env.ZENTAO_USERNAME;
|
|
31
|
-
const password = process.env.ZENTAO_PASSWORD;
|
|
32
|
-
const uid = process.env.ZENTAO_UID;
|
|
33
|
-
|
|
34
|
-
if (!zentaoUrl || !username || !password) {
|
|
35
|
-
console.error('错误: 请设置环境变量 ZENTAO_URL, ZENTAO_USERNAME, ZENTAO_PASSWORD');
|
|
36
|
-
console.error('');
|
|
37
|
-
console.error('Windows示例:');
|
|
38
|
-
console.error(' set ZENTAO_URL=https://your-zentao-url');
|
|
39
|
-
console.error(' set ZENTAO_USERNAME=your-username');
|
|
40
|
-
console.error(' set ZENTAO_PASSWORD=your-password');
|
|
41
|
-
console.error(' node upload-clipboard-image.js');
|
|
42
|
-
console.error('');
|
|
43
|
-
console.error('Mac/Linux示例:');
|
|
44
|
-
console.error(' export ZENTAO_URL=https://your-zentao-url');
|
|
45
|
-
console.error(' export ZENTAO_USERNAME=your-username');
|
|
46
|
-
console.error(' export ZENTAO_PASSWORD=your-password');
|
|
47
|
-
console.error(' node upload-clipboard-image.js');
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 尝试读取剪贴板图片
|
|
52
|
-
let imageData;
|
|
53
|
-
|
|
54
|
-
// Windows: 使用 PowerShell 读取剪贴板图片
|
|
55
|
-
if (process.platform === 'win32') {
|
|
56
|
-
const { execSync } = await import('child_process');
|
|
57
|
-
try {
|
|
58
|
-
const psScript = `
|
|
59
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
60
|
-
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
61
|
-
if ($img -ne $null) {
|
|
62
|
-
$ms = New-Object System.IO.MemoryStream
|
|
63
|
-
$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
64
|
-
[Convert]::ToBase64String($ms.ToArray())
|
|
65
|
-
}
|
|
66
|
-
`;
|
|
67
|
-
const base64 = execSync(`powershell -Command "${psScript.replace(/\n/g, ' ')}"`, {
|
|
68
|
-
encoding: 'utf8',
|
|
69
|
-
maxBuffer: 10 * 1024 * 1024
|
|
70
|
-
}).trim();
|
|
71
|
-
|
|
72
|
-
if (base64) {
|
|
73
|
-
imageData = `data:image/png;base64,${base64}`;
|
|
74
|
-
console.log('✓ 从剪贴板读取图片成功 (Windows)');
|
|
75
|
-
} else {
|
|
76
|
-
console.error('错误: 剪贴板中没有图片');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error('错误: 无法读取剪贴板图片', error.message);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// macOS: 使用 pngpaste
|
|
85
|
-
else if (process.platform === 'darwin') {
|
|
86
|
-
const { execSync } = await import('child_process');
|
|
87
|
-
try {
|
|
88
|
-
// 检查是否安装了 pngpaste
|
|
89
|
-
try {
|
|
90
|
-
execSync('which pngpaste', { stdio: 'ignore' });
|
|
91
|
-
} catch {
|
|
92
|
-
console.error('错误: 请先安装 pngpaste');
|
|
93
|
-
console.error('运行: brew install pngpaste');
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const tempFile = path.join(__dirname, 'temp_clipboard.png');
|
|
98
|
-
execSync(`pngpaste "${tempFile}"`);
|
|
99
|
-
const buffer = fs.readFileSync(tempFile);
|
|
100
|
-
const base64 = buffer.toString('base64');
|
|
101
|
-
imageData = `data:image/png;base64,${base64}`;
|
|
102
|
-
fs.unlinkSync(tempFile);
|
|
103
|
-
console.log('✓ 从剪贴板读取图片成功 (macOS)');
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error('错误: 剪贴板中没有图片或读取失败', error.message);
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// Linux: 使用 xclip
|
|
110
|
-
else {
|
|
111
|
-
const { execSync } = await import('child_process');
|
|
112
|
-
try {
|
|
113
|
-
// 检查是否安装了 xclip
|
|
114
|
-
try {
|
|
115
|
-
execSync('which xclip', { stdio: 'ignore' });
|
|
116
|
-
} catch {
|
|
117
|
-
console.error('错误: 请先安装 xclip');
|
|
118
|
-
console.error('运行: sudo apt-get install xclip (Ubuntu/Debian)');
|
|
119
|
-
console.error('或: sudo yum install xclip (CentOS/RHEL)');
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const buffer = execSync('xclip -selection clipboard -t image/png -o', {
|
|
124
|
-
maxBuffer: 10 * 1024 * 1024
|
|
125
|
-
});
|
|
126
|
-
const base64 = buffer.toString('base64');
|
|
127
|
-
imageData = `data:image/png;base64,${base64}`;
|
|
128
|
-
console.log('✓ 从剪贴板读取图片成功 (Linux)');
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error('错误: 剪贴板中没有图片或读取失败', error.message);
|
|
131
|
-
process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// 导入 ZentaoAPI
|
|
136
|
-
const modulePath = path.join(__dirname, '..', 'dist', 'api', 'zentaoApi.js');
|
|
137
|
-
const { ZentaoAPI } = await import(modulePath);
|
|
138
|
-
|
|
139
|
-
// 创建 API 实例
|
|
140
|
-
console.log('正在连接禅道系统...');
|
|
141
|
-
const api = new ZentaoAPI({
|
|
142
|
-
url: zentaoUrl,
|
|
143
|
-
username: username,
|
|
144
|
-
password: password,
|
|
145
|
-
apiVersion: 'v1'
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// 创建 img 文件夹
|
|
149
|
-
const imgDir = path.join(process.cwd(), 'img');
|
|
150
|
-
if (!fs.existsSync(imgDir)) {
|
|
151
|
-
fs.mkdirSync(imgDir, { recursive: true });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// 解析 base64 数据
|
|
155
|
-
const matches = imageData.match(/^data:image\/(\w+);base64,(.+)$/);
|
|
156
|
-
if (!matches) {
|
|
157
|
-
console.error('错误: 无效的图片数据');
|
|
158
|
-
process.exit(1);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const ext = matches[1];
|
|
162
|
-
const base64Data = matches[2];
|
|
163
|
-
const buffer = Buffer.from(base64Data, 'base64');
|
|
164
|
-
const filename = `clipboard_${Date.now()}.${ext}`;
|
|
165
|
-
|
|
166
|
-
// 保存到本地
|
|
167
|
-
const savedPath = path.join(imgDir, filename);
|
|
168
|
-
fs.writeFileSync(savedPath, buffer);
|
|
169
|
-
console.log(`✓ 图片已保存到: ${savedPath}`);
|
|
170
|
-
|
|
171
|
-
// 上传到禅道
|
|
172
|
-
console.log('正在上传到禅道...');
|
|
173
|
-
const result = await api.uploadFile({
|
|
174
|
-
file: buffer,
|
|
175
|
-
filename: filename,
|
|
176
|
-
uid: uid
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
console.log('');
|
|
180
|
-
console.log('========================================');
|
|
181
|
-
console.log('✓ 上传成功!');
|
|
182
|
-
console.log('========================================');
|
|
183
|
-
console.log('文件ID:', result.id);
|
|
184
|
-
console.log('访问URL:', result.url);
|
|
185
|
-
console.log('本地路径:', savedPath);
|
|
186
|
-
console.log('========================================');
|
|
187
|
-
|
|
188
|
-
} catch (error) {
|
|
189
|
-
console.error('');
|
|
190
|
-
console.error('========================================');
|
|
191
|
-
console.error('✗ 上传失败');
|
|
192
|
-
console.error('========================================');
|
|
193
|
-
console.error('错误信息:', error.message);
|
|
194
|
-
console.error('========================================');
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// 运行
|
|
200
|
-
uploadClipboardImage();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 从剪贴板获取图片并上传到禅道
|
|
5
|
+
* 使用方法: node upload-clipboard-image.js
|
|
6
|
+
*
|
|
7
|
+
* 环境变量:
|
|
8
|
+
* - ZENTAO_URL: 禅道系统URL
|
|
9
|
+
* - ZENTAO_USERNAME: 用户名
|
|
10
|
+
* - ZENTAO_PASSWORD: 密码
|
|
11
|
+
* - ZENTAO_UID: 可选的UID参数
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import clipboardy from 'clipboardy';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { dirname } from 'path';
|
|
19
|
+
|
|
20
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
21
|
+
const __dirname = dirname(__filename);
|
|
22
|
+
|
|
23
|
+
// 动态导入 ZentaoAPI
|
|
24
|
+
async function uploadClipboardImage() {
|
|
25
|
+
try {
|
|
26
|
+
console.log('正在读取剪贴板内容...');
|
|
27
|
+
|
|
28
|
+
// 检查环境变量
|
|
29
|
+
const zentaoUrl = process.env.ZENTAO_URL;
|
|
30
|
+
const username = process.env.ZENTAO_USERNAME;
|
|
31
|
+
const password = process.env.ZENTAO_PASSWORD;
|
|
32
|
+
const uid = process.env.ZENTAO_UID;
|
|
33
|
+
|
|
34
|
+
if (!zentaoUrl || !username || !password) {
|
|
35
|
+
console.error('错误: 请设置环境变量 ZENTAO_URL, ZENTAO_USERNAME, ZENTAO_PASSWORD');
|
|
36
|
+
console.error('');
|
|
37
|
+
console.error('Windows示例:');
|
|
38
|
+
console.error(' set ZENTAO_URL=https://your-zentao-url');
|
|
39
|
+
console.error(' set ZENTAO_USERNAME=your-username');
|
|
40
|
+
console.error(' set ZENTAO_PASSWORD=your-password');
|
|
41
|
+
console.error(' node upload-clipboard-image.js');
|
|
42
|
+
console.error('');
|
|
43
|
+
console.error('Mac/Linux示例:');
|
|
44
|
+
console.error(' export ZENTAO_URL=https://your-zentao-url');
|
|
45
|
+
console.error(' export ZENTAO_USERNAME=your-username');
|
|
46
|
+
console.error(' export ZENTAO_PASSWORD=your-password');
|
|
47
|
+
console.error(' node upload-clipboard-image.js');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 尝试读取剪贴板图片
|
|
52
|
+
let imageData;
|
|
53
|
+
|
|
54
|
+
// Windows: 使用 PowerShell 读取剪贴板图片
|
|
55
|
+
if (process.platform === 'win32') {
|
|
56
|
+
const { execSync } = await import('child_process');
|
|
57
|
+
try {
|
|
58
|
+
const psScript = `
|
|
59
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
60
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
61
|
+
if ($img -ne $null) {
|
|
62
|
+
$ms = New-Object System.IO.MemoryStream
|
|
63
|
+
$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
64
|
+
[Convert]::ToBase64String($ms.ToArray())
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
const base64 = execSync(`powershell -Command "${psScript.replace(/\n/g, ' ')}"`, {
|
|
68
|
+
encoding: 'utf8',
|
|
69
|
+
maxBuffer: 10 * 1024 * 1024
|
|
70
|
+
}).trim();
|
|
71
|
+
|
|
72
|
+
if (base64) {
|
|
73
|
+
imageData = `data:image/png;base64,${base64}`;
|
|
74
|
+
console.log('✓ 从剪贴板读取图片成功 (Windows)');
|
|
75
|
+
} else {
|
|
76
|
+
console.error('错误: 剪贴板中没有图片');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('错误: 无法读取剪贴板图片', error.message);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// macOS: 使用 pngpaste
|
|
85
|
+
else if (process.platform === 'darwin') {
|
|
86
|
+
const { execSync } = await import('child_process');
|
|
87
|
+
try {
|
|
88
|
+
// 检查是否安装了 pngpaste
|
|
89
|
+
try {
|
|
90
|
+
execSync('which pngpaste', { stdio: 'ignore' });
|
|
91
|
+
} catch {
|
|
92
|
+
console.error('错误: 请先安装 pngpaste');
|
|
93
|
+
console.error('运行: brew install pngpaste');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const tempFile = path.join(__dirname, 'temp_clipboard.png');
|
|
98
|
+
execSync(`pngpaste "${tempFile}"`);
|
|
99
|
+
const buffer = fs.readFileSync(tempFile);
|
|
100
|
+
const base64 = buffer.toString('base64');
|
|
101
|
+
imageData = `data:image/png;base64,${base64}`;
|
|
102
|
+
fs.unlinkSync(tempFile);
|
|
103
|
+
console.log('✓ 从剪贴板读取图片成功 (macOS)');
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('错误: 剪贴板中没有图片或读取失败', error.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Linux: 使用 xclip
|
|
110
|
+
else {
|
|
111
|
+
const { execSync } = await import('child_process');
|
|
112
|
+
try {
|
|
113
|
+
// 检查是否安装了 xclip
|
|
114
|
+
try {
|
|
115
|
+
execSync('which xclip', { stdio: 'ignore' });
|
|
116
|
+
} catch {
|
|
117
|
+
console.error('错误: 请先安装 xclip');
|
|
118
|
+
console.error('运行: sudo apt-get install xclip (Ubuntu/Debian)');
|
|
119
|
+
console.error('或: sudo yum install xclip (CentOS/RHEL)');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const buffer = execSync('xclip -selection clipboard -t image/png -o', {
|
|
124
|
+
maxBuffer: 10 * 1024 * 1024
|
|
125
|
+
});
|
|
126
|
+
const base64 = buffer.toString('base64');
|
|
127
|
+
imageData = `data:image/png;base64,${base64}`;
|
|
128
|
+
console.log('✓ 从剪贴板读取图片成功 (Linux)');
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('错误: 剪贴板中没有图片或读取失败', error.message);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 导入 ZentaoAPI
|
|
136
|
+
const modulePath = path.join(__dirname, '..', 'dist', 'api', 'zentaoApi.js');
|
|
137
|
+
const { ZentaoAPI } = await import(modulePath);
|
|
138
|
+
|
|
139
|
+
// 创建 API 实例
|
|
140
|
+
console.log('正在连接禅道系统...');
|
|
141
|
+
const api = new ZentaoAPI({
|
|
142
|
+
url: zentaoUrl,
|
|
143
|
+
username: username,
|
|
144
|
+
password: password,
|
|
145
|
+
apiVersion: 'v1'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// 创建 img 文件夹
|
|
149
|
+
const imgDir = path.join(process.cwd(), 'img');
|
|
150
|
+
if (!fs.existsSync(imgDir)) {
|
|
151
|
+
fs.mkdirSync(imgDir, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 解析 base64 数据
|
|
155
|
+
const matches = imageData.match(/^data:image\/(\w+);base64,(.+)$/);
|
|
156
|
+
if (!matches) {
|
|
157
|
+
console.error('错误: 无效的图片数据');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const ext = matches[1];
|
|
162
|
+
const base64Data = matches[2];
|
|
163
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
164
|
+
const filename = `clipboard_${Date.now()}.${ext}`;
|
|
165
|
+
|
|
166
|
+
// 保存到本地
|
|
167
|
+
const savedPath = path.join(imgDir, filename);
|
|
168
|
+
fs.writeFileSync(savedPath, buffer);
|
|
169
|
+
console.log(`✓ 图片已保存到: ${savedPath}`);
|
|
170
|
+
|
|
171
|
+
// 上传到禅道
|
|
172
|
+
console.log('正在上传到禅道...');
|
|
173
|
+
const result = await api.uploadFile({
|
|
174
|
+
file: buffer,
|
|
175
|
+
filename: filename,
|
|
176
|
+
uid: uid
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
console.log('');
|
|
180
|
+
console.log('========================================');
|
|
181
|
+
console.log('✓ 上传成功!');
|
|
182
|
+
console.log('========================================');
|
|
183
|
+
console.log('文件ID:', result.id);
|
|
184
|
+
console.log('访问URL:', result.url);
|
|
185
|
+
console.log('本地路径:', savedPath);
|
|
186
|
+
console.log('========================================');
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error('');
|
|
190
|
+
console.error('========================================');
|
|
191
|
+
console.error('✗ 上传失败');
|
|
192
|
+
console.error('========================================');
|
|
193
|
+
console.error('错误信息:', error.message);
|
|
194
|
+
console.error('========================================');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 运行
|
|
200
|
+
uploadClipboardImage();
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
# 从剪贴板获取图片并上传到禅道
|
|
2
|
-
# 使用方法: .\upload-clipboard-image.ps1
|
|
3
|
-
#
|
|
4
|
-
# 参数:
|
|
5
|
-
# -ZentaoUrl: 禅道系统URL
|
|
6
|
-
# -Username: 用户名
|
|
7
|
-
# -Password: 密码
|
|
8
|
-
# -Uid: 可选的UID参数
|
|
9
|
-
|
|
10
|
-
param(
|
|
11
|
-
[Parameter(Mandatory=$true)]
|
|
12
|
-
[string]$ZentaoUrl,
|
|
13
|
-
|
|
14
|
-
[Parameter(Mandatory=$true)]
|
|
15
|
-
[string]$Username,
|
|
16
|
-
|
|
17
|
-
[Parameter(Mandatory=$true)]
|
|
18
|
-
[string]$Password,
|
|
19
|
-
|
|
20
|
-
[Parameter(Mandatory=$false)]
|
|
21
|
-
[string]$Uid = ""
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
# 添加必要的程序集
|
|
25
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
26
|
-
Add-Type -AssemblyName System.Drawing
|
|
27
|
-
|
|
28
|
-
Write-Host "正在读取剪贴板图片..." -ForegroundColor Cyan
|
|
29
|
-
|
|
30
|
-
# 从剪贴板获取图片
|
|
31
|
-
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
32
|
-
|
|
33
|
-
if ($img -eq $null) {
|
|
34
|
-
Write-Host "错误: 剪贴板中没有图片" -ForegroundColor Red
|
|
35
|
-
exit 1
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
Write-Host "✓ 成功读取剪贴板图片 ($($img.Width)x$($img.Height))" -ForegroundColor Green
|
|
39
|
-
|
|
40
|
-
# 转换为 base64
|
|
41
|
-
$ms = New-Object System.IO.MemoryStream
|
|
42
|
-
$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
43
|
-
$base64 = [Convert]::ToBase64String($ms.ToArray())
|
|
44
|
-
$ms.Close()
|
|
45
|
-
|
|
46
|
-
Write-Host "✓ 图片已转换为 base64" -ForegroundColor Green
|
|
47
|
-
|
|
48
|
-
# 创建 img 文件夹
|
|
49
|
-
$imgDir = Join-Path (Get-Location) "img"
|
|
50
|
-
if (-not (Test-Path $imgDir)) {
|
|
51
|
-
New-Item -ItemType Directory -Path $imgDir | Out-Null
|
|
52
|
-
Write-Host "✓ 创建 img 文件夹: $imgDir" -ForegroundColor Green
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# 保存到本地
|
|
56
|
-
$filename = "clipboard_$([DateTimeOffset]::Now.ToUnixTimeMilliseconds()).png"
|
|
57
|
-
$localPath = Join-Path $imgDir $filename
|
|
58
|
-
$bytes = [Convert]::FromBase64String($base64)
|
|
59
|
-
[System.IO.File]::WriteAllBytes($localPath, $bytes)
|
|
60
|
-
Write-Host "✓ 图片已保存到: $localPath" -ForegroundColor Green
|
|
61
|
-
|
|
62
|
-
# 获取 Token
|
|
63
|
-
Write-Host "正在连接禅道系统..." -ForegroundColor Cyan
|
|
64
|
-
$passwordMd5 = [System.BitConverter]::ToString(
|
|
65
|
-
[System.Security.Cryptography.MD5]::Create().ComputeHash(
|
|
66
|
-
[System.Text.Encoding]::UTF8.GetBytes($Password)
|
|
67
|
-
)
|
|
68
|
-
).Replace("-", "").ToLower()
|
|
69
|
-
|
|
70
|
-
$tokenUrl = "$ZentaoUrl/zentao/api.php/v1/tokens"
|
|
71
|
-
$tokenBody = @{
|
|
72
|
-
account = $Username
|
|
73
|
-
password = $passwordMd5
|
|
74
|
-
} | ConvertTo-Json
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
$tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $tokenBody -ContentType "application/json"
|
|
78
|
-
$token = $tokenResponse.token
|
|
79
|
-
Write-Host "✓ 成功获取 Token" -ForegroundColor Green
|
|
80
|
-
} catch {
|
|
81
|
-
Write-Host "错误: 无法获取 Token - $($_.Exception.Message)" -ForegroundColor Red
|
|
82
|
-
exit 1
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# 上传文件
|
|
86
|
-
Write-Host "正在上传到禅道..." -ForegroundColor Cyan
|
|
87
|
-
|
|
88
|
-
$uploadUrl = "$ZentaoUrl/zentao/api.php/v1/files"
|
|
89
|
-
if ($Uid) {
|
|
90
|
-
$uploadUrl += "?uid=$Uid"
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
# 创建 multipart/form-data
|
|
94
|
-
$boundary = [System.Guid]::NewGuid().ToString()
|
|
95
|
-
$LF = "`r`n"
|
|
96
|
-
|
|
97
|
-
$bodyLines = (
|
|
98
|
-
"--$boundary",
|
|
99
|
-
"Content-Disposition: form-data; name=`"imgFile`"; filename=`"$filename`"",
|
|
100
|
-
"Content-Type: image/png$LF",
|
|
101
|
-
[System.Text.Encoding]::GetEncoding("iso-8859-1").GetString($bytes),
|
|
102
|
-
"--$boundary--$LF"
|
|
103
|
-
) -join $LF
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
$response = Invoke-RestMethod -Uri $uploadUrl -Method Post `
|
|
107
|
-
-Headers @{ Token = $token } `
|
|
108
|
-
-ContentType "multipart/form-data; boundary=$boundary" `
|
|
109
|
-
-Body $bodyLines
|
|
110
|
-
|
|
111
|
-
Write-Host ""
|
|
112
|
-
Write-Host "========================================" -ForegroundColor Green
|
|
113
|
-
Write-Host "✓ 上传成功!" -ForegroundColor Green
|
|
114
|
-
Write-Host "========================================" -ForegroundColor Green
|
|
115
|
-
Write-Host "文件ID: $($response.id)"
|
|
116
|
-
Write-Host "访问URL: $($response.url)"
|
|
117
|
-
Write-Host "本地路径: $localPath"
|
|
118
|
-
Write-Host "========================================" -ForegroundColor Green
|
|
119
|
-
|
|
120
|
-
} catch {
|
|
121
|
-
Write-Host ""
|
|
122
|
-
Write-Host "========================================" -ForegroundColor Red
|
|
123
|
-
Write-Host "✗ 上传失败" -ForegroundColor Red
|
|
124
|
-
Write-Host "========================================" -ForegroundColor Red
|
|
125
|
-
Write-Host "错误信息: $($_.Exception.Message)" -ForegroundColor Red
|
|
126
|
-
Write-Host "========================================" -ForegroundColor Red
|
|
127
|
-
exit 1
|
|
128
|
-
}
|
|
1
|
+
# 从剪贴板获取图片并上传到禅道
|
|
2
|
+
# 使用方法: .\upload-clipboard-image.ps1
|
|
3
|
+
#
|
|
4
|
+
# 参数:
|
|
5
|
+
# -ZentaoUrl: 禅道系统URL
|
|
6
|
+
# -Username: 用户名
|
|
7
|
+
# -Password: 密码
|
|
8
|
+
# -Uid: 可选的UID参数
|
|
9
|
+
|
|
10
|
+
param(
|
|
11
|
+
[Parameter(Mandatory=$true)]
|
|
12
|
+
[string]$ZentaoUrl,
|
|
13
|
+
|
|
14
|
+
[Parameter(Mandatory=$true)]
|
|
15
|
+
[string]$Username,
|
|
16
|
+
|
|
17
|
+
[Parameter(Mandatory=$true)]
|
|
18
|
+
[string]$Password,
|
|
19
|
+
|
|
20
|
+
[Parameter(Mandatory=$false)]
|
|
21
|
+
[string]$Uid = ""
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# 添加必要的程序集
|
|
25
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
26
|
+
Add-Type -AssemblyName System.Drawing
|
|
27
|
+
|
|
28
|
+
Write-Host "正在读取剪贴板图片..." -ForegroundColor Cyan
|
|
29
|
+
|
|
30
|
+
# 从剪贴板获取图片
|
|
31
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
32
|
+
|
|
33
|
+
if ($img -eq $null) {
|
|
34
|
+
Write-Host "错误: 剪贴板中没有图片" -ForegroundColor Red
|
|
35
|
+
exit 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Write-Host "✓ 成功读取剪贴板图片 ($($img.Width)x$($img.Height))" -ForegroundColor Green
|
|
39
|
+
|
|
40
|
+
# 转换为 base64
|
|
41
|
+
$ms = New-Object System.IO.MemoryStream
|
|
42
|
+
$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
43
|
+
$base64 = [Convert]::ToBase64String($ms.ToArray())
|
|
44
|
+
$ms.Close()
|
|
45
|
+
|
|
46
|
+
Write-Host "✓ 图片已转换为 base64" -ForegroundColor Green
|
|
47
|
+
|
|
48
|
+
# 创建 img 文件夹
|
|
49
|
+
$imgDir = Join-Path (Get-Location) "img"
|
|
50
|
+
if (-not (Test-Path $imgDir)) {
|
|
51
|
+
New-Item -ItemType Directory -Path $imgDir | Out-Null
|
|
52
|
+
Write-Host "✓ 创建 img 文件夹: $imgDir" -ForegroundColor Green
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# 保存到本地
|
|
56
|
+
$filename = "clipboard_$([DateTimeOffset]::Now.ToUnixTimeMilliseconds()).png"
|
|
57
|
+
$localPath = Join-Path $imgDir $filename
|
|
58
|
+
$bytes = [Convert]::FromBase64String($base64)
|
|
59
|
+
[System.IO.File]::WriteAllBytes($localPath, $bytes)
|
|
60
|
+
Write-Host "✓ 图片已保存到: $localPath" -ForegroundColor Green
|
|
61
|
+
|
|
62
|
+
# 获取 Token
|
|
63
|
+
Write-Host "正在连接禅道系统..." -ForegroundColor Cyan
|
|
64
|
+
$passwordMd5 = [System.BitConverter]::ToString(
|
|
65
|
+
[System.Security.Cryptography.MD5]::Create().ComputeHash(
|
|
66
|
+
[System.Text.Encoding]::UTF8.GetBytes($Password)
|
|
67
|
+
)
|
|
68
|
+
).Replace("-", "").ToLower()
|
|
69
|
+
|
|
70
|
+
$tokenUrl = "$ZentaoUrl/zentao/api.php/v1/tokens"
|
|
71
|
+
$tokenBody = @{
|
|
72
|
+
account = $Username
|
|
73
|
+
password = $passwordMd5
|
|
74
|
+
} | ConvertTo-Json
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
$tokenResponse = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $tokenBody -ContentType "application/json"
|
|
78
|
+
$token = $tokenResponse.token
|
|
79
|
+
Write-Host "✓ 成功获取 Token" -ForegroundColor Green
|
|
80
|
+
} catch {
|
|
81
|
+
Write-Host "错误: 无法获取 Token - $($_.Exception.Message)" -ForegroundColor Red
|
|
82
|
+
exit 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# 上传文件
|
|
86
|
+
Write-Host "正在上传到禅道..." -ForegroundColor Cyan
|
|
87
|
+
|
|
88
|
+
$uploadUrl = "$ZentaoUrl/zentao/api.php/v1/files"
|
|
89
|
+
if ($Uid) {
|
|
90
|
+
$uploadUrl += "?uid=$Uid"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# 创建 multipart/form-data
|
|
94
|
+
$boundary = [System.Guid]::NewGuid().ToString()
|
|
95
|
+
$LF = "`r`n"
|
|
96
|
+
|
|
97
|
+
$bodyLines = (
|
|
98
|
+
"--$boundary",
|
|
99
|
+
"Content-Disposition: form-data; name=`"imgFile`"; filename=`"$filename`"",
|
|
100
|
+
"Content-Type: image/png$LF",
|
|
101
|
+
[System.Text.Encoding]::GetEncoding("iso-8859-1").GetString($bytes),
|
|
102
|
+
"--$boundary--$LF"
|
|
103
|
+
) -join $LF
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
$response = Invoke-RestMethod -Uri $uploadUrl -Method Post `
|
|
107
|
+
-Headers @{ Token = $token } `
|
|
108
|
+
-ContentType "multipart/form-data; boundary=$boundary" `
|
|
109
|
+
-Body $bodyLines
|
|
110
|
+
|
|
111
|
+
Write-Host ""
|
|
112
|
+
Write-Host "========================================" -ForegroundColor Green
|
|
113
|
+
Write-Host "✓ 上传成功!" -ForegroundColor Green
|
|
114
|
+
Write-Host "========================================" -ForegroundColor Green
|
|
115
|
+
Write-Host "文件ID: $($response.id)"
|
|
116
|
+
Write-Host "访问URL: $($response.url)"
|
|
117
|
+
Write-Host "本地路径: $localPath"
|
|
118
|
+
Write-Host "========================================" -ForegroundColor Green
|
|
119
|
+
|
|
120
|
+
} catch {
|
|
121
|
+
Write-Host ""
|
|
122
|
+
Write-Host "========================================" -ForegroundColor Red
|
|
123
|
+
Write-Host "✗ 上传失败" -ForegroundColor Red
|
|
124
|
+
Write-Host "========================================" -ForegroundColor Red
|
|
125
|
+
Write-Host "错误信息: $($_.Exception.Message)" -ForegroundColor Red
|
|
126
|
+
Write-Host "========================================" -ForegroundColor Red
|
|
127
|
+
exit 1
|
|
128
|
+
}
|