Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fbd774266 | ||
|
|
bdec010d2e | ||
|
|
05a00b7b78 | ||
|
|
eaf23c3034 | ||
|
|
276a8b35e5 | ||
|
|
466d659f6e | ||
|
|
84e26381b5 | ||
|
|
469b5a5f69 | ||
|
|
ad77ebd2f9 | ||
|
|
b75543c3bc | ||
|
|
0677275742 | ||
|
|
0c3724e0ad | ||
|
|
803083d23c | ||
|
|
f4f8067a12 | ||
|
|
caa9f084d6 | ||
|
|
81407b65d1 | ||
|
|
8a24293fd7 | ||
|
|
8f1886a585 | ||
|
|
0a64e5fa67 | ||
|
|
7a70603971 | ||
|
|
0d5e00e744 | ||
|
|
91ba1433af | ||
|
|
12e56d14f2 | ||
|
|
7326119f52 | ||
|
|
136983cf14 | ||
|
|
105a1b80ae | ||
|
|
b8000ca533 | ||
|
|
c3e374e6e5 | ||
|
|
a9b6e87249 | ||
|
|
61de5422bf | ||
|
|
f96697f619 | ||
|
|
b4560d6370 | ||
|
|
a7bcde8d82 | ||
|
|
34bb4d54c2 | ||
|
|
e0116a1a03 | ||
|
|
12fec7939d | ||
|
|
ff8e02cceb | ||
|
|
8122bed97f |
4
.gitignore
vendored
@@ -14,3 +14,7 @@ node_modules/
|
||||
|
||||
/packages/*/node_modules
|
||||
/packages/ui/certd-server/tmp/
|
||||
/packages/ui/certd-ui/dist/
|
||||
/other
|
||||
/dev-sidecar-test
|
||||
/packages/core/certd/yarn.lock
|
||||
|
||||
101
README.md
@@ -26,88 +26,31 @@ CertD 是一个帮助你全自动申请和部署SSL证书的工具。
|
||||
## 快速开始
|
||||
本案例演示,如何配置自动申请证书,并部署到阿里云CDN,然后快要到期前自动更新证书并重新部署
|
||||
|
||||
|
||||
1. 环境准备
|
||||
安装[nodejs](https://nodejs.org/zh-cn/)
|
||||
|
||||
2. 创建任务项目
|
||||
|
||||
2. 生成node项目
|
||||
|
||||
通过ui生成: https://certd.docmirror.cn/
|
||||
|
||||
开始生成证书,先填写域名,支持将多个域名打到一个证书上
|
||||

|
||||
|
||||
配置证书详细信息
|
||||

|
||||
|
||||
配置证书部署流程
|
||||

|
||||
|
||||
配置好之后,点击导出按钮,导出一个node项目包
|
||||
|
||||
4. 运行
|
||||
将导出的压缩包解压,然后执行如下命令,即可开始申请证书并部署
|
||||
```
|
||||
mkdir certd-run # 项目名称可以任意命名
|
||||
cd certd-run -y
|
||||
npm install @certd/executor -s --production
|
||||
```
|
||||
|
||||
3. 创建index.js
|
||||
|
||||
参数配置分几个部分
|
||||
args: 运行时参数
|
||||
accessProviders: 授权提供者,提供dns验证与部署任务的授权
|
||||
cert: 证书申请的配置
|
||||
deploy: 证书部署流程
|
||||
|
||||
```js
|
||||
import { Executor } from '@certd/executor'
|
||||
const options = {
|
||||
args: { // 运行时参数
|
||||
forceDeploy: true,
|
||||
},
|
||||
accessProviders: { //授权提供者
|
||||
aliyun: { // 阿里云accessKey,用于dns验证和上传证书到阿里云,并部署到cdn
|
||||
providerType: 'aliyun',
|
||||
accessKeyId: 'Your accessKeyId',
|
||||
accessKeySecret: 'Your accessKeySecret'
|
||||
},
|
||||
},
|
||||
cert: { //免费证书申请配置
|
||||
domains: [ //可以在一张证书上绑定多个域名(前提是他们的验证方式要一样,目前仅支持dns验证)
|
||||
'*.yourdomain.com',
|
||||
'*.test.yourdomain.com',
|
||||
'yourdomain.com'
|
||||
],
|
||||
email: 'Your email',
|
||||
dnsProvider: 'aliyun', //上方accessProviders里面配置的
|
||||
csrInfo: { //证书csr信息
|
||||
country: 'CN',
|
||||
state: 'GuangDong',
|
||||
locality: 'ShengZhen',
|
||||
organization: 'Your company Org.',
|
||||
organizationUnit: 'IT Department',
|
||||
emailAddress: 'Your email'
|
||||
}
|
||||
},
|
||||
deploy: [ //部署流程配置,数组,可以配置多条流程
|
||||
{
|
||||
deployName: '流程1-部署到阿里云CDN',
|
||||
tasks: [ //流程任务,一个流程下可以包含多个部署任务,并且将按顺序执行
|
||||
{ //任务1
|
||||
taskName: '上传到阿里云', //任务名称
|
||||
type: 'uploadCertToAliyun', //任务插件名称
|
||||
props: { //任务插件参数
|
||||
accessProvider: 'aliyun'
|
||||
}
|
||||
},
|
||||
{ // 任务2
|
||||
taskName: '部署证书到CDN',
|
||||
type: 'deployCertToAliyunCDN', //任务插件名称
|
||||
props:{
|
||||
domainName: 'your cdn domain 全称', //cdn域名全称
|
||||
certName: 'certd自动部署',//证书名称前缀
|
||||
accessProvider: 'aliyun'
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
const executor = new Executor()
|
||||
await executor.run(options)
|
||||
```
|
||||
|
||||
4. 运行
|
||||
```
|
||||
node index.js
|
||||
npm install
|
||||
npm run certd
|
||||
```
|
||||
5. 执行效果
|
||||
生成的证书默认会存储在 `${home}/.certd/${email}/certs/${domain}/current`目录下
|
||||
@@ -129,7 +72,7 @@ node index.js
|
||||
所以当你临时需要将证书部署到其他地方时,直接追加部署任务,然后再次运行即可
|
||||
|
||||
## CI/DI集成与自动续期重新部署
|
||||
集成前,将以上代码提交到内网git仓库,或者私有git仓库(由于包含敏感信息,不要提交到公开git仓库)
|
||||
集成前,将以上导出的node项目提交到内网git仓库,或者私有git仓库(由于包含敏感信息,不要提交到公开git仓库)
|
||||
|
||||
### jenkins任务
|
||||
1. 创建任务
|
||||
|
||||
BIN
doc/step1.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
doc/step2.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
doc/step3.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
doc/tasks.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -2,5 +2,5 @@
|
||||
"packages": [
|
||||
"packages/*/*"
|
||||
],
|
||||
"version": "0.1.16"
|
||||
"version": "0.2.0"
|
||||
}
|
||||
|
||||
2
packages/core/api/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/api",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/api",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -35,9 +35,13 @@ export class AbstractDnsProvider {
|
||||
}
|
||||
|
||||
getAccessProvider (accessProvider, accessProviders = this.accessProviders) {
|
||||
let access = accessProvider
|
||||
if (typeof accessProvider === 'string' && accessProviders) {
|
||||
accessProvider = accessProviders[accessProvider]
|
||||
access = accessProviders[accessProvider]
|
||||
}
|
||||
return accessProvider
|
||||
if (access == null) {
|
||||
throw new Error(`accessProvider :${accessProvider}不存在`)
|
||||
}
|
||||
return access
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/core/certd/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
.vscode/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
/.idea/
|
||||
2
packages/core/certd/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/certd",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/certd",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "a ssl cert keeper",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
@@ -10,8 +10,8 @@
|
||||
"author": "Greper",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^0.1.6",
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/acme-client": "^0.2.0",
|
||||
"@certd/api": "^0.2.0",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"node-forge": "^0.10.0"
|
||||
|
||||
@@ -11,8 +11,8 @@ export class CertStore {
|
||||
this.domains = domains
|
||||
this.domain = this.getMainDomain(this.domains)
|
||||
this.safetyDomain = this.getSafetyDomain(this.domain)
|
||||
// this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
|
||||
this.domainDir = this.safetyDomain
|
||||
this.domainDir = this.safetyDomain + '-' + md5(this.getDomainStr(this.domains))
|
||||
// this.domainDir = this.safetyDomain
|
||||
this.certsRootPath = this.store.buildKey(this.email, 'certs')
|
||||
|
||||
this.currentMarkPath = this.store.buildKey(this.certsRootPath, this.domainDir, 'current.json')
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('Certd', function () {
|
||||
const certd = new Certd(options)
|
||||
const currentRootPath = certd.certStore.currentMarkPath
|
||||
console.log('rootDir', currentRootPath)
|
||||
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club\w*\\current.json/)
|
||||
expect(currentRootPath).match(/xiaojunnuo@qq.com\\certs\\_.docmirror.club-\w*\\current.json/)
|
||||
})
|
||||
it('#writeAndReadCert', async function () {
|
||||
const options = createOptions()
|
||||
|
||||
9652
packages/core/executor/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/executor",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
@@ -10,15 +10,15 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/certd": "^0.1.16",
|
||||
"@certd/api": "^0.2.0",
|
||||
"@certd/certd": "^0.2.0",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/plugin-aliyun": "^0.1.16",
|
||||
"@certd/plugin-host": "^0.1.16",
|
||||
"@certd/plugin-tencent": "^0.1.16",
|
||||
"@certd/plugin-aliyun": "^0.2.0",
|
||||
"@certd/plugin-host": "^0.2.0",
|
||||
"@certd/plugin-tencent": "^0.2.0",
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||
|
||||
@@ -27,7 +27,7 @@ export class Executor {
|
||||
options = _.merge(createDefaultOptions(), options)
|
||||
return await this.doRun(options)
|
||||
} catch (e) {
|
||||
logger.error('任务执行出错:' + e.message, e)
|
||||
logger.error('任务执行出错', e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
@@ -58,14 +58,14 @@ export class Executor {
|
||||
logger.info('----------------------')
|
||||
if (!cert.isNew) {
|
||||
// 如果没有更新
|
||||
if (!options.args.forceDeploy && !options.args.forceRedeploy) {
|
||||
// 且不需要强制运行deploy
|
||||
if (options.args.forceRedeploy) {
|
||||
// 强制重新部署,清空保存的状态
|
||||
await certd.certStore.setCurrentFile('context.json', '{}')
|
||||
} else if (!options.args.forceDeploy) {
|
||||
// 且不需要强制deploy
|
||||
logger.info('证书无更新,无需重新部署')
|
||||
logger.info('任务完成')
|
||||
return { cert }
|
||||
} else {
|
||||
// 强制重新运行,清空保存的状态
|
||||
await certd.certStore.setCurrentFile('context.json', '{}')
|
||||
}
|
||||
}
|
||||
// 读取上次执行进度
|
||||
@@ -88,15 +88,16 @@ export class Executor {
|
||||
logger.info('任务完成')
|
||||
trace.print()
|
||||
const result = resultTrace.get({ })
|
||||
const returnData = {
|
||||
if (result) {
|
||||
if (result.status === 'error' && options.args.doNotThrowError === false) {
|
||||
throw new Error(result.remark)
|
||||
}
|
||||
}
|
||||
return {
|
||||
cert,
|
||||
context,
|
||||
result
|
||||
}
|
||||
if (result.status === 'error' && options.args.doNotThrowError === false) {
|
||||
throw new Error(result.remark)
|
||||
}
|
||||
return returnData
|
||||
}
|
||||
|
||||
async runCertd (certd) {
|
||||
|
||||
@@ -76,7 +76,9 @@ export class Trace {
|
||||
}
|
||||
}
|
||||
const result = this.get({ type: 'result' })
|
||||
this.printTraceLine(result, 'result', '')
|
||||
if (result) {
|
||||
this.printTraceLine(result, 'result', '')
|
||||
}
|
||||
const mainContext = {}
|
||||
_.merge(mainContext, context)
|
||||
delete mainContext.__trace__
|
||||
|
||||
2
packages/plugins/plugin-aliyun/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/plugin-aliyun",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@certd/plugin-aliyun",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/api": "^0.2.0",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/certd": "^0.1.16",
|
||||
"@certd/certd": "^0.2.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
|
||||
2
packages/plugins/plugin-host/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/plugin-host",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@certd/plugin-host",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/api": "^0.2.0",
|
||||
"dayjs": "^1.9.7",
|
||||
"lodash-es": "^4.17.20",
|
||||
"ssh2": "^0.8.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/certd": "^0.1.16",
|
||||
"@certd/certd": "^0.2.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
|
||||
@@ -17,7 +17,7 @@ export class SSHAccessProvider {
|
||||
required: true
|
||||
},
|
||||
password: { desc: '登录密码' },
|
||||
publicKey: {
|
||||
privateKey: {
|
||||
desc: '密钥,密码或此项必填一项'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export class SshClient {
|
||||
}
|
||||
* @param transports
|
||||
*/
|
||||
uploadFiles ({ connectConf, transports }) {
|
||||
uploadFiles ({ connectConf, transports, sudo = false }) {
|
||||
const conn = new ssh2.Client()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -29,7 +29,8 @@ export class SshClient {
|
||||
try {
|
||||
for (const transport of transports) {
|
||||
logger.info('上传文件:', JSON.stringify(transport))
|
||||
await this.exec({ connectConf, script: 'mkdir -p ' + path.dirname(transport.remotePath) })
|
||||
sudo = sudo ? 'sudo' : ''
|
||||
await this.exec({ connectConf, script: `${sudo} mkdir -p ${path.dirname(transport.remotePath)} ` })
|
||||
await this.fastPut({ sftp, ...transport })
|
||||
}
|
||||
resolve()
|
||||
@@ -47,6 +48,7 @@ export class SshClient {
|
||||
if (_.isArray(script)) {
|
||||
script = script.join('\n')
|
||||
}
|
||||
console.log('执行命令:', script)
|
||||
return new Promise((resolve, reject) => {
|
||||
this.connect({
|
||||
connectConf,
|
||||
@@ -58,11 +60,12 @@ export class SshClient {
|
||||
}
|
||||
let data = null
|
||||
stream.on('close', (code, signal) => {
|
||||
console.log(`[${connectConf.host}][close]:code:${code}, signal:${signal} `)
|
||||
|
||||
console.log(`[${connectConf.host}][close]:code:${code}`)
|
||||
data = data ? data.toString() : null
|
||||
if (code === 0) {
|
||||
data = data ? data.toString() : null
|
||||
resolve(data)
|
||||
} else {
|
||||
reject(new Error(data))
|
||||
}
|
||||
conn.end()
|
||||
}).on('data', (ret) => {
|
||||
@@ -70,8 +73,7 @@ export class SshClient {
|
||||
data = ret
|
||||
}).stderr.on('data', (err) => {
|
||||
console.log(`[${connectConf.host}][error]: ` + err)
|
||||
reject(new Error(err.toString()))
|
||||
stream.close()
|
||||
data = err
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ export class UploadCertToHost extends AbstractHostPlugin {
|
||||
filter: 'ssh'
|
||||
},
|
||||
required: true
|
||||
},
|
||||
sudo: {
|
||||
label: '是否sudo'
|
||||
}
|
||||
},
|
||||
output: {
|
||||
@@ -45,7 +48,6 @@ export class UploadCertToHost extends AbstractHostPlugin {
|
||||
async execute ({ cert, props, context }) {
|
||||
const { crtPath, keyPath, accessProvider } = props
|
||||
const connectConf = this.getAccessProvider(accessProvider)
|
||||
console.log('connectConf', connectConf)
|
||||
const sshClient = new SshClient()
|
||||
await sshClient.uploadFiles({
|
||||
connectConf,
|
||||
|
||||
@@ -22,7 +22,31 @@ describe('HostShellExecute', function () {
|
||||
const ret = await plugin.doExecute(uploadOpts)
|
||||
expect(ret).ok
|
||||
console.log('-----' + JSON.stringify(ret))
|
||||
})
|
||||
|
||||
await plugin.doRollback(uploadOpts)
|
||||
it('#execute-hk-restart-docker', async function () {
|
||||
this.timeout(10000)
|
||||
const options = createOptions()
|
||||
const plugin = new HostShellExecute(options)
|
||||
const uploadOpts = {
|
||||
props: { script: ['cd /home/ubuntu/deloy/nginx-proxy\nsudo docker-compose build\nsudo docker-compose up -d\n'], accessProvider: 'aliyun-ssh-hk' },
|
||||
context: {}
|
||||
}
|
||||
const ret = await plugin.doExecute(uploadOpts)
|
||||
expect(ret).ok
|
||||
console.log('-----' + JSON.stringify(ret))
|
||||
})
|
||||
|
||||
it('#execute-publicKey-login', async function () {
|
||||
this.timeout(10000)
|
||||
const options = createOptions()
|
||||
const plugin = new HostShellExecute(options)
|
||||
const shellOpts = {
|
||||
props: { script: ['ls'], accessProvider: 'tencent-ssh-base01' },
|
||||
context: {}
|
||||
}
|
||||
const ret = await plugin.doExecute(shellOpts)
|
||||
expect(ret).ok
|
||||
console.log('-----' + JSON.stringify(ret))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,4 +24,25 @@ describe('PluginUploadToHost', function () {
|
||||
|
||||
await plugin.doRollback(uploadOpts)
|
||||
})
|
||||
|
||||
it('#execute-to-ubantu', async function () {
|
||||
this.timeout(10000)
|
||||
const options = createOptions()
|
||||
options.args = { test: false }
|
||||
options.cert.email = 'xiaojunnuo@qq.com'
|
||||
options.cert.domains = ['*.docmirror.cn']
|
||||
const plugin = new UploadCertToHost(options)
|
||||
const certd = new Certd(options)
|
||||
const cert = await certd.readCurrentCert()
|
||||
const context = {}
|
||||
const uploadOpts = {
|
||||
cert,
|
||||
props: { crtPath: '/home/ubuntu/deloy/nginx-proxy/ssl/test.crt', keyPath: '/home/ubuntu/deloy/nginx-proxy/ssl/test.key', accessProvider: 'aliyun-ssh-hk' },
|
||||
context
|
||||
}
|
||||
await plugin.doExecute(uploadOpts)
|
||||
console.log('context:', context)
|
||||
|
||||
await plugin.doRollback(uploadOpts)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/plugin-tencent",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "@certd/plugin-tencent",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/api": "^0.2.0",
|
||||
"dayjs": "^1.9.7",
|
||||
"kubernetes-client": "^9.0.0",
|
||||
"lodash-es": "^4.17.20",
|
||||
"tencentcloud-sdk-nodejs": "^4.0.44"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/certd": "^0.1.16",
|
||||
"@certd/certd": "^0.2.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
|
||||
@@ -29,7 +29,7 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
|
||||
this.loginToken = accessProvider.id + ',' + accessProvider.token
|
||||
}
|
||||
|
||||
async doRequest (options) {
|
||||
async doRequest (options, successCodes = []) {
|
||||
const config = {
|
||||
method: 'post',
|
||||
formData: {
|
||||
@@ -43,8 +43,11 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
|
||||
_.merge(config, options)
|
||||
|
||||
const ret = await request(config)
|
||||
if (!ret || !ret.status || ret.status.code !== '1') {
|
||||
throw new Error('请求失败:' + ret.status.message + ',api=' + config.url)
|
||||
if (!ret || !ret.status) {
|
||||
const code = ret.status.code
|
||||
if (code !== '1' || !successCodes.includes(code)) {
|
||||
throw new Error('请求失败:' + ret.status.message + ',api=' + config.url)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -73,7 +76,7 @@ export class DnspodDnsProvider extends AbstractDnsProvider {
|
||||
value: value,
|
||||
mx: 1
|
||||
}
|
||||
})
|
||||
}, ['104'])// 104错误码为记录已存在,无需再次添加
|
||||
this.logger.info('添加域名解析成功:', fullRecord, value, JSON.stringify(ret.record))
|
||||
return ret.record
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
||||
label: 'ingress名称',
|
||||
desc: '支持多个(传入数组)'
|
||||
},
|
||||
ingressClass: {
|
||||
type: String,
|
||||
label: 'ingress类型',
|
||||
desc: '可选 qcloud / nginx'
|
||||
},
|
||||
clusterIp: {
|
||||
type: String,
|
||||
label: '集群内网ip',
|
||||
@@ -86,7 +91,13 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
||||
// 修改内网解析ip地址
|
||||
k8sClient.setLookup({ [clusterDomain]: { ip: props.clusterIp } })
|
||||
}
|
||||
await this.patchCertSecret({ k8sClient, props, context })
|
||||
const ingressType = props.ingressClass || 'qcloud'
|
||||
if (ingressType === 'qcloud') {
|
||||
await this.patchQcloudCertSecret({ k8sClient, props, context })
|
||||
} else {
|
||||
await this.patchNginxCertSecret({ cert, k8sClient, props, context })
|
||||
}
|
||||
|
||||
await this.sleep(2000) // 停留2秒,等待secret部署完成
|
||||
await this.restartIngress({ k8sClient, props })
|
||||
return true
|
||||
@@ -121,7 +132,7 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
||||
return ret.Kubeconfig
|
||||
}
|
||||
|
||||
async patchCertSecret ({ k8sClient, props, context }) {
|
||||
async patchQcloudCertSecret ({ k8sClient, props, context }) {
|
||||
const { tencentCertId } = context
|
||||
if (tencentCertId == null) {
|
||||
throw new Error('请先将【上传证书到腾讯云】作为前置任务')
|
||||
@@ -151,6 +162,35 @@ export class DeployCertToTencentTKEIngress extends AbstractTencentPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
async patchNginxCertSecret ({ cert, k8sClient, props, context }) {
|
||||
const crt = cert.crt
|
||||
const key = cert.key
|
||||
const crtBase64 = Buffer.from(crt).toString('base64')
|
||||
const keyBase64 = Buffer.from(key).toString('base64')
|
||||
|
||||
const { namespace, secretName } = props
|
||||
|
||||
const body = {
|
||||
data: {
|
||||
'tls.crt': crtBase64,
|
||||
'tls.key': keyBase64
|
||||
},
|
||||
metadata: {
|
||||
labels: {
|
||||
certd: this.appendTimeSuffix('certd')
|
||||
}
|
||||
}
|
||||
}
|
||||
let secretNames = secretName
|
||||
if (typeof secretName === 'string') {
|
||||
secretNames = [secretName]
|
||||
}
|
||||
for (const secret of secretNames) {
|
||||
await k8sClient.patchSecret({ namespace, secretName: secret, body })
|
||||
this.logger.info(`CertSecret已更新:${secret}`)
|
||||
}
|
||||
}
|
||||
|
||||
async restartIngress ({ k8sClient, props }) {
|
||||
const { namespace, ingressName } = props
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import pkg from 'chai'
|
||||
import { DeployCertToTencentTKEIngress } from '../../src/plugins/deploy-to-tke-ingress/index.js'
|
||||
import { Certd } from '@certd/certd'
|
||||
import { createOptions } from '../../../../../test/options.js'
|
||||
import { K8sClient } from '../../src/utils/util.k8s.client.js'
|
||||
|
||||
const { expect } = pkg
|
||||
|
||||
async function getOptions () {
|
||||
const options = createOptions()
|
||||
options.args.test = false
|
||||
options.cert.email = 'xiaojunnuo@qq.com'
|
||||
options.cert.domains = ['*.docmirror.cn']
|
||||
const certd = new Certd(options)
|
||||
const cert = await certd.readCurrentCert()
|
||||
const context = {}
|
||||
const deployOpts = {
|
||||
accessProviders: options.accessProviders,
|
||||
cert,
|
||||
props: {
|
||||
accessProvider: 'tencent-yonsz',
|
||||
region: 'ap-guangzhou',
|
||||
clusterId: 'cls-6lbj1vee'
|
||||
},
|
||||
context
|
||||
}
|
||||
return { options, deployOpts }
|
||||
}
|
||||
|
||||
describe('DeployCertToTencentTKEIngressNginx', function () {
|
||||
it('#getTKESecrets', async function () {
|
||||
this.timeout(50000)
|
||||
const { options, deployOpts } = await getOptions()
|
||||
const plugin = new DeployCertToTencentTKEIngress(options)
|
||||
const tkeClient = plugin.getTkeClient(options.accessProviders[deployOpts.props.accessProvider], deployOpts.props.region)
|
||||
const kubeConfig = await plugin.getTkeKubeConfig(tkeClient, deployOpts.props.clusterId)
|
||||
|
||||
const k8sClient = new K8sClient(kubeConfig)
|
||||
k8sClient.setLookup({
|
||||
'cls-6lbj1vee.ccs.tencent-cloud.com': { ip: '13.123.123.123' }
|
||||
})
|
||||
const secrets = await k8sClient.getSecret({ namespace: 'stress' })
|
||||
|
||||
console.log('secrets:', secrets)
|
||||
})
|
||||
it('#execute', async function () {
|
||||
this.timeout(5000)
|
||||
|
||||
const { options, deployOpts } = await getOptions()
|
||||
deployOpts.props.ingressName = 'stress-ingress-nginx'
|
||||
deployOpts.props.ingressClass = 'nginx'
|
||||
deployOpts.props.secretName = 'stress-all'
|
||||
deployOpts.props.namespace = 'stress'
|
||||
const plugin = new DeployCertToTencentTKEIngress(options)
|
||||
|
||||
const ret = await plugin.doExecute(deployOpts)
|
||||
console.log('sucess', ret)
|
||||
})
|
||||
})
|
||||
6
packages/ui/certd-server/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM registry.cn-shenzhen.aliyuncs.com/greper/node:15.8.0-alpine
|
||||
ENV TZ=Asia/Shanghai
|
||||
EXPOSE 3000
|
||||
ADD ./ /app/
|
||||
RUN cd /app/ && ls
|
||||
ENTRYPOINT node /app/bin/www.js
|
||||
@@ -7,6 +7,8 @@ import Static from 'koa-static'
|
||||
import fs from 'fs'
|
||||
import _ from 'lodash-es'
|
||||
import './install.js'
|
||||
import pathUtil from './utils/util.path.js'
|
||||
import compress from 'koa-compress'
|
||||
const app = new Koa()
|
||||
|
||||
// error handler
|
||||
@@ -18,8 +20,14 @@ app.use(bodyparser({
|
||||
}))
|
||||
app.use(json())
|
||||
app.use(logger())
|
||||
// gzip
|
||||
// app.use(compress({ threshold: 5120 }))
|
||||
|
||||
app.use(Static(new URL('public', import.meta.url).pathname))
|
||||
const staticPlugin = Static(pathUtil.join('public'), {
|
||||
maxage: 30 * 24 * 60 * 3600,
|
||||
gzip: true
|
||||
})
|
||||
app.use(staticPlugin)
|
||||
|
||||
// logger
|
||||
app.use(async (ctx, next) => {
|
||||
@@ -29,8 +37,6 @@ app.use(async (ctx, next) => {
|
||||
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
|
||||
})
|
||||
|
||||
console.log('url', import.meta.url)
|
||||
|
||||
// routes
|
||||
const files = fs.readdirSync(new URL('controllers/', import.meta.url))
|
||||
// 过滤出.js文件:
|
||||
|
||||
@@ -3,7 +3,7 @@ import { accessProviderRegistry } from '@certd/api'
|
||||
import _ from 'lodash-es'
|
||||
import { Ret } from '../models/Ret.js'
|
||||
const router = Router()
|
||||
router.prefix('/access-providers')
|
||||
router.prefix('/api/access-providers')
|
||||
|
||||
router.get('/list', function (ctx, next) {
|
||||
const list = []
|
||||
|
||||
@@ -3,7 +3,7 @@ import { dnsProviderRegistry } from '@certd/api'
|
||||
import _ from 'lodash-es'
|
||||
import { Ret } from '../models/Ret.js'
|
||||
const router = Router()
|
||||
router.prefix('/dns-providers')
|
||||
router.prefix('/api/dns-providers')
|
||||
|
||||
router.get('/list', function (ctx, next) {
|
||||
const list = []
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from 'fs'
|
||||
import exportsService from '../service/exports-service.js'
|
||||
|
||||
const router = Router()
|
||||
router.prefix('/exports')
|
||||
router.prefix('/api/exports')
|
||||
|
||||
router.post('/toZip', async function (ctx, next) {
|
||||
// const request = ctx.request
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Router from 'koa-router'
|
||||
const router = Router()
|
||||
|
||||
router.get('/', async (ctx, next) => {
|
||||
router.get('/api/', async (ctx, next) => {
|
||||
await ctx.render('index', {
|
||||
title: 'Hello CertD!'
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { pluginRegistry } from '@certd/api'
|
||||
import _ from 'lodash-es'
|
||||
import { Ret } from '../models/Ret.js'
|
||||
const router = Router()
|
||||
router.prefix('/plugins')
|
||||
router.prefix('/api/plugins')
|
||||
|
||||
router.get('/list', function (ctx, next) {
|
||||
const list = []
|
||||
|
||||
901
packages/ui/certd-server/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/server",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -10,16 +10,17 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/api": "^0.1.16",
|
||||
"@certd/executor": "^0.1.16",
|
||||
"@certd/plugin-aliyun": "^0.1.16",
|
||||
"@certd/plugin-host": "^0.1.16",
|
||||
"@certd/plugin-tencent": "^0.1.16",
|
||||
"@certd/api": "^0.2.0",
|
||||
"@certd/executor": "^0.2.0",
|
||||
"@certd/plugin-aliyun": "^0.2.0",
|
||||
"@certd/plugin-host": "^0.2.0",
|
||||
"@certd/plugin-tencent": "^0.2.0",
|
||||
"compressing": "^1.5.1",
|
||||
"debug": "^4.1.1",
|
||||
"fs-extra": "^9.1.0",
|
||||
"koa": "^2.7.0",
|
||||
"koa-bodyparser": "^4.2.1",
|
||||
"koa-compress": "^5.0.1",
|
||||
"koa-convert": "^1.2.0",
|
||||
"koa-json": "^2.0.2",
|
||||
"koa-logger": "^3.2.0",
|
||||
@@ -31,7 +32,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^7.18.0",
|
||||
"eslint": "^7.19.0",
|
||||
"eslint-config-standard": "^16.0.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
|
||||
BIN
packages/ui/certd-server/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
packages/ui/certd-server/public/logo/logo-lang.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
packages/ui/certd-server/public/logo/logo-svg.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
17
packages/ui/certd-server/public/logo/logo.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="500" height="500" viewBox="0 0 500.000000 500.000000"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<path d="M28.34 56.68h28.34V36.12H28.34a7.79 7.79 0 1 1 0-15.58h19.84v9.05h8.5V12H28.34a16.29 16.29 0 0 0 0 32.58h19.84v3.56H28.34a19.84 19.84 0 0 1 0-39.68h28.34V0H28.34a28.34 28.34 0 0 0 0 56.68z"
|
||||
fill="#2c3e50"
|
||||
transform="translate(124, 60) scale(4,4)"
|
||||
></path>
|
||||
<path d="M13.00-21.91L21.24-21.91L21.24-17.23L14.04-17.23Q10.39-17.23 9-15.62L9-15.62Q7.65-14.08 7.65-10.93L7.65-10.93Q7.65-7.42 9.86-5.80L9.86-5.80Q10.75-5.17 11.81-4.93Q12.87-4.68 14.76-4.68L14.76-4.68L21.24-4.68L21.24 0L13.00 0Q9.67 0 7.74-0.67Q5.80-1.35 4.32-3.01L4.32-3.01Q1.48-6.17 1.48-11.03L1.48-11.03Q1.48-16.88 4.86-19.75L4.86-19.75Q6.21-20.93 8.10-21.42Q9.99-21.91 13.00-21.91L13.00-21.91ZM31.05-13.32L43.74-13.32L43.74-8.64L31.05-8.64Q31.23-6.48 32.44-5.58Q33.66-4.68 36.41-4.68L36.41-4.68L43.74-4.68L43.74 0L35.73 0Q33.12 0 31.48-0.47Q29.83-0.94 28.39-2.02L28.39-2.02Q24.39-5.13 24.39-11.25L24.39-11.25Q24.39-15.21 26.50-18.18L26.50-18.18Q27.94-20.20 29.97-21.06Q31.99-21.91 35.28-21.91L35.28-21.91L43.74-21.91L43.74-17.23L35.73-17.23Q33.25-17.23 32.27-16.40Q31.27-15.57 31.05-13.32L31.05-13.32ZM48.64 0L48.64-21.91L57.55-21.91Q60.30-21.91 61.81-21.53Q63.31-21.15 64.31-20.25L64.31-20.25Q65.30-19.35 65.70-18Q66.10-16.65 66.10-14.13L66.10-14.13L66.10-12.01L60.30-12.01L60.30-13.18Q60.30-15.52 59.49-16.38Q58.68-17.23 56.38-17.23L56.38-17.23L54.67-17.23L54.67 0L48.64 0ZM67.50-21.91L71.33-21.91L71.33-30.02L77.36-30.02L77.36-21.91L82.94-21.91L82.94-17.23L77.36-17.23L77.36-9.27Q77.36-6.48 77.85-5.76L77.85-5.76Q78.61-4.68 80.55-4.68L80.55-4.68L82.94-4.68L82.94 0L78.57 0Q74.66 0 72.99-1.89Q71.33-3.78 71.33-8.23L71.33-8.23L71.33-17.23L67.50-17.23L67.50-21.91ZM96.08-21.91L101.75-21.91L101.75-30.02L107.73-30.02L107.73 0L97.38 0Q94.23 0 92.61-0.49L92.61-0.49Q88.78-1.71 86.90-5.26L86.90-5.26Q85.59-7.65 85.59-11.12L85.59-11.12Q85.59-16.74 89.37-19.84L89.37-19.84Q91.84-21.91 96.08-21.91L96.08-21.91ZM97.38-4.68L101.75-4.68L101.75-17.23L97.38-17.23Q94.50-17.23 93.02-15.30L93.02-15.30Q91.71-13.68 91.71-11.12L91.71-11.12Q91.71-7.38 93.87-5.76L93.87-5.76Q95.36-4.68 97.38-4.68L97.38-4.68Z"
|
||||
fill="#2c3e50"
|
||||
transform="translate(28, 430) scale(4,4)"
|
||||
|
||||
></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -1,8 +0,0 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
||||
@@ -27,12 +27,16 @@ export default {
|
||||
fs.writeJsonSync(optionsFilePath, options)
|
||||
|
||||
// 依赖版本
|
||||
const exePkgJson = fs.readFileSync('node_modules/@certd/executor/package.json')
|
||||
const exePkgJson = fs.readFileSync(pathUtil.join('node_modules/@certd/executor/package.json'))
|
||||
const executorPkg = JSON.parse(exePkgJson)
|
||||
const currentVersion = executorPkg.version
|
||||
|
||||
const templatePkg = require('../templates/certd-run/package.json')
|
||||
const templatePkgJson = fs.readFileSync(pathUtil.join('templates/certd-run/package.json'))
|
||||
const templatePkg = JSON.parse(templatePkgJson)
|
||||
templatePkg.dependencies['@certd/executor'] = '^' + currentVersion
|
||||
templatePkg.dependencies['@certd/plugin-aliyun'] = '^' + currentVersion
|
||||
templatePkg.dependencies['@certd/plugin-host'] = '^' + currentVersion
|
||||
templatePkg.dependencies['@certd/plugin-tencent'] = '^' + currentVersion
|
||||
const pkgFilePath = path.join(targetProjectDir, 'package.json')
|
||||
fs.writeJsonSync(pkgFilePath, templatePkg)
|
||||
|
||||
|
||||
@@ -2,11 +2,17 @@ import { Executor } from '@certd/executor'
|
||||
import PluginAliyun from '@certd/plugin-aliyun'
|
||||
import PluginTencent from '@certd/plugin-tencent'
|
||||
import PluginHost from '@certd/plugin-host'
|
||||
import options from './options.json'
|
||||
|
||||
// 安装默认插件和授权提供者
|
||||
PluginAliyun.install()
|
||||
PluginTencent.install()
|
||||
PluginHost.install()
|
||||
|
||||
// import options
|
||||
import { createRequire } from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
const options =require('./options.json')
|
||||
|
||||
//开始执行
|
||||
const executor = new Executor()
|
||||
executor.run(options)
|
||||
await executor.run(options)
|
||||
|
||||
@@ -2,7 +2,6 @@ import os from 'os'
|
||||
export default {
|
||||
join (...dirs) {
|
||||
const url = new URL('../' + dirs.join('/'), import.meta.url)
|
||||
console.log('url', url)
|
||||
let path = url.pathname
|
||||
if (os.type() === 'Windows_NT') {
|
||||
path = path.substring(1)
|
||||
|
||||
1
packages/ui/certd-ui/.env
Normal file
@@ -0,0 +1 @@
|
||||
VUE_APP_API=/api
|
||||
@@ -2,4 +2,8 @@ module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
// plugins: [['import', {
|
||||
// libraryName: 'ant-design-vue',
|
||||
// style: true // or 'css'
|
||||
// }]]
|
||||
}
|
||||
|
||||
1268
packages/ui/certd-ui/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/certd-ui",
|
||||
"version": "0.1.16",
|
||||
"version": "0.2.0",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "vue-cli-service serve",
|
||||
@@ -9,9 +9,9 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/dns-providers": "^0.1.13",
|
||||
"@certd/plugins": "^0.1.13",
|
||||
"@ant-design/icons-vue": "^6.0.1",
|
||||
"ant-design-vue": "^2.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.8.1",
|
||||
"lodash-es": "^4.17.20",
|
||||
"vue": "^3.0.4",
|
||||
@@ -31,7 +31,9 @@
|
||||
"@vue/eslint-config-standard": "^6.0.0",
|
||||
"@vue/test-utils": "^2.0.0-0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-plugin-import": "^1.13.3",
|
||||
"chai": "^4.2.0",
|
||||
"compression-webpack-plugin": "^5.0.1",
|
||||
"eslint": "^7.15.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 17 KiB |
86
packages/ui/certd-ui/src/antdv.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import _ from 'lodash'
|
||||
import {
|
||||
AutoComplete,
|
||||
Alert,
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Calendar,
|
||||
Card,
|
||||
Cascader,
|
||||
Checkbox,
|
||||
Col,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Layout,
|
||||
List,
|
||||
LocaleProvider,
|
||||
Modal,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
Switch,
|
||||
Tabs,
|
||||
Tag,
|
||||
TimePicker,
|
||||
Tooltip,
|
||||
Drawer,
|
||||
// ColorPicker,
|
||||
ConfigProvider,
|
||||
Descriptions,
|
||||
Space
|
||||
} from 'ant-design-vue'
|
||||
|
||||
const list = {
|
||||
AutoComplete,
|
||||
Alert,
|
||||
Avatar,
|
||||
Badge,
|
||||
Button,
|
||||
Calendar,
|
||||
Card,
|
||||
Cascader,
|
||||
Checkbox,
|
||||
Col,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Layout,
|
||||
List,
|
||||
LocaleProvider,
|
||||
TimePicker,
|
||||
Modal,
|
||||
Radio,
|
||||
Row,
|
||||
Select,
|
||||
Switch,
|
||||
Tabs,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Drawer,
|
||||
// ColorPicker,
|
||||
ConfigProvider,
|
||||
Descriptions,
|
||||
Space
|
||||
}
|
||||
export default function (app) {
|
||||
_.forEach(list, item => {
|
||||
app.use(item)
|
||||
//
|
||||
// app.config.globalProperties.$message = message
|
||||
// app.config.globalProperties.$notification = notification
|
||||
app.config.globalProperties.$info = Modal.info
|
||||
app.config.globalProperties.$success = Modal.success
|
||||
app.config.globalProperties.$error = Modal.error
|
||||
app.config.globalProperties.$warning = Modal.warning
|
||||
app.config.globalProperties.$confirm = Modal.confirm
|
||||
app.config.globalProperties.$destroyAll = Modal.destroyAll
|
||||
})
|
||||
}
|
||||
@@ -55,10 +55,8 @@ export function errorLog (error) {
|
||||
// 打印到控制台
|
||||
console.log(error)
|
||||
// 显示提示
|
||||
notification({
|
||||
message: error.message,
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
notification.error({
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
BIN
packages/ui/certd-ui/src/assets/favicon.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
packages/ui/certd-ui/src/assets/logo-svg.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
17
packages/ui/certd-ui/src/assets/logo.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="500" height="500" viewBox="0 0 500.000000 500.000000"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<path d="M28.34 56.68h28.34V36.12H28.34a7.79 7.79 0 1 1 0-15.58h19.84v9.05h8.5V12H28.34a16.29 16.29 0 0 0 0 32.58h19.84v3.56H28.34a19.84 19.84 0 0 1 0-39.68h28.34V0H28.34a28.34 28.34 0 0 0 0 56.68z"
|
||||
fill="#2c3e50"
|
||||
transform="translate(124, 60) scale(4,4)"
|
||||
></path>
|
||||
<path d="M13.00-21.91L21.24-21.91L21.24-17.23L14.04-17.23Q10.39-17.23 9-15.62L9-15.62Q7.65-14.08 7.65-10.93L7.65-10.93Q7.65-7.42 9.86-5.80L9.86-5.80Q10.75-5.17 11.81-4.93Q12.87-4.68 14.76-4.68L14.76-4.68L21.24-4.68L21.24 0L13.00 0Q9.67 0 7.74-0.67Q5.80-1.35 4.32-3.01L4.32-3.01Q1.48-6.17 1.48-11.03L1.48-11.03Q1.48-16.88 4.86-19.75L4.86-19.75Q6.21-20.93 8.10-21.42Q9.99-21.91 13.00-21.91L13.00-21.91ZM31.05-13.32L43.74-13.32L43.74-8.64L31.05-8.64Q31.23-6.48 32.44-5.58Q33.66-4.68 36.41-4.68L36.41-4.68L43.74-4.68L43.74 0L35.73 0Q33.12 0 31.48-0.47Q29.83-0.94 28.39-2.02L28.39-2.02Q24.39-5.13 24.39-11.25L24.39-11.25Q24.39-15.21 26.50-18.18L26.50-18.18Q27.94-20.20 29.97-21.06Q31.99-21.91 35.28-21.91L35.28-21.91L43.74-21.91L43.74-17.23L35.73-17.23Q33.25-17.23 32.27-16.40Q31.27-15.57 31.05-13.32L31.05-13.32ZM48.64 0L48.64-21.91L57.55-21.91Q60.30-21.91 61.81-21.53Q63.31-21.15 64.31-20.25L64.31-20.25Q65.30-19.35 65.70-18Q66.10-16.65 66.10-14.13L66.10-14.13L66.10-12.01L60.30-12.01L60.30-13.18Q60.30-15.52 59.49-16.38Q58.68-17.23 56.38-17.23L56.38-17.23L54.67-17.23L54.67 0L48.64 0ZM67.50-21.91L71.33-21.91L71.33-30.02L77.36-30.02L77.36-21.91L82.94-21.91L82.94-17.23L77.36-17.23L77.36-9.27Q77.36-6.48 77.85-5.76L77.85-5.76Q78.61-4.68 80.55-4.68L80.55-4.68L82.94-4.68L82.94 0L78.57 0Q74.66 0 72.99-1.89Q71.33-3.78 71.33-8.23L71.33-8.23L71.33-17.23L67.50-17.23L67.50-21.91ZM96.08-21.91L101.75-21.91L101.75-30.02L107.73-30.02L107.73 0L97.38 0Q94.23 0 92.61-0.49L92.61-0.49Q88.78-1.71 86.90-5.26L86.90-5.26Q85.59-7.65 85.59-11.12L85.59-11.12Q85.59-16.74 89.37-19.84L89.37-19.84Q91.84-21.91 96.08-21.91L96.08-21.91ZM97.38-4.68L101.75-4.68L101.75-17.23L97.38-17.23Q94.50-17.23 93.02-15.30L93.02-15.30Q91.71-13.68 91.71-11.12L91.71-11.12Q91.71-7.38 93.87-5.76L93.87-5.76Q95.36-4.68 97.38-4.68L97.38-4.68Z"
|
||||
fill="#2c3e50"
|
||||
transform="translate(28, 430) scale(4,4)"
|
||||
|
||||
></path>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -20,7 +20,7 @@ const icons = {
|
||||
DeleteOutlined
|
||||
}
|
||||
export default function (app) {
|
||||
_.forEach(icons, item => {
|
||||
app.component(item.name, item)
|
||||
_.forEach(icons, (item, key) => {
|
||||
app.component(key, item)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import components from './components'
|
||||
const app = createApp(App)
|
||||
app.config.productionTip = false
|
||||
app.use(i18n)
|
||||
app.use(Antd)
|
||||
icons(app)
|
||||
app.use(Antd)
|
||||
app.use(components)
|
||||
app.use(router).mount('#app')
|
||||
|
||||
@@ -160,10 +160,10 @@
|
||||
|
||||
<d-container>
|
||||
<template #header>
|
||||
<div><a-button @click="exportsToZip">导出可执行项目</a-button></div>
|
||||
<br/>
|
||||
<div> <a-button @click="exportsToJson">复制options.json</a-button></div>
|
||||
<div><a-button @click="exportsToZip">导出为可执行项目</a-button></div>
|
||||
<br/>
|
||||
<!-- <div> <a-button @click="exportsToJson">复制options.json</a-button></div>-->
|
||||
<!-- <br/>-->
|
||||
</template>
|
||||
<pre class="json">{{options}}</pre>
|
||||
|
||||
|
||||
@@ -1,9 +1,47 @@
|
||||
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
// 设置不参与构建的库
|
||||
const externals = {}
|
||||
// cdnDependencies.forEach(pkg => { externals[pkg.name] = pkg.library })
|
||||
|
||||
module.exports = {
|
||||
lintOnSave: true,
|
||||
pages: {
|
||||
index: {
|
||||
entry: 'src/main.js',
|
||||
// template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
|
||||
title: 'Cert-D'
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
proxy: {
|
||||
'/': {
|
||||
target: 'http://localhost:3000/'
|
||||
}
|
||||
}
|
||||
},
|
||||
css: {
|
||||
loaderOptions: {
|
||||
less: {
|
||||
javascriptEnabled: true
|
||||
}
|
||||
}
|
||||
},
|
||||
configureWebpack: config => {
|
||||
const configNew = {}
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
configNew.externals = externals
|
||||
configNew.plugins = [
|
||||
// gzip
|
||||
new CompressionWebpackPlugin({
|
||||
filename: '[path].gz[query]',
|
||||
test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
|
||||
threshold: 5120,
|
||||
minRatio: 0.8,
|
||||
deleteOriginalAssets: false
|
||||
})
|
||||
]
|
||||
}
|
||||
return configNew
|
||||
}
|
||||
}
|
||||
|
||||