long-git-cli 1.0.16 → 1.0.20
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/dist/commands/open-account.d.ts +6 -0
- package/dist/commands/open-account.d.ts.map +1 -0
- package/dist/commands/open-account.js +151 -0
- package/dist/commands/open-account.js.map +1 -0
- package/dist/devops/ui/server.d.ts.map +1 -1
- package/dist/devops/ui/server.js +6 -1
- package/dist/devops/ui/server.js.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/oa-config-manager.d.ts +28 -0
- package/dist/utils/oa-config-manager.d.ts.map +1 -0
- package/dist/utils/oa-config-manager.js +108 -0
- package/dist/utils/oa-config-manager.js.map +1 -0
- package/dist/utils/open-account.d.ts +8 -0
- package/dist/utils/open-account.d.ts.map +1 -0
- package/dist/utils/open-account.js +298 -0
- package/dist/utils/open-account.js.map +1 -0
- package/dist/utils/slider-captcha.d.ts +9 -0
- package/dist/utils/slider-captcha.d.ts.map +1 -0
- package/dist/utils/slider-captcha.js +202 -0
- package/dist/utils/slider-captcha.js.map +1 -0
- package/package.json +5 -1
- package/src/devops/ui/public/css/components.css +397 -0
- package/src/devops/ui/public/css/main.css +381 -0
- package/src/devops/ui/public/index.html +489 -0
- package/src/devops/ui/public/js/api.js +116 -0
- package/src/devops/ui/public/js/app.js +104 -0
- package/src/devops/ui/public/js/components.js +267 -0
- package/src/devops/ui/public/js/config-page.js +339 -0
- package/src/devops/ui/public/js/deploy-page.js +158 -0
- package/src/devops/ui/public/js/full-deploy-page.js +247 -0
- package/src/devops/ui/public/js/pipeline-page.js +364 -0
- package/src/devops/ui/public/js/projects-page.js +659 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jenkins 部署测试页面逻辑
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const DeployPage = {
|
|
6
|
+
/**
|
|
7
|
+
* 初始化部署页面
|
|
8
|
+
*/
|
|
9
|
+
async init() {
|
|
10
|
+
/** 加载保存的配置 */
|
|
11
|
+
await this.loadConfig();
|
|
12
|
+
|
|
13
|
+
/** 绑定事件 */
|
|
14
|
+
this.bindEvents();
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 加载保存的配置
|
|
19
|
+
*/
|
|
20
|
+
async loadConfig() {
|
|
21
|
+
try {
|
|
22
|
+
const config = localStorage.getItem('jenkins-deploy-config');
|
|
23
|
+
if (config) {
|
|
24
|
+
const data = JSON.parse(config);
|
|
25
|
+
document.getElementById('deploy-jenkins-url').value = data.url || '';
|
|
26
|
+
document.getElementById('deploy-jenkins-username').value = data.username || '';
|
|
27
|
+
document.getElementById('deploy-jenkins-token').value = data.token || '';
|
|
28
|
+
document.getElementById('deploy-job-name').value = data.jobName || '';
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error('加载配置失败:', error);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 保存配置
|
|
37
|
+
*/
|
|
38
|
+
saveConfig() {
|
|
39
|
+
try {
|
|
40
|
+
const config = {
|
|
41
|
+
url: document.getElementById('deploy-jenkins-url').value.trim(),
|
|
42
|
+
username: document.getElementById('deploy-jenkins-username').value.trim(),
|
|
43
|
+
token: document.getElementById('deploy-jenkins-token').value.trim(),
|
|
44
|
+
jobName: document.getElementById('deploy-job-name').value.trim(),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
localStorage.setItem('jenkins-deploy-config', JSON.stringify(config));
|
|
48
|
+
Components.Toast.success('配置已保存');
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('保存配置失败:', error);
|
|
51
|
+
Components.Toast.error('保存配置失败: ' + error.message);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 绑定事件
|
|
57
|
+
*/
|
|
58
|
+
bindEvents() {
|
|
59
|
+
/** 保存配置按钮 */
|
|
60
|
+
document.getElementById('save-deploy-config-btn')?.addEventListener('click', () => {
|
|
61
|
+
this.saveConfig();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/** 部署按钮 */
|
|
65
|
+
document.getElementById('deploy-btn')?.addEventListener('click', () => {
|
|
66
|
+
this.triggerDeploy();
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 添加日志
|
|
72
|
+
*/
|
|
73
|
+
addLog(message, type = 'info') {
|
|
74
|
+
const logContainer = document.getElementById('deploy-logs');
|
|
75
|
+
if (!logContainer) return;
|
|
76
|
+
|
|
77
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
78
|
+
const logEntry = document.createElement('div');
|
|
79
|
+
logEntry.style.cssText = 'padding: 0.25rem 0; font-family: monospace; font-size: 0.875rem;';
|
|
80
|
+
|
|
81
|
+
const colors = {
|
|
82
|
+
info: 'var(--text-secondary)',
|
|
83
|
+
success: 'var(--success-color)',
|
|
84
|
+
error: 'var(--error-color)',
|
|
85
|
+
warning: '#f59e0b',
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
logEntry.innerHTML = `<span style="color: var(--text-secondary);">[${timestamp}]</span> <span style="color: ${colors[type]};">${message}</span>`;
|
|
89
|
+
logContainer.appendChild(logEntry);
|
|
90
|
+
logContainer.scrollTop = logContainer.scrollHeight;
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 清空日志
|
|
95
|
+
*/
|
|
96
|
+
clearLogs() {
|
|
97
|
+
const logContainer = document.getElementById('deploy-logs');
|
|
98
|
+
if (logContainer) {
|
|
99
|
+
logContainer.innerHTML = '';
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 触发部署
|
|
105
|
+
*/
|
|
106
|
+
async triggerDeploy() {
|
|
107
|
+
try {
|
|
108
|
+
const url = document.getElementById('deploy-jenkins-url').value.trim();
|
|
109
|
+
const username = document.getElementById('deploy-jenkins-username').value.trim();
|
|
110
|
+
const token = document.getElementById('deploy-jenkins-token').value.trim();
|
|
111
|
+
const jobName = document.getElementById('deploy-job-name').value.trim();
|
|
112
|
+
|
|
113
|
+
if (!url || !username || !token || !jobName) {
|
|
114
|
+
Components.Toast.error('请填写所有必填字段');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** 显示日志容器 */
|
|
119
|
+
const statusDiv = document.getElementById('deploy-status');
|
|
120
|
+
statusDiv.style.display = 'block';
|
|
121
|
+
statusDiv.innerHTML = `
|
|
122
|
+
<div style="padding: 1rem; background-color: #1e1e1e; border-radius: var(--radius-md); border: 1px solid var(--border-color);">
|
|
123
|
+
<div style="color: #4ade80; font-size: 0.875rem; margin-bottom: 0.5rem; font-weight: 600;">
|
|
124
|
+
📋 Jenkins 部署日志
|
|
125
|
+
</div>
|
|
126
|
+
<div id="deploy-logs" style="max-height: 400px; overflow-y: auto; color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
this.clearLogs();
|
|
132
|
+
this.addLog('开始触发 Jenkins 部署...', 'info');
|
|
133
|
+
this.addLog(`Jenkins: ${url}`, 'info');
|
|
134
|
+
this.addLog(`Job: ${jobName}`, 'info');
|
|
135
|
+
this.addLog(`参数: action=approve`, 'info');
|
|
136
|
+
|
|
137
|
+
const response = await API.post('/test-jenkins-deploy', {
|
|
138
|
+
url,
|
|
139
|
+
username,
|
|
140
|
+
token,
|
|
141
|
+
jobName,
|
|
142
|
+
parameters: {
|
|
143
|
+
action: 'approve',
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
this.addLog(`构建触发成功`, 'success');
|
|
148
|
+
this.addLog(`构建编号: #${response.buildNumber}`, 'success');
|
|
149
|
+
this.addLog(`构建结果: ${response.result}`, 'success');
|
|
150
|
+
this.addLog(`构建 URL: ${response.url}`, 'success');
|
|
151
|
+
|
|
152
|
+
Components.Toast.success('部署成功!');
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.addLog(`错误: ${error.message}`, 'error');
|
|
155
|
+
Components.Toast.error('部署失败: ' + error.message);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 完整部署流程页面逻辑
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const FullDeployPage = {
|
|
6
|
+
projects: {},
|
|
7
|
+
selectedProject: null,
|
|
8
|
+
selectedEnvironment: null,
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 初始化完整部署页面
|
|
12
|
+
*/
|
|
13
|
+
async init() {
|
|
14
|
+
/** 加载项目列表 */
|
|
15
|
+
await this.loadProjects();
|
|
16
|
+
|
|
17
|
+
/** 绑定事件 */
|
|
18
|
+
this.bindEvents();
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 加载项目列表
|
|
23
|
+
*/
|
|
24
|
+
async loadProjects() {
|
|
25
|
+
try {
|
|
26
|
+
const data = await API.get("/projects");
|
|
27
|
+
this.projects = data.projects || {};
|
|
28
|
+
this.renderProjectSelect();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
Components.Toast.error("加载项目列表失败: " + error.message);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 渲染项目选择下拉框
|
|
36
|
+
*/
|
|
37
|
+
renderProjectSelect() {
|
|
38
|
+
const select = document.getElementById("full-deploy-project");
|
|
39
|
+
if (!select) return;
|
|
40
|
+
|
|
41
|
+
const projectPaths = Object.keys(this.projects);
|
|
42
|
+
|
|
43
|
+
if (projectPaths.length === 0) {
|
|
44
|
+
select.innerHTML = '<option value="">暂无项目,请先添加项目</option>';
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
select.innerHTML = '<option value="">请选择项目</option>';
|
|
49
|
+
projectPaths.forEach((projectPath) => {
|
|
50
|
+
const project = this.projects[projectPath];
|
|
51
|
+
const option = document.createElement("option");
|
|
52
|
+
option.value = projectPath;
|
|
53
|
+
option.textContent = project.name;
|
|
54
|
+
select.appendChild(option);
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 渲染环境选择下拉框
|
|
60
|
+
*/
|
|
61
|
+
renderEnvironmentSelect(projectPath) {
|
|
62
|
+
const select = document.getElementById("full-deploy-environment");
|
|
63
|
+
if (!select) return;
|
|
64
|
+
|
|
65
|
+
const project = this.projects[projectPath];
|
|
66
|
+
if (!project) {
|
|
67
|
+
select.innerHTML = '<option value="">请先选择项目</option>';
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const environments = Object.keys(project.environments || {});
|
|
72
|
+
|
|
73
|
+
if (environments.length === 0) {
|
|
74
|
+
select.innerHTML = '<option value="">该项目暂无环境配置</option>';
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
select.innerHTML = '<option value="">请选择环境</option>';
|
|
79
|
+
environments.forEach((env) => {
|
|
80
|
+
const option = document.createElement("option");
|
|
81
|
+
option.value = env;
|
|
82
|
+
option.textContent = env;
|
|
83
|
+
select.appendChild(option);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 绑定事件
|
|
89
|
+
*/
|
|
90
|
+
bindEvents() {
|
|
91
|
+
/** 项目选择变化 */
|
|
92
|
+
document
|
|
93
|
+
.getElementById("full-deploy-project")
|
|
94
|
+
?.addEventListener("change", (e) => {
|
|
95
|
+
const projectPath = e.target.value;
|
|
96
|
+
this.selectedProject = projectPath;
|
|
97
|
+
this.renderEnvironmentSelect(projectPath);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/** 环境选择变化 */
|
|
101
|
+
document
|
|
102
|
+
.getElementById("full-deploy-environment")
|
|
103
|
+
?.addEventListener("change", (e) => {
|
|
104
|
+
this.selectedEnvironment = e.target.value;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** 部署按钮 */
|
|
108
|
+
document
|
|
109
|
+
.getElementById("full-deploy-btn")
|
|
110
|
+
?.addEventListener("click", () => {
|
|
111
|
+
this.startDeploy();
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 开始部署
|
|
117
|
+
*/
|
|
118
|
+
async startDeploy() {
|
|
119
|
+
try {
|
|
120
|
+
const projectPath = this.selectedProject;
|
|
121
|
+
const environment = this.selectedEnvironment;
|
|
122
|
+
|
|
123
|
+
if (!projectPath || !environment) {
|
|
124
|
+
Components.Toast.error("请选择项目和环境");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const statusDiv = document.getElementById("full-deploy-status");
|
|
129
|
+
statusDiv.style.display = "block";
|
|
130
|
+
statusDiv.innerHTML = `
|
|
131
|
+
<div style="padding: 1rem; background-color: #1e1e1e; border-radius: var(--radius-md); border: 1px solid var(--border-color);">
|
|
132
|
+
<div style="color: #4ade80; font-size: 0.875rem; margin-bottom: 0.5rem; font-weight: 600;">
|
|
133
|
+
📋 一键部署执行日志
|
|
134
|
+
</div>
|
|
135
|
+
<div id="full-deploy-logs" style="max-height: 400px; overflow-y: auto; color: var(--text-secondary); font-family: monospace; font-size: 0.875rem;">
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
this.clearLogs();
|
|
141
|
+
|
|
142
|
+
// 使用 EventSource 接收 SSE
|
|
143
|
+
const params = new URLSearchParams({
|
|
144
|
+
projectPath,
|
|
145
|
+
environment,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const eventSource = new EventSource(`/api/full-deploy-stream?${params}`);
|
|
149
|
+
|
|
150
|
+
eventSource.addEventListener('log', (e) => {
|
|
151
|
+
const data = JSON.parse(e.data);
|
|
152
|
+
this.addLog(data.message, data.type || 'info');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
eventSource.addEventListener('error', (e) => {
|
|
156
|
+
try {
|
|
157
|
+
const data = JSON.parse(e.data);
|
|
158
|
+
this.addLog(`错误: ${data.message}`, 'error');
|
|
159
|
+
Components.Toast.error('部署失败');
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.error('解析错误消息失败:', err);
|
|
162
|
+
}
|
|
163
|
+
eventSource.close();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
eventSource.addEventListener('complete', (e) => {
|
|
167
|
+
const data = JSON.parse(e.data);
|
|
168
|
+
this.addLog(`${data.message}`, 'success');
|
|
169
|
+
this.addLog(`Tag: ${data.tagName}`, 'info');
|
|
170
|
+
this.addLog(`Jenkins 构建: #${data.jenkinsBuildNumber}`, 'info');
|
|
171
|
+
this.addLog(`构建 URL: ${data.jenkinsBuildUrl}`, 'info');
|
|
172
|
+
Components.Toast.success('部署成功!');
|
|
173
|
+
eventSource.close();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
eventSource.onerror = (error) => {
|
|
177
|
+
console.error('SSE 连接错误:', error);
|
|
178
|
+
this.addLog('连接中断或服务器错误', 'error');
|
|
179
|
+
Components.Toast.error('连接失败');
|
|
180
|
+
eventSource.close();
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.addLog(`错误: ${error.message}`, 'error');
|
|
185
|
+
Components.Toast.error('部署失败: ' + error.message);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 添加日志
|
|
191
|
+
*/
|
|
192
|
+
addLog(message, type = 'info') {
|
|
193
|
+
const logContainer = document.getElementById('full-deploy-logs');
|
|
194
|
+
if (!logContainer) return;
|
|
195
|
+
|
|
196
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
197
|
+
const logEntry = document.createElement('div');
|
|
198
|
+
logEntry.style.cssText = 'padding: 0.25rem 0; font-family: monospace; font-size: 0.875rem;';
|
|
199
|
+
|
|
200
|
+
const colors = {
|
|
201
|
+
info: 'var(--text-secondary)',
|
|
202
|
+
success: 'var(--success-color)',
|
|
203
|
+
error: 'var(--error-color)',
|
|
204
|
+
warning: '#f59e0b',
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
logEntry.innerHTML = `<span style="color: var(--text-secondary);">[${timestamp}]</span> <span style="color: ${colors[type]};">${message}</span>`;
|
|
208
|
+
logContainer.appendChild(logEntry);
|
|
209
|
+
logContainer.scrollTop = logContainer.scrollHeight;
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 清空日志
|
|
214
|
+
*/
|
|
215
|
+
clearLogs() {
|
|
216
|
+
const logContainer = document.getElementById('full-deploy-logs');
|
|
217
|
+
if (logContainer) {
|
|
218
|
+
logContainer.innerHTML = '';
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 创建进度 HTML
|
|
224
|
+
*/
|
|
225
|
+
createProgressHtml(currentStep, steps) {
|
|
226
|
+
return `
|
|
227
|
+
<div style="padding: 1rem; background-color: var(--bg-secondary); border-radius: var(--radius-md); border: 1px solid var(--border-color);">
|
|
228
|
+
<div style="color: var(--text-primary); font-size: 0.875rem; margin-bottom: 0.5rem;">
|
|
229
|
+
<strong>${currentStep}</strong>
|
|
230
|
+
</div>
|
|
231
|
+
${
|
|
232
|
+
steps.length > 0
|
|
233
|
+
? `
|
|
234
|
+
<div style="margin-top: 1rem;">
|
|
235
|
+
${steps.map((step) => `<div style="color: var(--text-secondary); font-size: 0.75rem; margin-bottom: 0.25rem;">✓ ${step}</div>`).join("")}
|
|
236
|
+
</div>
|
|
237
|
+
`
|
|
238
|
+
: ""
|
|
239
|
+
}
|
|
240
|
+
<div style="margin-top: 0.75rem;">
|
|
241
|
+
<div class="loading-spinner"></div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
`;
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|