mirror of
https://github.com/certd/certd.git
synced 2026-04-24 20:57:26 +08:00
chore: license说明
This commit is contained in:
@@ -60,7 +60,14 @@ export async function mine(): Promise<UserInfoRes> {
|
||||
});
|
||||
}
|
||||
return await request({
|
||||
url: "/sys/authority/user/mine",
|
||||
url: "/mine/info",
|
||||
method: "post"
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPlusInfo() {
|
||||
return await request({
|
||||
url: "/mine/plusInfo",
|
||||
method: "post"
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { request } from "/@/api/service";
|
||||
|
||||
export async function doActive(form: any) {
|
||||
return await request({
|
||||
url: "/sys/plus/active",
|
||||
method: "post",
|
||||
data: form
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="layout-vip" :class="{ 'layout-plus': userStore.plusInfo?.isPlus }">
|
||||
<contextHolder />
|
||||
<fs-icon icon="mingcute:vip-1-line"></fs-icon>
|
||||
<div class="text">
|
||||
<span v-if="userStore.plusInfo?.isPlus">
|
||||
<span>专业版</span>
|
||||
<span>{{ expireTime }}</span>
|
||||
</span>
|
||||
<span v-else @click="openUpgrade"> 当前免费版 </span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="tsx" setup>
|
||||
import { ref, reactive } from "vue";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import dayjs from "dayjs";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import * as api from "./api";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const expireTime = ref("");
|
||||
if (userStore.plusInfo?.isPlus) {
|
||||
expireTime.value = dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
const formState = reactive({
|
||||
code: ""
|
||||
});
|
||||
|
||||
async function doActive() {
|
||||
if (!formState.code) {
|
||||
message.error("请输入激活码");
|
||||
throw new Error("请输入激活码");
|
||||
}
|
||||
const res = await api.doActive(formState);
|
||||
if (res) {
|
||||
await userStore.reInit();
|
||||
Modal.success({
|
||||
title: "激活成功",
|
||||
content: `您已成功激活专业版,有效期至:${dayjs(userStore.plusInfo.expireTime).format("YYYY-MM-DD")}`
|
||||
});
|
||||
}
|
||||
}
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
function openUpgrade() {
|
||||
const placeholder = "请输入激活码";
|
||||
modal.confirm({
|
||||
title: "升级专业版",
|
||||
async onOk() {
|
||||
await doActive();
|
||||
},
|
||||
content: () => {
|
||||
return (
|
||||
<div class="mt-10 mb-10">
|
||||
<a-input v-model:value={formState.code} placeholder={placeholder} />
|
||||
<div class="mt-10">
|
||||
<a href="https://afdian.com/a/greper" target="_blank">
|
||||
爱发电赞助获取激活码
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.layout-vip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
&.isPlus {
|
||||
color: #c5913f;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,14 +12,14 @@
|
||||
|
||||
<a-layout class="layout-body">
|
||||
<a-layout-header class="header">
|
||||
<div class="header-buttons">
|
||||
<div class="header-left header-buttons">
|
||||
<div class="menu-fold" @click="asideCollapsedToggle">
|
||||
<MenuUnfoldOutlined v-if="asideCollapsed" />
|
||||
<MenuFoldOutlined v-else />
|
||||
</div>
|
||||
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="frameworkMenus" />
|
||||
<vip-info class="flex-center header-btn"></vip-info>
|
||||
</div>
|
||||
|
||||
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="frameworkMenus" />
|
||||
<div class="header-right header-buttons">
|
||||
<!-- <button-->
|
||||
<!-- w:bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"-->
|
||||
@@ -83,10 +83,11 @@ import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons-vue";
|
||||
import FsThemeSet from "/@/layout/components/theme/index.vue";
|
||||
import { env } from "../utils/util.env";
|
||||
import FsThemeModeSet from "./components/theme/mode-set.vue";
|
||||
import VipInfo from "./components/vip-info/index.vue";
|
||||
export default {
|
||||
name: "LayoutFramework",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
components: { FsThemeSet, MenuFoldOutlined, MenuUnfoldOutlined, FsMenu, FsLocale, FsSourceLink, FsUserInfo, FsTabs, FsThemeModeSet },
|
||||
components: { FsThemeSet, MenuFoldOutlined, MenuUnfoldOutlined, FsMenu, FsLocale, FsSourceLink, FsUserInfo, FsTabs, FsThemeModeSet, VipInfo },
|
||||
setup() {
|
||||
const resourceStore = useResourceStore();
|
||||
const frameworkMenus = computed(() => {
|
||||
@@ -133,6 +134,7 @@ export default {
|
||||
.fs-framework {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
min-width: 1200px;
|
||||
.menu-fold {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -174,34 +176,41 @@ export default {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& > * {
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
}
|
||||
height: 100%;
|
||||
|
||||
& > .header-btn {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
.ant-layout-header.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& > * {
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
}
|
||||
height: 100%;
|
||||
//border-bottom: 1px solid rgba(255, 255, 255, 0);
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
|
||||
& > .header-btn {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
//border-bottom: 1px solid rgba(255, 255, 255, 0);
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.header-right {
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.header-menu {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.header-right {
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.header-menu {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.aside-menu {
|
||||
flex: 1;
|
||||
ui {
|
||||
|
||||
@@ -146,7 +146,6 @@ export default {
|
||||
// position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
padding: 0 16px;
|
||||
margin: 48px 0 24px;
|
||||
text-align: center;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
// 请根据自身业务需要修改
|
||||
const token = userStore.getToken;
|
||||
if (token) {
|
||||
await userStore.init();
|
||||
next();
|
||||
} else {
|
||||
// 没有登录的时候跳转到登录界面
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { store } from "../index";
|
||||
import router from "../../router";
|
||||
// @ts-ignore
|
||||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
// @ts-ignore
|
||||
import * as UserApi from "/src/api/modules/api.user";
|
||||
import { RegisterReq } from "/src/api/modules/api.user";
|
||||
// @ts-ignore
|
||||
import { LoginReq, UserInfoRes } from "/@/api/modules/api.user";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import { mitter } from "/src/utils/util.mitt";
|
||||
import { RegisterReq } from "/src/api/modules/api.user";
|
||||
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfoRes>;
|
||||
token?: string;
|
||||
plusInfo?: PlusInfo;
|
||||
inited: boolean;
|
||||
}
|
||||
|
||||
interface PlusInfo {
|
||||
level: number;
|
||||
expireTime: number;
|
||||
isPlus: boolean;
|
||||
}
|
||||
|
||||
const USER_INFO_KEY = "USER_INFO";
|
||||
@@ -26,7 +33,10 @@ export const useUserStore = defineStore({
|
||||
// user info
|
||||
userInfo: null,
|
||||
// token
|
||||
token: undefined
|
||||
token: undefined,
|
||||
// plus
|
||||
plusInfo: null,
|
||||
inited: false
|
||||
}),
|
||||
getters: {
|
||||
getUserInfo(): UserInfoRes {
|
||||
@@ -73,10 +83,7 @@ export const useUserStore = defineStore({
|
||||
// save token
|
||||
this.setToken(token, expire);
|
||||
// get user info
|
||||
const userInfo = await this.getUserInfoAction();
|
||||
await router.replace("/");
|
||||
mitter.emit("app.login", { userInfo, token: data });
|
||||
return userInfo;
|
||||
return await this.onLoginSuccess(data);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
@@ -86,6 +93,19 @@ export const useUserStore = defineStore({
|
||||
this.setUserInfo(userInfo);
|
||||
return userInfo;
|
||||
},
|
||||
|
||||
async onLoginSuccess(loginData: any) {
|
||||
await this.getUserInfoAction();
|
||||
await this.loadPlusInfo();
|
||||
const userInfo = await this.getUserInfoAction();
|
||||
mitter.emit("app.login", { userInfo, token: loginData, plusInfo: this.plusInfo });
|
||||
await router.replace("/");
|
||||
return userInfo;
|
||||
},
|
||||
|
||||
async loadPlusInfo() {
|
||||
this.plusInfo = await UserApi.getPlusInfo();
|
||||
},
|
||||
/**
|
||||
* @description: logout
|
||||
*/
|
||||
@@ -108,6 +128,19 @@ export const useUserStore = defineStore({
|
||||
await this.logout(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
async init() {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
if (this.getToken) {
|
||||
await this.loadPlusInfo();
|
||||
}
|
||||
this.inited = true;
|
||||
},
|
||||
async reInit() {
|
||||
this.inited = false;
|
||||
await this.init();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -14,9 +14,6 @@ html, body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body{
|
||||
min-width: 1000px;
|
||||
}
|
||||
div#app {
|
||||
height: 100%
|
||||
}
|
||||
@@ -48,6 +45,11 @@ h1, h2, h3, h4, h5, h6 {
|
||||
vertical-align: 0 !important;
|
||||
}
|
||||
|
||||
.flex-center{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.flex-o{
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
alter table cd_access alter column setting type text using setting::text;
|
||||
alter table sys_settings alter column setting type text using setting::text;
|
||||
alter table user_settings alter column setting type text using setting::text;
|
||||
alter table pi_history_log alter column logs type text using logs::text;
|
||||
alter table pi_history alter column pipeline type text using pipeline::text;
|
||||
|
||||
@@ -31,6 +31,10 @@ export const Constants = {
|
||||
code: 10,
|
||||
message: '参数错误',
|
||||
},
|
||||
needvip: {
|
||||
code: 88,
|
||||
message: '需要VIP',
|
||||
},
|
||||
auth: {
|
||||
code: 401,
|
||||
message: '您还未登录或token已过期',
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Constants } from '../constants.js';
|
||||
import { BaseException } from './base-exception.js';
|
||||
/**
|
||||
* 需要vip异常
|
||||
*/
|
||||
export class NeedVIPException extends BaseException {
|
||||
constructor(message) {
|
||||
super('NeedVIPException', Constants.res.needvip.code, message ? message : Constants.res.needvip.message);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import * as staticFile from '@midwayjs/static-file';
|
||||
import * as cron from './modules/plugin/cron/index.js';
|
||||
import * as flyway from '@certd/midway-flyway-js';
|
||||
import cors from '@koa/cors';
|
||||
import { ReportMiddleware } from './middleware/report.js';
|
||||
import { GlobalExceptionMiddleware } from './middleware/global-exception.js';
|
||||
import { PreviewMiddleware } from './middleware/preview.js';
|
||||
import { AuthorityMiddleware } from './middleware/authority.js';
|
||||
@@ -60,7 +59,6 @@ export class MainConfiguration {
|
||||
//this.app.use(bodyParser());
|
||||
//请求日志打印
|
||||
this.app.useMiddleware([
|
||||
ReportMiddleware,
|
||||
//统一异常处理
|
||||
GlobalExceptionMiddleware,
|
||||
//预览模式限制修改id<1000的数据
|
||||
|
||||
@@ -12,7 +12,7 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
|
||||
logger.info('请求开始:', url);
|
||||
try {
|
||||
await next();
|
||||
logger.info('请求完成', url, Date.now() - startTime + 'ms');
|
||||
logger.info('请求完成:', url, Date.now() - startTime + 'ms');
|
||||
} catch (err) {
|
||||
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
|
||||
ctx.status = 200;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Middleware, IMiddleware } from '@midwayjs/core';
|
||||
import { NextFunction, Context } from '@midwayjs/koa';
|
||||
|
||||
@Middleware()
|
||||
export class ReportMiddleware implements IMiddleware<Context, NextFunction> {
|
||||
resolve() {
|
||||
return async (ctx: Context, next: NextFunction) => {
|
||||
// 控制器前执行的逻辑
|
||||
const startTime = Date.now();
|
||||
// 执行下一个 Web 中间件,最后执行到控制器
|
||||
// 这里可以拿到下一个中间件或者控制器的返回值
|
||||
const result = await next();
|
||||
// 控制器之后执行的逻辑
|
||||
ctx.logger.info(
|
||||
`Report in "src/middleware/report.middleware.ts", rt = ${
|
||||
Date.now() - startTime
|
||||
}ms`
|
||||
);
|
||||
// 返回给上一个中间件的结果
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
static getName(): string {
|
||||
return 'report';
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Provide } from '@midwayjs/core';
|
||||
import { IWebMiddleware, IMidwayKoaContext, NextFunction } from '@midwayjs/koa';
|
||||
import { logger } from '../utils/logger.js';
|
||||
|
||||
@Provide()
|
||||
export class ReportMiddleware implements IWebMiddleware {
|
||||
resolve() {
|
||||
return async (ctx: IMidwayKoaContext, next: NextFunction) => {
|
||||
const { url } = ctx;
|
||||
logger.info('请求开始:', url);
|
||||
const startTime = Date.now();
|
||||
await next();
|
||||
if (ctx.status !== 200) {
|
||||
logger.error(
|
||||
'请求失败:',
|
||||
url,
|
||||
ctx.status,
|
||||
Date.now() - startTime + 'ms'
|
||||
);
|
||||
}
|
||||
logger.info('请求完成:', url, ctx.status, Date.now() - startTime + 'ms');
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { Constants } from '../../../basic/constants.js';
|
||||
import { UserService } from '../../authority/service/user-service.js';
|
||||
import { getPlusInfo } from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -24,4 +25,12 @@ export class MineController extends BaseController {
|
||||
await this.userService.changePassword(userId, body);
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/plusInfo', { summary: Constants.per.authOnly })
|
||||
async plusInfo(@Body(ALL) body) {
|
||||
const info = getPlusInfo();
|
||||
return this.ok({
|
||||
...info,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { In, Repository } from 'typeorm';
|
||||
import { BaseService } from '../../../basic/base-service.js';
|
||||
import { PipelineEntity } from '../entity/pipeline.js';
|
||||
import { PipelineDetail } from '../entity/vo/pipeline-detail.js';
|
||||
import { Executor, Pipeline, ResultType, RunHistory } from '@certd/pipeline';
|
||||
import { Executor, isPlus, Pipeline, ResultType, RunHistory } from '@certd/pipeline';
|
||||
import { AccessService } from './access-service.js';
|
||||
import { DbStorage } from './db-storage.js';
|
||||
import { StorageService } from './storage-service.js';
|
||||
@@ -15,9 +15,10 @@ import { HistoryLogEntity } from '../entity/history-log.js';
|
||||
import { HistoryLogService } from './history-log-service.js';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
import { EmailService } from '../../basic/service/email-service.js';
|
||||
import { NeedVIPException } from '../../../basic/exception/vip-exception.js';
|
||||
|
||||
const runningTasks: Map<string | number, Executor> = new Map();
|
||||
|
||||
const freeCount = 10;
|
||||
/**
|
||||
* 证书申请
|
||||
*/
|
||||
@@ -47,6 +48,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async add(bean: PipelineEntity) {
|
||||
if (!isPlus()) {
|
||||
const count = await this.repository.count();
|
||||
if (count >= freeCount) {
|
||||
throw new NeedVIPException('免费版最多只能创建10个pipeline');
|
||||
}
|
||||
}
|
||||
await super.add(bean);
|
||||
return bean;
|
||||
}
|
||||
|
||||
async page(query: any, page: { offset: number; limit: number }, order: any, buildQuery: any) {
|
||||
const result = await super.page(query, page, order, buildQuery);
|
||||
const pipelineIds: number[] = [];
|
||||
@@ -93,6 +105,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
}
|
||||
|
||||
async save(bean: PipelineEntity) {
|
||||
if (!isPlus()) {
|
||||
const count = await this.repository.count();
|
||||
if (count >= 10) {
|
||||
throw new NeedVIPException('免费版最多只能创建10个pipeline');
|
||||
}
|
||||
}
|
||||
await this.clearTriggers(bean.id);
|
||||
if (bean.content) {
|
||||
const pipeline = JSON.parse(bean.content);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { SysSettingsService } from '../service/sys-settings-service.js';
|
||||
import { BaseController } from '../../../basic/base-controller.js';
|
||||
import { appKey, utils, verify } from '@certd/pipeline';
|
||||
import { SysInstallInfo, SysLicenseInfo } from '../service/models.js';
|
||||
import { logger } from '../../../utils/logger.js';
|
||||
|
||||
/**
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/sys/plus')
|
||||
export class SysPlusController extends BaseController {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Post('/active', { summary: 'sys:settings:edit' })
|
||||
async active(@Body(ALL) body) {
|
||||
const { code } = body;
|
||||
const installInfo: SysInstallInfo = await this.sysSettingsService.getSetting(SysInstallInfo);
|
||||
const formData = {
|
||||
appKey: appKey,
|
||||
code,
|
||||
subjectId: installInfo.siteId,
|
||||
};
|
||||
const res: any = await utils.http({
|
||||
url: 'https://api.ai.handsfree.work/activation/active',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
});
|
||||
|
||||
if (res.code > 0) {
|
||||
logger.error('激活失败', res.message);
|
||||
return this.fail(res.message, 1);
|
||||
}
|
||||
|
||||
const license = res.data.license;
|
||||
|
||||
let licenseInfo: SysLicenseInfo = await this.sysSettingsService.getSetting(SysLicenseInfo);
|
||||
if (!licenseInfo) {
|
||||
licenseInfo = new SysLicenseInfo();
|
||||
}
|
||||
licenseInfo.license = license;
|
||||
await this.sysSettingsService.saveSetting(licenseInfo);
|
||||
|
||||
const verifyRes = await verify({
|
||||
subjectId: installInfo.siteId,
|
||||
license,
|
||||
});
|
||||
|
||||
if (!verifyRes.isPlus) {
|
||||
const message = verifyRes.message || '授权码校验失败';
|
||||
logger.error(message);
|
||||
return this.fail(message, 1);
|
||||
}
|
||||
return this.ok(res.data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './plugins/index.js';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './plugin-k8s.js';
|
||||
@@ -0,0 +1,109 @@
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
|
||||
import { CertInfo } from '@certd/plugin-cert';
|
||||
import { K8sClient } from '@certd/lib-k8s';
|
||||
import { K8sAccess } from '../access/index.js';
|
||||
import { appendTimeSuffix } from '../../plugin-aliyun/utils/index.js';
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: 'DeployToK8SIngress',
|
||||
title: 'K8S Ingress证书部署',
|
||||
group: pluginGroups.other.key,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
export class K8STestPlugin extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
title: '命名空间',
|
||||
value: 'default',
|
||||
component: {
|
||||
placeholder: '命名空间',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
namespace!: string;
|
||||
@TaskInput({
|
||||
title: 'ingress名称',
|
||||
value: '',
|
||||
component: {
|
||||
placeholder: 'ingress名称',
|
||||
},
|
||||
required: true,
|
||||
helper: '可以传入一个数组',
|
||||
})
|
||||
ingressName!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '保密字典Id',
|
||||
component: {
|
||||
placeholder: '保密字典Id',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
secretName!: string | string[];
|
||||
|
||||
@TaskInput({
|
||||
title: 'k8s授权',
|
||||
helper: 'kubeconfig',
|
||||
component: {
|
||||
name: 'pi-access-selector',
|
||||
type: 'k8s',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
accessId!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: '域名证书',
|
||||
helper: '请选择前置任务输出的域名证书',
|
||||
component: {
|
||||
name: 'pi-output-selector',
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
|
||||
async onInstance() {}
|
||||
async execute(): Promise<void> {
|
||||
const access: K8sAccess = await this.accessService.getById(this.accessId);
|
||||
const k8sClient = new K8sClient({
|
||||
kubeConfigStr: access.kubeconfig,
|
||||
logger: this.logger,
|
||||
});
|
||||
await this.patchNginxCertSecret({ cert: this.cert, k8sClient });
|
||||
await utils.sleep(3000); // 停留2秒,等待secret部署完成
|
||||
}
|
||||
|
||||
async patchNginxCertSecret(options: { cert: CertInfo; k8sClient: K8sClient }) {
|
||||
const { cert, k8sClient } = options;
|
||||
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 } = this;
|
||||
|
||||
const body: any = {
|
||||
data: {
|
||||
'tls.crt': crtBase64,
|
||||
'tls.key': keyBase64,
|
||||
},
|
||||
metadata: {
|
||||
labels: {
|
||||
certd: appendTimeSuffix('certd'),
|
||||
},
|
||||
},
|
||||
};
|
||||
let secretNames: any = secretName;
|
||||
if (typeof secretName === 'string') {
|
||||
secretNames = [secretName];
|
||||
}
|
||||
for (const secret of secretNames) {
|
||||
await k8sClient.patchSecret({ namespace, secretName: secret, body });
|
||||
this.logger.info(`ingress cert Secret已更新:${secret}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
new K8STestPlugin();
|
||||
Reference in New Issue
Block a user