mirror of
https://github.com/certd/certd.git
synced 2026-07-15 10:17:33 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 431afd618f | |||
| 6d5e5bd692 | |||
| ffd2e8149e | |||
| 2ab92dc13e | |||
| 7f6a8bc87e | |||
| b1ff163a28 | |||
| 440d55ccb8 | |||
| 285532d431 | |||
| f2c47459f8 | |||
| 49703f08e5 | |||
| 1d0aa9573b | |||
| b2014cf88b | |||
| a0e0078bad | |||
| 5ebca21c32 | |||
| 970aea90c9 | |||
| 3332d2288f |
@@ -17,6 +17,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: sync-to-cnb-dev
|
||||
on:
|
||||
push:
|
||||
branches: ['v2-dev']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||
|
||||
- name: push to cnb # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://cnb.cool/certd/certd.git
|
||||
git push --set-upstream upstream v2-dev
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: sync-to-cnb
|
||||
on:
|
||||
push:
|
||||
branches: ['v2']
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
git config --global user.email "xiaojunnuo@qq.com"
|
||||
|
||||
- name: Set git token # 3. 给git命令设置token,用于push到目标仓库
|
||||
uses: de-vri-es/setup-git-credentials@v2
|
||||
with: # token 格式为: username:password
|
||||
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
|
||||
|
||||
- name: push to cnb # 4. 执行同步
|
||||
run: |
|
||||
git remote add upstream https://cnb.cool/certd/certd.git
|
||||
git push --set-upstream upstream v2
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
ref: v2-dev
|
||||
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
|
||||
run: |
|
||||
git config --global user.name "xiaojunnuo"
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
|
||||
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
|
||||
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
|
||||
|
||||
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -70,5 +70,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
00:48
|
||||
01:09
|
||||
|
||||
@@ -47,5 +47,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -31,5 +31,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -56,5 +56,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -32,5 +32,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -64,5 +64,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -57,5 +57,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "aacee4a94c2e2481be8a307fb5a6a5d04a09dda7"
|
||||
"gitHead": "5f9341ad8edd08bb553c2d04e71f0838032e727f"
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ const loading = ref(false);
|
||||
const pagerRef: Ref = ref({
|
||||
pageNo: 1,
|
||||
total: 0,
|
||||
pageSize: props.pageSize || 100,
|
||||
pageSize: props.pageSize || 50,
|
||||
});
|
||||
const getOptions = async () => {
|
||||
if (loading.value) {
|
||||
@@ -182,7 +182,7 @@ const getOptions = async () => {
|
||||
pagerRef.value.pageNo = res.pageNo ?? 1;
|
||||
}
|
||||
if (res.pageSize != null) {
|
||||
pagerRef.value.pageSize = res.pageSize ?? 100;
|
||||
pagerRef.value.pageSize = res.pageSize ?? pageSize;
|
||||
}
|
||||
if (res.total != null) {
|
||||
pagerRef.value.total = res.total ?? list.length;
|
||||
@@ -235,9 +235,10 @@ watch(
|
||||
const { form } = value;
|
||||
const oldForm: any = oldValue?.form;
|
||||
let changed = oldForm == null || optionsRef.value.length == 0;
|
||||
debugger;
|
||||
if (props.watches && props.watches.length > 0) {
|
||||
for (const key of props.watches) {
|
||||
if (oldForm && form[key] != oldForm[key]) {
|
||||
if (oldForm && JSON.stringify(form[key]) != JSON.stringify(oldForm[key])) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -795,6 +795,8 @@ export default {
|
||||
reverseProxyEmpty: "No reverse proxy list configured",
|
||||
environmentVars: "Environment Variables",
|
||||
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
|
||||
|
||||
bindUrl: "Bind URL",
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
@@ -804,6 +804,7 @@ export default {
|
||||
reverseProxyEmpty: "未配置反向代理",
|
||||
environmentVars: "环境变量",
|
||||
environmentVarsHelper: "配置运行时环境变量,每行一个,格式:KEY=VALUE",
|
||||
bindUrl: "绑定URL",
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
@@ -272,14 +272,27 @@ export const useSettingStore = defineStore({
|
||||
},
|
||||
async checkUrlBound() {
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
if (!userStore.isAdmin) {
|
||||
return;
|
||||
}
|
||||
const bindUrl = this.installInfo.bindUrl;
|
||||
const bindUrl2 = this.installInfo.bindUrl2;
|
||||
if (!bindUrl) {
|
||||
//绑定url
|
||||
await this.doBindUrl("url");
|
||||
} else {
|
||||
//检查当前url 是否与绑定的url一致
|
||||
const url = window.location.href;
|
||||
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
|
||||
this.openBindUrlModal();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openBindUrlModal() {
|
||||
const event: any = { ModalRef: null };
|
||||
mitter.emit("getModal", event);
|
||||
const Modal = event.ModalRef;
|
||||
let modalRef: any = null;
|
||||
const bindUrl = this.installInfo.bindUrl;
|
||||
const bindUrl2 = this.installInfo.bindUrl2;
|
||||
|
||||
@@ -289,57 +302,47 @@ export const useSettingStore = defineStore({
|
||||
modalRef.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
if (!bindUrl) {
|
||||
//绑定url
|
||||
await this.doBindUrl("url");
|
||||
} else {
|
||||
//检查当前url 是否与绑定的url一致
|
||||
const url = window.location.href;
|
||||
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
|
||||
modalRef = Modal.warning({
|
||||
title: "URL地址未绑定,是否绑定此地址?",
|
||||
width: 500,
|
||||
keyboard: false,
|
||||
content: () => {
|
||||
return (
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>
|
||||
绑定地址1:
|
||||
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
|
||||
</span>
|
||||
<a-button type="primary" onClick={() => doBindRequest("url")}>
|
||||
绑定到地址1
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<span>
|
||||
绑定地址2:
|
||||
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
|
||||
</span>
|
||||
<a-button type="primary" onClick={() => doBindRequest("url2")}>
|
||||
绑定到地址2
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
onOk: async () => {
|
||||
// await this.doBindUrl();
|
||||
window.location.href = bindUrl;
|
||||
},
|
||||
okButtonProps: {
|
||||
danger: true,
|
||||
},
|
||||
okText: "不,回到原来的地址",
|
||||
cancelText: "不,回到原来的地址",
|
||||
onCancel: () => {
|
||||
window.location.href = bindUrl;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
const modalRef: any = Modal.warning({
|
||||
title: "URL地址未绑定,是否绑定此地址?",
|
||||
width: 500,
|
||||
keyboard: false,
|
||||
content: () => {
|
||||
return (
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>
|
||||
绑定地址1:
|
||||
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
|
||||
</span>
|
||||
<a-button type="primary" onClick={() => doBindRequest("url")}>
|
||||
绑定到地址1
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<span>
|
||||
绑定地址2:
|
||||
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
|
||||
</span>
|
||||
<a-button type="primary" onClick={() => doBindRequest("url2")}>
|
||||
绑定到地址2
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
onOk: async () => {
|
||||
// await this.doBindUrl();
|
||||
window.location.href = bindUrl;
|
||||
},
|
||||
okButtonProps: {
|
||||
danger: true,
|
||||
},
|
||||
okText: "不,回到原来的地址",
|
||||
cancelText: "不,回到原来的地址",
|
||||
onCancel: () => {
|
||||
window.location.href = bindUrl;
|
||||
},
|
||||
});
|
||||
},
|
||||
async loadProductInfo() {
|
||||
try {
|
||||
|
||||
@@ -18,6 +18,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
form.type = props.type;
|
||||
delete form.access;
|
||||
delete form.keyId;
|
||||
const res = await context.api.UpdateObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
@@ -30,6 +32,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
form.type = props.type;
|
||||
delete form.access;
|
||||
const res = await context.api.AddObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
@@ -70,6 +73,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
width: "1050px",
|
||||
},
|
||||
},
|
||||
|
||||
rowHandle: {
|
||||
width: 200,
|
||||
},
|
||||
@@ -89,6 +93,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}, // 点击行
|
||||
};
|
||||
},
|
||||
remove: {
|
||||
confirmMessage: "授权如果已经被使用,可能会导致流水线无法正常运行,请谨慎操作",
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
|
||||
@@ -15,6 +15,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const editRequest = async (req: EditReq) => {
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
delete form.access;
|
||||
const res = await api.UpdateObj(form);
|
||||
return res;
|
||||
};
|
||||
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
delete form.access;
|
||||
const res = await api.AddObj(form);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const editRequest = async (req: EditReq) => {
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
delete form.body;
|
||||
const res = await api.UpdateObj(form);
|
||||
return res;
|
||||
};
|
||||
@@ -26,6 +27,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
delete form.body;
|
||||
const res = await api.AddObj(form);
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const editRequest = async (req: EditReq) => {
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
delete form.body;
|
||||
const res = await api.UpdateObj(form);
|
||||
return res;
|
||||
};
|
||||
@@ -21,6 +22,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
delete form.body;
|
||||
const res = await api.AddObj(form);
|
||||
return res;
|
||||
};
|
||||
|
||||
+2
@@ -14,6 +14,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const editRequest = async (req: EditReq) => {
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
delete form.body;
|
||||
const res = await context.api.UpdateObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
delete form.body;
|
||||
const res = await context.api.AddObj(form);
|
||||
lastResRef.value = res;
|
||||
return res;
|
||||
|
||||
@@ -29,15 +29,15 @@ const pipelineOptions: PipelineOptions = {
|
||||
onLoaded(detail);
|
||||
return {
|
||||
pipeline: {
|
||||
id: detail.pipeline.id,
|
||||
stages: [],
|
||||
triggers: [],
|
||||
...JSON.parse(detail.pipeline.content || "{}"),
|
||||
type: detail.pipeline.type,
|
||||
from: detail.pipeline.from,
|
||||
id: detail.pipeline.id,
|
||||
userId: detail.pipeline.userId,
|
||||
projectId: detail.pipeline.projectId,
|
||||
},
|
||||
type: detail.pipeline.type,
|
||||
from: detail.pipeline.from,
|
||||
validTime: detail.pipeline.validTime,
|
||||
webhookKey: detail.pipeline.webhookKey,
|
||||
id: detail.pipeline.id,
|
||||
|
||||
@@ -49,6 +49,7 @@ defineOptions({
|
||||
|
||||
const route = useRoute();
|
||||
const projectIdStr = route.query.projectId as string;
|
||||
const migrate = route.query.migrate as string;
|
||||
let projectId = Number(projectIdStr);
|
||||
const projectStore = useProjectStore();
|
||||
if (!projectId) {
|
||||
@@ -116,7 +117,11 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
await loadProjectDetail();
|
||||
crudExpose.doRefresh();
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
if (migrate === "true") {
|
||||
openTransferDialog();
|
||||
}
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
</div>
|
||||
<div class="helper">SaaS模式:每个用户管理自己的流水线和授权资源,独立使用。</div>
|
||||
<div class="helper">企业模式:通过项目合作管理流水线证书和授权资源,所有用户视为企业内部员工。</div>
|
||||
<div class="helper"><a @click="adminModeIntroOpen = true">更多管理模式介绍</a></div>
|
||||
<div class="helper text-red-500">建议在开始使用时固定一个合适的模式,之后就不要随意切换了。</div>
|
||||
<div v-if="formState.public.adminMode === 'enterprise'" class="helper">设置为企业模式之后,之前创建的个人数据不会显示,您可以选择<a @click="goCurrentProject"> 将个人数据迁移到项目</a></div>
|
||||
<div v-if="settingsStore.isComm" class="helper text-red-500">商业版不建议设置为企业模式,除非你确定要转成企业内部使用</div>
|
||||
<div><a @click="adminModeIntroOpen = true"> 更多管理模式介绍</a></div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }">
|
||||
@@ -33,6 +34,7 @@ import { useI18n } from "/src/locales";
|
||||
import { dict } from "@fast-crud/fast-crud";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
import AdminModeIntro from "/@/views/sys/enterprise/project/intro.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
const { t } = useI18n();
|
||||
|
||||
defineOptions({
|
||||
@@ -82,5 +84,15 @@ const onFinish = async (form: any) => {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const goCurrentProject = () => {
|
||||
router.push({
|
||||
path: "/certd/project/detail",
|
||||
query: {
|
||||
migrate: "true",
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<pre class="helper pre">{{ t("certd.sys.setting.passkeyEnabledHelper", [bindDomain]) }}</pre>
|
||||
<div v-if="!bindDomainIsSame" class="text-red-500 text-sm mt-2">
|
||||
{{ t("certd.sys.setting.passkeyHostnameNotSame") }}
|
||||
{{ t("certd.sys.setting.passkeyHostnameNotSame") }} <a-button class="ml-2" size="small" type="primary" @click="settingsStore.openBindUrlModal()">{{ t("certd.sys.setting.bindUrl") }}</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
|
||||
|
||||
@@ -274,6 +274,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
RunnableCollection.initPipelineRunnableType(pipeline);
|
||||
pipeline.userId = bean.userId;
|
||||
pipeline.projectId = bean.projectId;
|
||||
if (bean.id) {
|
||||
pipeline.id = bean.id;
|
||||
}
|
||||
let domains = [];
|
||||
if (pipeline.stages) {
|
||||
RunnableCollection.each(pipeline.stages, (runnable: any) => {
|
||||
@@ -1074,6 +1077,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (!isPlus()) {
|
||||
throw new NeedVIPException("此功能需要升级专业版");
|
||||
}
|
||||
//允许管理员修改,userId=null
|
||||
const query: any = {}
|
||||
if (userId && userId > 0) {
|
||||
query.userId = userId;
|
||||
@@ -1126,6 +1130,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (!isPlus()) {
|
||||
throw new NeedVIPException("此功能需要升级专业版");
|
||||
}
|
||||
//允许管理员修改,userId=null
|
||||
const query: any = {}
|
||||
if (userId && userId > 0) {
|
||||
query.userId = userId;
|
||||
@@ -1164,9 +1169,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
if (!isPlus()) {
|
||||
throw new NeedVIPException("此功能需要升级专业版");
|
||||
}
|
||||
|
||||
if (userId == null || ids.length === 0) {
|
||||
return;
|
||||
//允许userId为空,为空则为管理员触发
|
||||
if (ids.length === 0) {
|
||||
throw new Error("参数错误 ids 不能为空");
|
||||
}
|
||||
const where: any = {
|
||||
id: In(ids),
|
||||
|
||||
+156
-9
@@ -3,6 +3,7 @@ import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from
|
||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||
import { VolcengineAccess } from "../access.js";
|
||||
import { VolcengineClient } from "../ve-client.js";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@IsTaskPlugin({
|
||||
name: "VolcengineDeployToALB",
|
||||
@@ -32,6 +33,7 @@ export class VolcengineDeployToALB extends AbstractTaskPlugin {
|
||||
certDomains!: string[];
|
||||
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: "Access授权",
|
||||
helper: "火山引擎AccessKeyId、AccessKeySecret",
|
||||
@@ -126,6 +128,22 @@ export class VolcengineDeployToALB extends AbstractTaskPlugin {
|
||||
listenerList!: string | string[];
|
||||
|
||||
|
||||
@TaskInput({
|
||||
title: "证书部署类型",
|
||||
helper: "选择部署默认证书还是扩展证书",
|
||||
component: {
|
||||
name: "a-select",
|
||||
options: [
|
||||
{ label: "默认证书", value: "default" },
|
||||
{ label: "扩展证书", value: "extension" }
|
||||
]
|
||||
},
|
||||
value: "default",
|
||||
required: true
|
||||
})
|
||||
certType!: string;
|
||||
|
||||
|
||||
async onInstance() {
|
||||
}
|
||||
|
||||
@@ -149,20 +167,101 @@ export class VolcengineDeployToALB extends AbstractTaskPlugin {
|
||||
const service = await this.getAlbService();
|
||||
for (const listener of this.listenerList) {
|
||||
this.logger.info(`开始部署监听器${listener}证书`);
|
||||
await service.request({
|
||||
action: "ModifyListenerAttributes",
|
||||
query: {
|
||||
ListenerId: listener,
|
||||
CertificateSource: "cert_center",
|
||||
CertCenterCertificateId: certId
|
||||
}
|
||||
});
|
||||
this.logger.info(`部署监听器${listener}证书成功`);
|
||||
if (this.certType === "default") {
|
||||
// 部署默认证书
|
||||
const res = await service.request({
|
||||
action: "ModifyListenerAttributes",
|
||||
query: {
|
||||
ListenerId: listener,
|
||||
CertificateSource: "cert_center",
|
||||
CertCenterCertificateId: certId
|
||||
}
|
||||
});
|
||||
this.logger.info(`部署监听器${listener}默认证书成功,res:${JSON.stringify(res)}`);
|
||||
} else {
|
||||
// 部署扩展证书
|
||||
await this.deployExtensionCertificate(service, listener, certId as string);
|
||||
}
|
||||
await this.ctx.utils.sleep(5000);
|
||||
}
|
||||
|
||||
this.logger.info("部署完成");
|
||||
}
|
||||
|
||||
private async deployExtensionCertificate(service: any, listenerId: string, certId: string) {
|
||||
// 获取监听器当前的扩展证书列表
|
||||
const domainExtensions = await this.getListenerDomainExtensions(service, listenerId);
|
||||
|
||||
// 删除过期的扩展证书
|
||||
try {
|
||||
await this.deleteExpiredExtensions(service, listenerId, domainExtensions);
|
||||
} catch (error) {
|
||||
this.logger.error(`删除过期扩展证书失败:${error.message ||error}`);
|
||||
}
|
||||
|
||||
// 新增扩展证书
|
||||
const query: any = {
|
||||
ListenerId: listenerId,
|
||||
"DomainExtensions.1.Action": "create",
|
||||
"DomainExtensions.1.CertificateSource": "cert_center",
|
||||
"DomainExtensions.1.CertCenterCertificateId": certId
|
||||
};
|
||||
|
||||
// 如果有证书域名信息,添加到扩展证书中
|
||||
if (this.certDomains && this.certDomains.length > 0) {
|
||||
query["DomainExtensions.1.Domain"] = this.certDomains[0];
|
||||
}
|
||||
|
||||
await service.request({
|
||||
action: "ModifyListenerAttributes",
|
||||
query: query
|
||||
});
|
||||
this.logger.info(`部署监听器${listenerId}扩展证书成功`);
|
||||
}
|
||||
|
||||
private async getListenerDomainExtensions(service: any, listenerId: string): Promise<any[]> {
|
||||
const res = await service.request({
|
||||
action: "DescribeListenerAttributes",
|
||||
method: "GET",
|
||||
query: {
|
||||
ListenerId: listenerId
|
||||
}
|
||||
});
|
||||
|
||||
return res.Result.DomainExtensions || [];
|
||||
}
|
||||
|
||||
private async deleteExpiredExtensions(service: any, listenerId: string, domainExtensions: any[]) {
|
||||
const expiredExtensions = [];
|
||||
for (const ext of domainExtensions) {
|
||||
if (!await this.isCertificateExpired(ext)) {
|
||||
expiredExtensions.push(ext);
|
||||
}
|
||||
}
|
||||
if (expiredExtensions.length === 0) {
|
||||
this.logger.info(`没有过期的扩展证书,跳过删除`);
|
||||
return;
|
||||
}
|
||||
|
||||
const query: any = {
|
||||
ListenerId: listenerId
|
||||
};
|
||||
expiredExtensions.forEach((ext, index) => {
|
||||
const idx = index + 1;
|
||||
query[`DomainExtensions.${idx}.Action`] = "delete";
|
||||
query[`DomainExtensions.${idx}.DomainExtensionId`] = ext.DomainExtensionId;
|
||||
});
|
||||
|
||||
this.logger.info(`准备删除过期扩展证书,数量:${expiredExtensions.length}个,query:${JSON.stringify(query)}`);
|
||||
|
||||
await service.request({
|
||||
action: "ModifyListenerAttributes",
|
||||
query: query
|
||||
});
|
||||
this.logger.info(`删除${expiredExtensions.length}个过期扩展证书成功`);
|
||||
await this.ctx.utils.sleep(5000);
|
||||
}
|
||||
|
||||
|
||||
private async getCertService(access: VolcengineAccess) {
|
||||
const client = new VolcengineClient({
|
||||
@@ -189,6 +288,54 @@ export class VolcengineDeployToALB extends AbstractTaskPlugin {
|
||||
return service;
|
||||
}
|
||||
|
||||
private async isCertificateExpired(extension: any): Promise<boolean> {
|
||||
try {
|
||||
let certificateId: string;
|
||||
|
||||
// 根据证书来源获取证书ID
|
||||
if (extension.CertificateSource === "cert_center") {
|
||||
certificateId = extension.CertCenterCertificateId;
|
||||
} else if (extension.CertificateSource === "alb") {
|
||||
this.logger.warn(`ALB证书不支持过期检查,跳过`);
|
||||
return false;
|
||||
} else if (extension.CertificateSource === "pca_leaf") {
|
||||
this.logger.warn(`PCA Leaf证书不支持过期检查,跳过`);
|
||||
return false;
|
||||
} else {
|
||||
this.logger.warn(`未知的证书来源: ${extension.CertificateSource},跳过`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!certificateId) {
|
||||
this.logger.warn(`证书ID为空,跳过`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取证书服务
|
||||
const access = await this.getAccess<VolcengineAccess>(this.accessId);
|
||||
const certService = await this.getCertService(access);
|
||||
|
||||
// 获取证书详情
|
||||
const certDetail = await certService.GetCertificateDetail(certificateId);
|
||||
|
||||
// 判断证书是否过期
|
||||
if (certDetail.NotAfter) {
|
||||
const expireTime = dayjs(certDetail.NotAfter);
|
||||
const now = dayjs();
|
||||
const isExpired = expireTime.isBefore(now);
|
||||
if (isExpired) {
|
||||
this.logger.info(`证书 ${certificateId} 已过期,过期时间: ${expireTime.toISOString()}`);
|
||||
}
|
||||
return isExpired;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
this.logger.error(`检查证书是否过期失败: ${error.message || error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async onGetListenerList(data: any) {
|
||||
if (!this.accessId) {
|
||||
throw new Error("请选择Access授权");
|
||||
|
||||
@@ -42,6 +42,17 @@ export class VolcengineClient {
|
||||
});
|
||||
return res.Result.InstanceId || res.Result.RepeatId;
|
||||
};
|
||||
|
||||
service.GetCertificateDetail = async (certificateId: string) => {
|
||||
const res = await service.request({
|
||||
action: "CertificateGetInstance",
|
||||
method: "POST",
|
||||
body: {
|
||||
InstanceId: certificateId
|
||||
}
|
||||
});
|
||||
return res.Result;
|
||||
};
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
01:13
|
||||
00:52
|
||||
|
||||
@@ -1 +1 @@
|
||||
01:30
|
||||
01:11
|
||||
|
||||
Reference in New Issue
Block a user