vk-ssl-auto-deploy 0.8.6 → 0.8.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vk-ssl-auto-deploy",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "SSL证书自动部署工具 - 提供HTTP API接口,支持证书文件自动上传和部署",
5
5
  "main": "app.js",
6
6
  "scripts": {
package/routes/admin.js CHANGED
@@ -466,44 +466,36 @@ router.post('/check-update', verifyPassword, async (req, res) => {
466
466
  }
467
467
  });
468
468
 
469
- // 执行更新
469
+ // 获取更新命令
470
470
  router.post('/execute-update', verifyPassword, async (req, res) => {
471
471
  try {
472
472
  const platform = process.platform;
473
- let command;
473
+ let command, instructions;
474
474
 
475
475
  if (platform === 'win32') {
476
476
  // Windows 系统
477
- command = 'powershell -c "iwr -useb https://gitee.com/vk1688/vk-ssl-auto-deploy/raw/master/install.ps1 | iex"';
477
+ command = 'powershell -ExecutionPolicy Bypass -Command "iwr -useb https://gitee.com/vk1688/vk-ssl-auto-deploy/raw/master/install.ps1 | iex"';
478
+ instructions = '请以管理员身份打开 PowerShell,然后执行以下命令:';
478
479
  } else {
479
480
  // Linux/Unix 系统
480
481
  command = 'curl -fsSL https://gitee.com/vk1688/vk-ssl-auto-deploy/raw/master/install.sh | sudo bash';
482
+ instructions = '请在服务器终端中执行以下命令:';
481
483
  }
482
484
 
483
- // 异步执行更新命令
484
- exec(command, (error, stdout, stderr) => {
485
- if (error) {
486
- console.error('更新失败:', error);
487
- return;
488
- }
489
- console.log('更新输出:', stdout);
490
- if (stderr) {
491
- console.error('更新错误:', stderr);
492
- }
493
- });
494
-
495
485
  res.json({
496
486
  code: 0,
497
- msg: '更新命令已执行,程序将在更新完成后自动重启',
487
+ msg: '请手动在服务器上执行更新命令',
498
488
  data: {
499
- platform,
500
- command
489
+ platform: platform === 'win32' ? 'Windows' : 'Linux/Unix',
490
+ instructions,
491
+ command,
492
+ note: '更新完成后程序将自动重启'
501
493
  }
502
494
  });
503
495
  } catch (error) {
504
496
  res.status(500).json({
505
497
  code: 500,
506
- msg: '执行更新失败: ' + error.message,
498
+ msg: '获取更新命令失败: ' + error.message,
507
499
  data: null
508
500
  });
509
501
  }
package/views/admin.ejs CHANGED
@@ -594,6 +594,28 @@
594
594
  }
595
595
  }
596
596
 
597
+ @keyframes slideIn {
598
+ from {
599
+ opacity: 0;
600
+ transform: translateX(100px);
601
+ }
602
+ to {
603
+ opacity: 1;
604
+ transform: translateX(0);
605
+ }
606
+ }
607
+
608
+ @keyframes slideOut {
609
+ from {
610
+ opacity: 1;
611
+ transform: translateX(0);
612
+ }
613
+ to {
614
+ opacity: 0;
615
+ transform: translateX(100px);
616
+ }
617
+ }
618
+
597
619
  .dialog-title {
598
620
  font-size: 1.5em;
599
621
  color: #00ff88;
@@ -1261,14 +1283,8 @@
1261
1283
 
1262
1284
  if (!silent) {
1263
1285
  addLog(`✓ 发现新版本: ${remoteVersion} (当前版本: ${currentVersion})`, 'success');
1264
- const confirmed = await showConfirm(
1265
- `发现新版本 ${remoteVersion}`,
1266
- `当前版本: ${currentVersion}\n新版本: ${remoteVersion}\n\n确定要立即更新吗?更新完成后程序将自动重启。`
1267
- );
1268
-
1269
- if (confirmed) {
1270
- await performUpdate();
1271
- }
1286
+ // 直接显示更新命令弹窗
1287
+ await performUpdate(currentVersion, remoteVersion);
1272
1288
  }
1273
1289
  } else {
1274
1290
  // 移除红点(如果有)
@@ -1302,8 +1318,8 @@
1302
1318
  }
1303
1319
 
1304
1320
  // 执行更新
1305
- async function performUpdate() {
1306
- addLog('→ 开始执行更新...', 'info');
1321
+ async function performUpdate(currentVersion = '', remoteVersion = '') {
1322
+ addLog('→ 获取更新命令...', 'info');
1307
1323
 
1308
1324
  try {
1309
1325
  const response = await authenticatedFetch('/admin/execute-update', {
@@ -1312,21 +1328,149 @@
1312
1328
  const result = await response.json();
1313
1329
 
1314
1330
  if (result.code === 0) {
1315
- addLog('✓ 更新命令已执行,程序将在更新完成后自动重启', 'success');
1316
- addLog('→ 请稍等片刻,更新完成后刷新页面即可', 'info');
1317
- showAlert('更新中', '更新命令已执行,程序将在更新完成后自动重启。请稍等片刻后刷新页面。', 'success');
1331
+ const { platform, instructions, command, note } = result.data;
1332
+
1333
+ addLog(' 已获取更新命令', 'success');
1334
+
1335
+ // 显示更新命令弹窗
1336
+ showUpdateCommandDialog(instructions, command, note, currentVersion, remoteVersion);
1318
1337
  } else {
1319
- addLog('✗ 执行更新失败: ' + result.msg, 'error');
1320
- showAlert('执行更新失败', result.msg, 'error');
1338
+ addLog('✗ 获取更新命令失败: ' + result.msg, 'error');
1339
+ showAlert('获取更新命令失败', result.msg, 'error');
1321
1340
  }
1322
1341
  } catch (error) {
1323
1342
  if (error.message !== '未设置密码' && error.message !== '密码验证失败') {
1324
- addLog('✗ 执行更新失败: ' + error.message, 'error');
1325
- showAlert('执行更新失败', error.message, 'error');
1343
+ addLog('✗ 获取更新命令失败: ' + error.message, 'error');
1344
+ showAlert('获取更新命令失败', error.message, 'error');
1326
1345
  }
1327
1346
  }
1328
1347
  }
1329
1348
 
1349
+ // 显示更新命令对话框
1350
+ function showUpdateCommandDialog(instructions, command, note, currentVersion = '', remoteVersion = '') {
1351
+ const dialog = document.getElementById('customDialog');
1352
+ const dialogTitle = document.getElementById('dialogTitleText');
1353
+ const dialogMessage = document.getElementById('dialogMessage');
1354
+ const dialogButtons = document.getElementById('dialogButtons');
1355
+ const dialogIcon = document.getElementById('dialogIcon');
1356
+
1357
+ dialogIcon.textContent = '🔄';
1358
+ dialogTitle.textContent = '手动执行更新';
1359
+
1360
+ // 版本信息HTML(如果有版本号才显示)
1361
+ let versionInfo = '';
1362
+ if (currentVersion && remoteVersion) {
1363
+ versionInfo = `
1364
+ <div style="margin-bottom: 20px; padding: 12px; background: rgba(0, 255, 136, 0.1); border-radius: 8px; border: 1px solid rgba(0, 255, 136, 0.3);">
1365
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1366
+ <div>
1367
+ <span style="color: #888; font-size: 14px;">当前版本:</span>
1368
+ <span style="color: #00ff88; font-weight: bold;">${currentVersion}</span>
1369
+ </div>
1370
+ <div style="color: #00ff88; font-size: 20px;">→</div>
1371
+ <div>
1372
+ <span style="color: #888; font-size: 14px;">最新版本:</span>
1373
+ <span style="color: #00ff88; font-weight: bold;">${remoteVersion}</span>
1374
+ </div>
1375
+ </div>
1376
+ </div>
1377
+ `;
1378
+ }
1379
+
1380
+ // 创建自定义消息内容
1381
+ dialogMessage.innerHTML = `
1382
+ <div style="text-align: left; line-height: 1.8;">
1383
+ ${versionInfo}
1384
+ <p style="margin-bottom: 15px; color: #aaa;">${instructions}</p>
1385
+ <div style="position: relative; background: rgba(0, 0, 0, 0.5); border: 1px solid rgba(0, 255, 136, 0.3); border-radius: 8px; padding: 15px 15px 50px 15px; margin: 15px 0; font-family: 'Consolas', 'Monaco', monospace;">
1386
+ <pre style="margin: 0; color: #00ff88; word-wrap: break-word; white-space: pre-wrap; font-size: 13px; line-height: 1.6;">${command}</pre>
1387
+ <button onclick="copyCommand('${command.replace(/'/g, "\\'")}')" style="position: absolute; bottom: 10px; right: 10px; padding: 8px 16px; background: rgba(0, 255, 136, 0.2); border: 1px solid rgba(0, 255, 136, 0.5); border-radius: 5px; color: #00ff88; cursor: pointer; font-size: 13px; font-weight: bold; transition: all 0.2s;" onmouseover="this.style.background='rgba(0, 255, 136, 0.3)'; this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 12px rgba(0, 255, 136, 0.3)'" onmouseout="this.style.background='rgba(0, 255, 136, 0.2)'; this.style.transform='translateY(0)'; this.style.boxShadow='none'">📋 复制命令</button>
1388
+ </div>
1389
+ <p style="margin-top: 15px; color: #888; font-size: 14px;">💡 ${note}</p>
1390
+ </div>
1391
+ `;
1392
+
1393
+ // 创建关闭按钮
1394
+ dialogButtons.innerHTML = '';
1395
+ const closeBtn = document.createElement('button');
1396
+ closeBtn.textContent = '关闭';
1397
+ closeBtn.onclick = () => {
1398
+ dialog.classList.remove('show');
1399
+ };
1400
+ dialogButtons.appendChild(closeBtn);
1401
+
1402
+ dialog.classList.add('show');
1403
+ }
1404
+
1405
+ // 复制命令到剪贴板
1406
+ function copyCommand(command) {
1407
+ // 解码转义的单引号
1408
+ const decodedCommand = command.replace(/\\'/g, "'");
1409
+
1410
+ // 使用现代 Clipboard API
1411
+ if (navigator.clipboard && window.isSecureContext) {
1412
+ navigator.clipboard.writeText(decodedCommand).then(() => {
1413
+ addLog('✓ 命令已复制到剪贴板', 'success');
1414
+ showTempMessage('命令已复制!');
1415
+ }).catch(err => {
1416
+ console.error('复制失败:', err);
1417
+ fallbackCopyCommand(decodedCommand);
1418
+ });
1419
+ } else {
1420
+ // 降级方案
1421
+ fallbackCopyCommand(decodedCommand);
1422
+ }
1423
+ }
1424
+
1425
+ // 降级复制方法
1426
+ function fallbackCopyCommand(command) {
1427
+ const textArea = document.createElement("textarea");
1428
+ textArea.value = command;
1429
+ textArea.style.position = "fixed";
1430
+ textArea.style.left = "-999999px";
1431
+ document.body.appendChild(textArea);
1432
+ textArea.select();
1433
+
1434
+ try {
1435
+ document.execCommand('copy');
1436
+ addLog('✓ 命令已复制到剪贴板', 'success');
1437
+ showTempMessage('命令已复制!');
1438
+ } catch (err) {
1439
+ console.error('复制失败:', err);
1440
+ addLog('✗ 复制失败,请手动复制', 'error');
1441
+ }
1442
+
1443
+ document.body.removeChild(textArea);
1444
+ }
1445
+
1446
+ // 显示临时提示消息
1447
+ function showTempMessage(message) {
1448
+ const tempMsg = document.createElement('div');
1449
+ tempMsg.textContent = message;
1450
+ tempMsg.style.cssText = `
1451
+ position: fixed;
1452
+ top: 20px;
1453
+ right: 20px;
1454
+ background: rgba(0, 255, 136, 0.9);
1455
+ color: #000;
1456
+ padding: 12px 24px;
1457
+ border-radius: 8px;
1458
+ font-weight: bold;
1459
+ z-index: 10001;
1460
+ animation: slideIn 0.3s ease-out;
1461
+ box-shadow: 0 4px 12px rgba(0, 255, 136, 0.3);
1462
+ `;
1463
+
1464
+ document.body.appendChild(tempMsg);
1465
+
1466
+ setTimeout(() => {
1467
+ tempMsg.style.animation = 'slideOut 0.3s ease-in';
1468
+ setTimeout(() => {
1469
+ document.body.removeChild(tempMsg);
1470
+ }, 300);
1471
+ }, 2000);
1472
+ }
1473
+
1330
1474
  // 同步定时任务信息
1331
1475
  function startScheduleInfoSync() {
1332
1476
  // 立即同步一次