| @@ -0,0 +1,8 @@ | |||
| { | |||
| "presets": [ | |||
| ["es2015", {"loose": true}], | |||
| "stage-1" | |||
| ], | |||
| "plugins": ["transform-runtime"], | |||
| "sourceMaps": true | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| # Logs | |||
| logs | |||
| *.log | |||
| # Runtime data | |||
| pids | |||
| *.pid | |||
| *.seed | |||
| # Directory for instrumented libs generated by jscoverage/JSCover | |||
| lib-cov | |||
| # Coverage directory used by tools like istanbul | |||
| coverage/ | |||
| # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | |||
| .grunt | |||
| # node-waf configuration | |||
| .lock-wscript | |||
| # Dependency directory | |||
| # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git | |||
| node_modules/ | |||
| # IDE config | |||
| .idea | |||
| # output | |||
| output/ | |||
| output.tar.gz | |||
| app/ | |||
| runtime/ | |||
| @@ -0,0 +1,5 @@ | |||
| { | |||
| "createAt": "2025-03-25 17:17:46", | |||
| "mode": "module", | |||
| "es": true | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| Application created by [ThinkJS](http://www.thinkjs.org) | |||
| ## Install dependencies | |||
| ``` | |||
| npm install | |||
| ``` | |||
| ## Start server | |||
| ``` | |||
| npm start | |||
| ``` | |||
| ## Deploy with pm2 | |||
| Use pm2 to deploy app on production enviroment. | |||
| ``` | |||
| pm2 startOrReload pm2.json | |||
| ``` | |||
| @@ -0,0 +1,120 @@ | |||
| server { | |||
| listen 80; | |||
| server_name example.com www.example.com; | |||
| root D:\demo\ssl\ssl_manage/www; | |||
| set $node_port 8360; | |||
| index index.js index.html index.htm; | |||
| if ( -f $request_filename/index.html ){ | |||
| rewrite (.*) $1/index.html break; | |||
| } | |||
| if ( !-f $request_filename ){ | |||
| rewrite (.*) /index.js; | |||
| } | |||
| location = /index.js { | |||
| proxy_http_version 1.1; | |||
| proxy_set_header X-Real-IP $remote_addr; | |||
| proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |||
| proxy_set_header Host $http_host; | |||
| proxy_set_header X-NginX-Proxy true; | |||
| proxy_set_header Upgrade $http_upgrade; | |||
| proxy_set_header Connection "upgrade"; | |||
| proxy_pass http://127.0.0.1:$node_port$request_uri; | |||
| proxy_redirect off; | |||
| } | |||
| location = /development.js { | |||
| deny all; | |||
| } | |||
| location = /testing.js { | |||
| deny all; | |||
| } | |||
| location = /production.js { | |||
| deny all; | |||
| } | |||
| location ~ /static/ { | |||
| etag on; | |||
| expires max; | |||
| } | |||
| } | |||
| ## http/2 nginx conf | |||
| # server { | |||
| # listen 80; | |||
| # server_name example.com www.example.com; | |||
| # rewrite ^(.*) https://example.com$1 permanent; | |||
| # } | |||
| # | |||
| # server { | |||
| # listen 443 ssl http2 fastopen=3 reuseport; | |||
| # server_name www.thinkjs.org thinkjs.org; | |||
| # set $node_port 8360; | |||
| # | |||
| # root D:\demo\ssl\ssl_manage/www; | |||
| # | |||
| # keepalive_timeout 70; | |||
| # | |||
| # ssl_certificate /path/to/certificate; | |||
| # ssl_certificate_key /path/to/certificate.key; | |||
| # ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |||
| # ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; | |||
| # ssl_prefer_server_ciphers on; | |||
| # # openssl dhparam -out dhparams.pem 2048 | |||
| # ssl_dhparam /path/to/dhparams.pem; | |||
| # | |||
| # ssl_session_cache shared:SSL:10m; | |||
| # ssl_session_timeout 10m; | |||
| # | |||
| # ssl_session_ticket_key /path/to/tls_session_ticket.key; | |||
| # ssl_session_tickets on; | |||
| # | |||
| # ssl_stapling on; | |||
| # ssl_stapling_verify on; | |||
| # ssl_trusted_certificate /path/to/startssl_trust_chain.crt; | |||
| # | |||
| # | |||
| # add_header x-Content-Type-Options nosniff; | |||
| # add_header X-Frame-Options deny; | |||
| # add_header Strict-Transport-Security "max-age=16070400"; | |||
| # | |||
| # index index.js index.html index.htm; | |||
| # if ( -f $request_filename/index.html ){ | |||
| # rewrite (.*) $1/index.html break; | |||
| # } | |||
| # if ( !-f $request_filename ){ | |||
| # rewrite (.*) /index.js; | |||
| # } | |||
| # location = /index.js { | |||
| # proxy_http_version 1.1; | |||
| # proxy_set_header X-Real-IP $remote_addr; | |||
| # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |||
| # proxy_set_header Host $http_host; | |||
| # proxy_set_header X-NginX-Proxy true; | |||
| # proxy_set_header Upgrade $http_upgrade; | |||
| # proxy_set_header Connection "upgrade"; | |||
| # proxy_pass http://127.0.0.1:$node_port$request_uri; | |||
| # proxy_redirect off; | |||
| # } | |||
| # | |||
| # location = /production.js { | |||
| # deny all; | |||
| # } | |||
| # | |||
| # location = /testing.js { | |||
| # deny all; | |||
| # } | |||
| # | |||
| # location ~ /static/ { | |||
| # etag on; | |||
| # expires max; | |||
| # } | |||
| #} | |||
| @@ -0,0 +1,30 @@ | |||
| { | |||
| "name": "thinkjs-application", | |||
| "description": "application created by thinkjs", | |||
| "version": "1.0.0", | |||
| "scripts": { | |||
| "start": "node www/development.js", | |||
| "compile": "babel src/ --out-dir app/", | |||
| "watch-compile": "node -e \"console.log('<npm run watch-compile> no longer need, use <npm start> command direct.');console.log();\"", | |||
| "watch": "npm run watch-compile" | |||
| }, | |||
| "dependencies": { | |||
| "babel-runtime": "6.x.x", | |||
| "dayjs": "^1.11.13", | |||
| "fs-extra": "^11.3.0", | |||
| "greenlock": "^2.8.9", | |||
| "greenlock-store-fs": "^3.2.2", | |||
| "le-challenge-fs": "^2.0.9", | |||
| "source-map-support": "0.4.0", | |||
| "thinkjs": "v2" | |||
| }, | |||
| "devDependencies": { | |||
| "babel-cli": "^6.18.0", | |||
| "babel-preset-es2015": "^6.18.0", | |||
| "babel-preset-stage-1": "^6.16.0", | |||
| "babel-plugin-transform-runtime": "^6.15.0", | |||
| "babel-core": "^6.20.0" | |||
| }, | |||
| "repository": "", | |||
| "license": "MIT" | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| { | |||
| "apps": [ | |||
| { | |||
| "name": "ssl", | |||
| "script": "www/live.js", | |||
| "cwd": "/home/program/front/git/ssl_manage", | |||
| "exec_mode": "cluster", | |||
| "instances": 1, | |||
| "max_memory_restart": "2G", | |||
| "autorestart": true, | |||
| "node_args": [], | |||
| "args": [], | |||
| "env": {}, | |||
| "error_file": "/data/pm2/logs/ssl-err.log", | |||
| "out_file": "/data/pm2/logs/ssl-out.log" | |||
| } | |||
| ] | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| /** | |||
| * this file will be loaded before server started | |||
| * you can define global functions used in controllers, models, templates | |||
| */ | |||
| /** | |||
| * use global.xxx to define global functions | |||
| * | |||
| * global.fn1 = function(){ | |||
| * | |||
| * } | |||
| */ | |||
| @@ -0,0 +1,13 @@ | |||
| /** | |||
| * this file will be loaded before server started | |||
| * you can register middleware | |||
| * https://thinkjs.org/doc/middleware.html | |||
| */ | |||
| /** | |||
| * | |||
| * think.middleware('xxx', http => { | |||
| * | |||
| * }) | |||
| * | |||
| */ | |||
| @@ -0,0 +1,7 @@ | |||
| 'use strict'; | |||
| /** | |||
| * config | |||
| */ | |||
| export default { | |||
| //key: value | |||
| }; | |||
| @@ -0,0 +1,22 @@ | |||
| 'use strict'; | |||
| /** | |||
| * db config | |||
| * @type {Object} | |||
| */ | |||
| export default { | |||
| type: 'mysql', | |||
| adapter: { | |||
| mysql: { | |||
| host: '127.0.0.1', | |||
| port: '', | |||
| database: '', | |||
| user: '', | |||
| password: '', | |||
| prefix: '', | |||
| encoding: 'utf8' | |||
| }, | |||
| mongo: { | |||
| } | |||
| } | |||
| }; | |||
| @@ -0,0 +1,15 @@ | |||
| 'use strict'; | |||
| export default { | |||
| 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", | |||
| ], | |||
| CERT_DIR:"/usr/local/nginx/conf/cert" | |||
| }; | |||
| @@ -0,0 +1,15 @@ | |||
| 'use strict'; | |||
| export default { | |||
| 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", | |||
| ], | |||
| CERT_DIR:"/usr/local/nginx/conf/cert" | |||
| }; | |||
| @@ -0,0 +1,5 @@ | |||
| 'use strict'; | |||
| export default { | |||
| resource_on: false | |||
| }; | |||
| @@ -0,0 +1,5 @@ | |||
| 'use strict'; | |||
| export default { | |||
| }; | |||
| @@ -0,0 +1,9 @@ | |||
| 'use strict'; | |||
| /** | |||
| * err config | |||
| */ | |||
| export default { | |||
| //key: value | |||
| key: 'errno', //error number | |||
| msg: 'errmsg' //error message | |||
| }; | |||
| @@ -0,0 +1,9 @@ | |||
| 'use strict'; | |||
| /** | |||
| * hook config | |||
| * https://thinkjs.org/doc/middleware.html#toc-df6 | |||
| */ | |||
| export default { | |||
| } | |||
| @@ -0,0 +1,5 @@ | |||
| 'use strict'; | |||
| export default { | |||
| }; | |||
| @@ -0,0 +1,20 @@ | |||
| 'use strict'; | |||
| /** | |||
| * session configs | |||
| */ | |||
| export default { | |||
| name: 'thinkjs', | |||
| type: 'file', | |||
| secret: 'VH7!OBT^', | |||
| timeout: 24 * 3600, | |||
| cookie: { // cookie options | |||
| length: 32, | |||
| httponly: true | |||
| }, | |||
| adapter: { | |||
| file: { | |||
| path: think.RUNTIME_PATH + '/session', | |||
| } | |||
| } | |||
| }; | |||
| @@ -0,0 +1,14 @@ | |||
| 'use strict'; | |||
| /** | |||
| * template config | |||
| */ | |||
| export default { | |||
| type: 'ejs', | |||
| content_type: 'text/html', | |||
| file_ext: '.html', | |||
| file_depr: '_', | |||
| root_path: think.ROOT_PATH + '/view', | |||
| adapter: { | |||
| ejs: {} | |||
| } | |||
| }; | |||
| @@ -0,0 +1,77 @@ | |||
| 'use strict'; | |||
| /** | |||
| * error controller | |||
| */ | |||
| export default class extends think.controller.base { | |||
| /** | |||
| * display error page | |||
| * @param {Number} status [] | |||
| * @return {Promise} [] | |||
| */ | |||
| displayError(status){ | |||
| //hide error message on production env | |||
| if(think.env === 'production'){ | |||
| this.http.error = null; | |||
| } | |||
| let errorConfig = this.config('error'); | |||
| let message = this.http.error && this.http.error.message || ''; | |||
| if(this.isJsonp()){ | |||
| return this.jsonp({ | |||
| [errorConfig.key]: status, | |||
| [errorConfig.msg]: message | |||
| }) | |||
| }else if(this.isAjax()){ | |||
| return this.fail(status, message); | |||
| } | |||
| let module = 'common'; | |||
| if(think.mode !== think.mode_module){ | |||
| module = this.config('default_module'); | |||
| } | |||
| let file = `${module}/error/${status}.html`; | |||
| let options = this.config('tpl'); | |||
| options = think.extend({}, options, {type: 'base', file_depr: '_'}); | |||
| this.fetch(file, {}, options).then(content => { | |||
| content = content.replace('ERROR_MESSAGE', message); | |||
| this.type(options.content_type); | |||
| this.end(content); | |||
| }); | |||
| } | |||
| /** | |||
| * Bad Request | |||
| * @return {Promise} [] | |||
| */ | |||
| _400Action(){ | |||
| return this.displayError(400); | |||
| } | |||
| /** | |||
| * Forbidden | |||
| * @return {Promise} [] | |||
| */ | |||
| _403Action(){ | |||
| return this.displayError(403); | |||
| } | |||
| /** | |||
| * Not Found | |||
| * @return {Promise} [] | |||
| */ | |||
| _404Action(){ | |||
| return this.displayError(404); | |||
| } | |||
| /** | |||
| * Internal Server Error | |||
| * @return {Promise} [] | |||
| */ | |||
| _500Action(){ | |||
| return this.displayError(500); | |||
| } | |||
| /** | |||
| * Service Unavailable | |||
| * @return {Promise} [] | |||
| */ | |||
| _503Action(){ | |||
| return this.displayError(503); | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| 'use strict'; | |||
| /** | |||
| * config | |||
| */ | |||
| export default { | |||
| //key: value | |||
| }; | |||
| @@ -0,0 +1,7 @@ | |||
| 'use strict'; | |||
| export default class extends think.controller.base { | |||
| /** | |||
| * some base method in here | |||
| */ | |||
| } | |||
| @@ -0,0 +1,236 @@ | |||
| 'use strict'; | |||
| import Base from './base.js'; | |||
| const Greenlock = require('greenlock'); | |||
| const path = require('path'); | |||
| const GreenlockStoreFs = require('greenlock-store-fs'); | |||
| const LeChallengeFs = require('le-challenge-fs'); | |||
| const fs = require('fs-extra'); | |||
| const { execSync } = require('child_process'); | |||
| const https = require('https'); | |||
| const dayjs = require('dayjs'); // 需安装dayjs库 | |||
| // SSL根目录 | |||
| const ROOT_PATH = path.join(think.ROOT_PATH, 'ssl'); | |||
| const PEM_PATH = path.join(ROOT_PATH, 'cert'); | |||
| // 创建存储对象 | |||
| const leStore = GreenlockStoreFs.create({ | |||
| configDir: path.join(ROOT_PATH, 'letsencrypt'), | |||
| }); | |||
| // 创建验证对象 | |||
| const leHttpChallenge = LeChallengeFs.create({ | |||
| webrootPath: path.join(ROOT_PATH, 'lechallenge'), | |||
| }); | |||
| // 是否同意协议 | |||
| function leAgree (opts, agreeCb) { | |||
| agreeCb(null, opts.tosUrl); | |||
| } | |||
| // 证书申请对象 | |||
| const greenlock = Greenlock.create({ | |||
| version: 'draft-12', | |||
| // 测试环境 | |||
| server: 'https://acme-staging-v02.api.letsencrypt.org/directory', | |||
| // 生产环境 | |||
| // server: 'https://acme-v02.api.letsencrypt.org/directory', | |||
| store: leStore, | |||
| challenges: { | |||
| 'http-01': leHttpChallenge, | |||
| }, | |||
| challengeType: 'http-01', | |||
| agreeToTerms: leAgree, | |||
| debug: true, | |||
| renewBy: 10 * 24 * 60 * 60 * 1000,// 10倒计时开始续期 | |||
| }); | |||
| // 创建SSL证书申请请求 | |||
| async function CreateSSL (domain, email = '') { | |||
| const results = await greenlock.register({ | |||
| domains: [domain], | |||
| email, | |||
| agreeTos: true, | |||
| rsaKeySize: 2048, | |||
| }); | |||
| // 如果生成证书就保存一次证书 | |||
| const dir = path.join(PEM_PATH, domain); | |||
| if (!fs.existsSync(dir)) { | |||
| fs.mkdirSync(dir); | |||
| } | |||
| if (results.cert && results.chain) { | |||
| fs.writeFileSync(path.join(dir, `${domain}.crt`), results.cert + results.chain, 'utf-8'); | |||
| console.log(`证书${domain}.crt创建成功!`); | |||
| } | |||
| if (results.privkey) { | |||
| fs.writeFileSync(path.join(dir, `${domain}.key`), results.privkey, 'utf-8'); | |||
| console.log(`证书${domain}.key创建成功!`); | |||
| } | |||
| return results; | |||
| } | |||
| /** | |||
| * 检查证书过期时间 | |||
| * @param {*} domain | |||
| * @returns | |||
| */ | |||
| function checkCertExpiry (domain) { | |||
| return new Promise((resolve, reject) => { | |||
| const req = https.request({ | |||
| hostname: domain, | |||
| port: 443, | |||
| method: 'GET', | |||
| agent: new https.Agent({ rejectUnauthorized: false }) // 避免自签名证书报错 | |||
| }, (res) => { | |||
| const cert = res.socket.getPeerCertificate(); | |||
| if (cert.valid_to) { | |||
| resolve(cert.valid_to); | |||
| } else { | |||
| reject(new Error('证书信息缺失')); | |||
| } | |||
| }); | |||
| 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 daysLeft; | |||
| } catch (err) { | |||
| console.error(`检测失败:${err.message}`); | |||
| return -1; // 错误时返回-1 | |||
| } | |||
| } | |||
| export default class extends Base { | |||
| /** | |||
| * index action | |||
| * @return {Promise} [] | |||
| */ | |||
| async indexAction () { | |||
| let domains = this.config().domains; | |||
| let msgs = [] | |||
| for (let domain of domains) { | |||
| try { | |||
| const daysLeft = await getRemainingDays(domain); | |||
| const msg = `证书${domain},剩余${daysLeft}天` | |||
| console.log(msg); | |||
| msgs.push(msg); | |||
| } catch (e) { | |||
| const msg = `证书${domain},证书缺失或无法访问` | |||
| console.log(e); | |||
| msgs.push(msg); | |||
| } | |||
| } | |||
| this.assign({ | |||
| msgs: msgs.join('\n') | |||
| }); | |||
| 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 < 90) { | |||
| updateList.push(domain); | |||
| } | |||
| } catch (e) { | |||
| console.log("==".repeat(20)) | |||
| console.log('检查证书失败:', e); | |||
| console.log("==".repeat(20)) | |||
| } | |||
| } | |||
| 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 { | |||
| const results = await CreateSSL(domain); | |||
| console.log(`证书${domain},生成成功`); | |||
| } catch (error) { | |||
| 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(); | |||
| } | |||
| async createAction () { | |||
| const domain = this.post('domain'); | |||
| const email = this.post('email'); | |||
| if (!domain || !email) { | |||
| return this.fail('参数错误'); | |||
| } | |||
| try { | |||
| const results = await CreateSSL(domain, email); | |||
| this.deployCert([domain]); | |||
| return this.success(results); | |||
| } catch (error) { | |||
| return this.fail(JSON.stringify(error)); | |||
| } | |||
| } | |||
| /** | |||
| * 复制证书到指定文件夹,并重启nginx | |||
| * @param {*} domains | |||
| * @returns | |||
| */ | |||
| async deployCert (domains) { | |||
| if (!domains || domains.length === 0) { | |||
| return; | |||
| } | |||
| try { | |||
| const dir2 = this.config().CERT_DIR; //nginx配置文件中 | |||
| // 1. 复制证书 | |||
| for (let domain of domains) { | |||
| const dir = path.join(PEM_PATH, domain); //生成的证书地址,项目中 | |||
| let originCrt = `${dir}/${domain}.crt`; | |||
| let originKey = `${dir}/${domain}.key`; | |||
| if (fs.existsSync(originCrt) && fs.existsSync(originKey)) { | |||
| await fs.copy(`${dir}/${domain}.crt`, `${dir2}/${domain}.crt`); | |||
| await fs.copy(`${dir}/${domain}.key`, `${dir2}/${domain}.key`); | |||
| } | |||
| } | |||
| // 2. 检查配置 | |||
| execSync('nginx -t'); | |||
| // 3. 重载服务 | |||
| execSync('nginx -s reload'); | |||
| console.log('证书部署成功!'); | |||
| } catch (error) { | |||
| console.error('部署失败:', error.message); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| 'use strict'; | |||
| /** | |||
| * logic | |||
| * @param {} [] | |||
| * @return {} [] | |||
| */ | |||
| export default class extends think.logic.base { | |||
| /** | |||
| * index action logic | |||
| * @return {} [] | |||
| */ | |||
| indexAction(){ | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| 'use strict'; | |||
| /** | |||
| * model | |||
| */ | |||
| export default class extends think.model.base { | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>New ThinkJS Application</title> | |||
| <style> | |||
| * { | |||
| padding: 0; | |||
| margin: 0; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| font-family: arial; | |||
| } | |||
| a, | |||
| a:visited { | |||
| color: #337ab7; | |||
| text-decoration: none; | |||
| } | |||
| header { | |||
| padding: 70px 0 70px 0; | |||
| background-color: #4A6495 | |||
| } | |||
| h1 { | |||
| font-size: 36px; | |||
| color: #fff; | |||
| font-weight: normal; | |||
| } | |||
| code { | |||
| padding: 2px 4px; | |||
| font-size: 90%; | |||
| color: #c7254e; | |||
| background-color: #f9f2f4; | |||
| border-radius: 4px; | |||
| } | |||
| .content { | |||
| width: 1000px; | |||
| margin: auto | |||
| } | |||
| .wrap { | |||
| width: 1000px; | |||
| margin: auto | |||
| } | |||
| .list { | |||
| width: 800px; | |||
| white-space: pre-line; | |||
| line-height: 3; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <header> | |||
| <div class="wrap"> | |||
| <h1>证书自动更新系统</h1> | |||
| </div> | |||
| </header> | |||
| <div class="content"> | |||
| <div class="list"> | |||
| <%= msgs %> | |||
| </div> | |||
| </div> | |||
| </body> | |||
| </html> | |||
| @@ -0,0 +1,55 @@ | |||
| ## application | |||
| ### start server | |||
| *development* | |||
| ```js | |||
| node www/development.js | |||
| ``` | |||
| *testing* | |||
| ```js | |||
| node www/testing.js | |||
| ``` | |||
| *production* | |||
| ```js | |||
| node www/production.js | |||
| ``` | |||
| or use pm2 to manage node: | |||
| ``` | |||
| pm2 start www/production.js | |||
| ``` | |||
| ### compile es6 code | |||
| ``` | |||
| npm run compile | |||
| ``` | |||
| ### how to link resource | |||
| *in template file* | |||
| ```html | |||
| <script src="/static/js/a.js"></script> | |||
| <img src="/static/img/a.png" alt=""> | |||
| <link rel="stylesheet" href="/static/css/a.js"> | |||
| ``` | |||
| *link image in css* | |||
| ```css | |||
| .a{ | |||
| background: url(../img/a.png) no-repeat; | |||
| } | |||
| ``` | |||
| @@ -0,0 +1,21 @@ | |||
| var thinkjs = require('thinkjs'); | |||
| var path = require('path'); | |||
| var rootPath = path.dirname(__dirname); | |||
| var instance = new thinkjs({ | |||
| APP_PATH: rootPath + path.sep + 'app', | |||
| RUNTIME_PATH: rootPath + path.sep + 'runtime', | |||
| ROOT_PATH: rootPath, | |||
| RESOURCE_PATH: __dirname, | |||
| env: 'development' | |||
| }); | |||
| // Build code from src to app directory. | |||
| instance.compile({ | |||
| log: true, | |||
| presets: [], | |||
| plugins: [] | |||
| }); | |||
| instance.run(); | |||
| @@ -0,0 +1,21 @@ | |||
| var thinkjs = require('thinkjs'); | |||
| var path = require('path'); | |||
| var rootPath = path.dirname(__dirname); | |||
| var instance = new thinkjs({ | |||
| APP_PATH: rootPath + path.sep + 'app', | |||
| RUNTIME_PATH: rootPath + path.sep + 'runtime', | |||
| ROOT_PATH: rootPath, | |||
| RESOURCE_PATH: __dirname, | |||
| env: 'live' | |||
| }); | |||
| // Build code from src to app directory. | |||
| instance.compile({ | |||
| log: true, | |||
| presets: [], | |||
| plugins: [] | |||
| }); | |||
| instance.run(); | |||
| @@ -0,0 +1,14 @@ | |||
| var thinkjs = require('thinkjs'); | |||
| var path = require('path'); | |||
| var rootPath = path.dirname(__dirname); | |||
| var instance = new thinkjs({ | |||
| APP_PATH: rootPath + path.sep + 'app', | |||
| RUNTIME_PATH: rootPath + path.sep + 'runtime', | |||
| ROOT_PATH: rootPath, | |||
| RESOURCE_PATH: __dirname, | |||
| env: 'production' | |||
| }); | |||
| instance.run(true); | |||
| @@ -0,0 +1,14 @@ | |||
| var thinkjs = require('thinkjs'); | |||
| var path = require('path'); | |||
| var rootPath = path.dirname(__dirname); | |||
| var instance = new thinkjs({ | |||
| APP_PATH: rootPath + path.sep + 'app', | |||
| RUNTIME_PATH: rootPath + path.sep + 'runtime', | |||
| ROOT_PATH: rootPath, | |||
| RESOURCE_PATH: __dirname, | |||
| env: 'testing' | |||
| }); | |||
| instance.run(); | |||