浏览代码

save production

master
leiyun 3 个月前
父节点
当前提交
2401aa1169
共有 8 个文件被更改,包括 212 次插入48 次删除
  1. +1
    -1
      devops/pm2.json
  2. +1
    -0
      package.json
  3. +15
    -9
      src/common/config/env/development.js
  4. +14
    -6
      src/common/config/env/live.js
  5. +13
    -9
      src/common/config/env/production.js
  6. +23
    -2
      src/home/controller/base.js
  7. +6
    -21
      src/home/controller/index.js
  8. +139
    -0
      src/home/controller/ssl.js

+ 1
- 1
devops/pm2.json 查看文件

@@ -2,7 +2,7 @@
"apps": [
{
"name": "ssl",
"script": "www/live.js",
"script": "www/production.js",
"cwd": "/home/program/front/git/ssl_manage",
"exec_mode": "cluster",
"instances": 1,


+ 1
- 0
package.json 查看文件

@@ -15,6 +15,7 @@
"greenlock": "^2.8.9",
"greenlock-store-fs": "^3.2.2",
"le-challenge-fs": "^2.0.9",
"request": "^2.88.2",
"scp2": "^0.5.0",
"source-map-support": "0.4.0",
"thinkjs": "v2"


+ 15
- 9
src/common/config/env/development.js 查看文件

@@ -3,15 +3,21 @@
export default {
resource_on: false,
domains:[
// "oa.live.educlouddata.com",
// "m.live.educlouddata.com",
// "yun.live.educlouddata.com",
// "yunh5.live.educlouddata.com",
// "kms.live.educlouddata.com",
// "ams.live.educlouddata.com",
// "data.live.educlouddata.com",
"live.qqsrx.top",
// "www.qbjjyyun.net",
//live
"oa.live.educlouddata.com",
//青白江
"oa.qbjjyyun.net",
"filea.oa.qbjjyyun.net",
"admin.ykj.qbjjyyun.net",
"m.ykj.qbjjyyun.net",
//青白江官网
"qbjjy.cn",
//金苹果
"oa.61gx.com",
//数字校园
"oa.educlouddata.com",
//南坪中学
"oa.npzx.org.cn",
],
CERT_DIR:"/usr/local/nginx/conf/cert"
};

+ 14
- 6
src/common/config/env/live.js 查看文件

@@ -2,13 +2,21 @@

export default {
domains:[
//live
"oa.live.educlouddata.com",
"m.live.educlouddata.com",
"yun.live.educlouddata.com",
"yunh5.live.educlouddata.com",
"kms.live.educlouddata.com",
"ams.live.educlouddata.com",
"data.live.educlouddata.com",
//青白江
"oa.qbjjyyun.net",
"filea.oa.qbjjyyun.net",
"admin.ykj.qbjjyyun.net",
"m.ykj.qbjjyyun.net",
//青白江官网
"qbjjy.cn",
//金苹果
"oa.61gx.com",
//数字校园
"oa.educlouddata.com",
//南坪中学
"oa.npzx.org.cn",
],
CERT_DIR:"/usr/local/nginx/conf/cert"
};

+ 13
- 9
src/common/config/env/production.js 查看文件

@@ -3,17 +3,21 @@
export default {
resource_on: false,
domains:[
//live
"oa.live.educlouddata.com",
//青白江
"oa.qbjjyyun.net",
"m.qbjjyyun.net",
"ykj.qbjjyyun.net",
"m.ykj.qbjjyyun.net",
"filea.oa.qbjjyyun.net",
"admin.ykj.qbjjyyun.net",
"kms.qbjjyyun.net",
"ams.qbjjyyun.net",
"data.qbjjyyun.net",
"xxzz.qbjjyyun.net",
"xxzz.h5.qbjjyyun.net",
"www.qbjjyyun.net",
"m.ykj.qbjjyyun.net",
//青白江官网
"qbjjy.cn",
//金苹果
"oa.61gx.com",
//数字校园
"oa.educlouddata.com",
//南坪中学
"oa.npzx.org.cn",
],
CERT_DIR:"/etc/nginx/cert",
SCP:[


+ 23
- 2
src/home/controller/base.js 查看文件

@@ -1,7 +1,28 @@
'use strict';
import request from 'request';
export default class extends think.controller.base {
/**
* some base method in here
* 监控
* @param {*} msg
*/
async monitor (msg) {
try {
let logStr = `探测域名证书:\n`;
let temp = "=".repeat(30);
logStr += `${temp}\n`;
logStr += `${msg}\n`;
logStr += `${temp}\n`;
let requestData = {
msgtype: "text",
text: {
"content": `${logStr}`
}
};
const PUSH_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=8f2158c7-953b-47d5-be2c-79c9fd228533";
request({ url: PUSH_URL, method: 'POST', json: true, headers: { "content-type": "application/json", }, body: requestData });
} catch (e) {
console.log(e)
}
}
}

+ 6
- 21
src/home/controller/index.js 查看文件

@@ -91,9 +91,11 @@ function checkCertExpiry (domain) {
agent: new https.Agent({ rejectUnauthorized: false }) // 避免自签名证书报错
}, (res) => {
const cert = res.socket.getPeerCertificate();
// console.log(cert);
console.log("=".repeat(20), `${domain}证书信息`, "=".repeat(20))
console.log(cert);
console.log("=".repeat(20), `${domain}证书end`, "=".repeat(20))
let subjectaltname = cert.subjectaltname ? cert.subjectaltname.replace('DNS:', '') : '';
if (cert.valid_to && subjectaltname == domain) {
if (cert.valid_to && subjectaltname.includes(domain)) {
resolve(cert.valid_to);
} else {
reject(new Error('证书信息缺失'));
@@ -179,26 +181,9 @@ export default class extends Base {
}
}
if (updateList.length) {
console.log("==".repeat(20), "开始生成证书", "==".repeat(20));
console.log('开始时间:', dayjs().format('YYYY-MM-DD HH:mm:ss'));
for (let domain of updateList) {
try {
let email = "waitshan@163.com"
const results = await CreateSSL(domain, email);
console.log(`证书${domain},生成成功`);
} catch (e) {
console.log("==".repeat(20), "证书生成失败", "==".repeat(20));
console.log("域名:", domain);
console.log(e);
console.log("==".repeat(20), "证书生成失败", "==".repeat(20));
}
}
await this.deployCert(updateList);
console.log(`更新证书数量:${updateList.length}`);
console.log('结束时间:', dayjs().format('YYYY-MM-DD HH:mm:ss'));
console.log("==".repeat(20), "结束生成证书", "==".repeat(20));
}
this.success();
this.success(updateList);
}

async createAction () {


+ 139
- 0
src/home/controller/ssl.js 查看文件

@@ -0,0 +1,139 @@
'use strict';

import Base from './base.js';

const https = require('https');
const dayjs = require('dayjs');

/**
* 检查域名是否匹配证书的 subjectaltname(支持通配符)
* @param {string} domain - 要检查的域名
* @param {string} subjectaltname - 证书的 subjectaltname 字段
* @returns {boolean}
*/
function matchesCertDomain (domain, subjectaltname) {
if (!subjectaltname) return false;

// 提取所有域名(去除 DNS: 前缀)
const certDomains = subjectaltname.split(',').map(d => d.replace(/DNS:/gi, '').trim());

for (let certDomain of certDomains) {
// 直接匹配
if (certDomain === domain) {
return true;
}

// 通配符匹配(如 *.example.com)
if (certDomain.startsWith('*.')) {
const baseDomain = certDomain.slice(2); // 去掉 '*.'
// 检查域名是否以该基础域名结尾,且前面有子域名
if (domain.endsWith('.' + baseDomain)) {
return true;
}
}
}

return false;
}

/**
* 检查证书过期时间
* @param {*} domain
* @returns
*/
function checkCertExpiry (domain) {
return new Promise((resolve, reject) => {
const req = https.request({
hostname: domain,
port: 443,
method: 'GET',
timeout: 10000, // 10秒超时
family: 4, // 强制使用 IPv4
agent: new https.Agent({
rejectUnauthorized: false, // 避免自签名证书报错
timeout: 10000
})
}, (res) => {
const cert = res.socket.getPeerCertificate();
console.log("=".repeat(20), `${domain}证书信息`, "=".repeat(20))
console.log(cert);
console.log("=".repeat(20), `${domain}证书end`, "=".repeat(20))

const subjectaltname = cert.subjectaltname || '';

if (cert.valid_to && matchesCertDomain(domain, subjectaltname)) {
resolve(cert.valid_to);
} else {
reject(new Error(`证书信息缺失或域名不匹配。域名: ${domain}, SAN: ${subjectaltname}`));
}
});

req.on('error', (err) => reject(err));
req.end();
});
}

/**
*
* @param {*} domain
* @returns
*/
async function getRemainingDays (domain) {
try {
const validTo = await checkCertExpiry(domain);
const expiryDate = dayjs(validTo, 'MMM DD HH:mm:ss YYYY GMT'); // 解析时间格式
const currentDate = dayjs();
const daysLeft = expiryDate.diff(currentDate, 'day');
return {
domain,
daysLeft
};
} catch (err) {
console.error(`检测失败:${err.message}`);
return {
domain,
daysLeft: -1
}; // 错误时返回-1
}
}


export default class extends Base {
/**
* index action
* @return {Promise} []
*/
async indexAction () {
return this.display();
}

/**
* 检查并更新证书
*/
async checksslAction () {
let domains = this.config().domains;
let updateList = []; //需要更新的数据
for (let domain of domains) {
try {
const { daysLeft } = await getRemainingDays(domain);
const msg = `证书${domain},剩余${daysLeft}天`;
console.log(msg);
if (daysLeft <= 10) {
if(daysLeft <= 0){
updateList.push(`${domain} 已过期:${Math.abs(daysLeft)}天`);
}else{
updateList.push(`${domain} 剩余时间:${daysLeft}天`);
}
}
} catch (e) {
console.log("==".repeat(20))
console.log('检查证书失败:', e);
console.log("==".repeat(20))
}
}
if (updateList.length) {
this.monitor(updateList.join('\n'));
}
this.success(updateList);
}
}

正在加载...
取消
保存