mirror of
https://github.com/certd/certd.git
synced 2026-04-08 00:51:00 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df98463325 | ||
|
|
f7492db8bd | ||
|
|
70b46d4a8f | ||
|
|
411486e1e7 |
@@ -124,6 +124,7 @@ export default defineConfig({
|
||||
{text: "子域名托管", link: "/guide/use/cert/subdomain.md"},
|
||||
{text: "流水线有效期", link: "/guide/use/pipeline/valid.md"},
|
||||
{text: "IP证书申请", link: "/guide/use/cert/ip.md"},
|
||||
{text: "企业模式", link: "/guide/use/mode/enterprise.md"},
|
||||
{text: "插件开发", link: "/guide/use/dev/plugin.md"},
|
||||
]
|
||||
},
|
||||
|
||||
40
docs/guide/use/mode/enterprise.md
Normal file
40
docs/guide/use/mode/enterprise.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 企业模式(项目管理)
|
||||
|
||||
## 模式简介
|
||||
Certd支持两种管理模式,`SaaS模式(默认)`和`企业模式`。
|
||||
|
||||
|
||||

|
||||
|
||||
## SaaS模式
|
||||
* 默认的模式,每个用户管理自己的流水线和授权资源,每个用户独立使用。
|
||||
* Certd系统作为SaaS提供证书自动申请部署服务,您的客户注册即可使用,无需自己部署
|
||||
|
||||
|
||||
## 企业模式
|
||||
|
||||
* 通过项目合作管理流水线证书和授权资源,所有用户视为企业内部员工。
|
||||
|
||||
* 当你想在企业内部使用,企业内部有多个项目,各个项目成员共同管理项目资源和证书时可以启用此模式
|
||||
|
||||
* 需要在"系统设置->管理模式"中开启`企业模式`
|
||||
|
||||

|
||||
|
||||
|
||||
::: warning
|
||||
* 建议在开始使用时固定一个合适的模式,之后就不要随意切换了。
|
||||
* 商业版不能使用企业模式,因为商业版提供功能价值在于SaaS服务,与企业模式冲突
|
||||
:::
|
||||
|
||||
### 数据迁移
|
||||
模式之间数据不互通,您可以通过个人数据迁移功能将数据转到项目之下
|
||||
|
||||
#### 个人数据迁移到项目
|
||||
注意:此操作不可逆,请谨慎操作
|
||||

|
||||
|
||||
#### 流水线数据转到其他项目
|
||||
项目之间流水线数据可以转移,依赖的授权数据会同步复制一份
|
||||
|
||||

|
||||
BIN
docs/guide/use/mode/images/admin_mode.jpg
Normal file
BIN
docs/guide/use/mode/images/admin_mode.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
docs/guide/use/mode/images/move.png
Normal file
BIN
docs/guide/use/mode/images/move.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
BIN
docs/guide/use/mode/images/transfer.jpg
Normal file
BIN
docs/guide/use/mode/images/transfer.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
@@ -76,10 +76,12 @@ export abstract class BaseService<T> {
|
||||
* @param where
|
||||
*/
|
||||
async delete(ids: string | any[], where?: any) {
|
||||
const idArr = this.resolveIdArr(ids);
|
||||
let idArr = this.resolveIdArr(ids);
|
||||
idArr = this.filterIds(idArr);
|
||||
if (idArr.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.getRepository().delete({
|
||||
id: In(idArr),
|
||||
...where,
|
||||
@@ -94,7 +96,9 @@ export abstract class BaseService<T> {
|
||||
}
|
||||
if (typeof ids === 'string') {
|
||||
return ids.split(',');
|
||||
} else {
|
||||
} else if(!Array.isArray(ids)){
|
||||
return [ids];
|
||||
}else {
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +221,7 @@ export abstract class BaseService<T> {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
ids = this.filterIds(ids);
|
||||
const res = await this.getRepository().find({
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
@@ -234,7 +239,16 @@ export abstract class BaseService<T> {
|
||||
throw new PermissionException('权限不足');
|
||||
}
|
||||
|
||||
filterIds(ids: any[]) {
|
||||
if (!ids) {
|
||||
throw new ValidateException('ids不能为空');
|
||||
}
|
||||
return ids.filter((item) => {
|
||||
return item!=null && item != ""
|
||||
});
|
||||
}
|
||||
async batchDelete(ids: number[], userId: number,projectId?:number) {
|
||||
ids = this.filterIds(ids);
|
||||
if(userId!=null){
|
||||
const list = await this.getRepository().find({
|
||||
where: {
|
||||
|
||||
@@ -204,13 +204,13 @@ const vipTypeDefine: any = {
|
||||
desc: t("vip.community_free_version"),
|
||||
type: "free",
|
||||
icon: "lucide:package-open",
|
||||
privilege: [t("vip.unlimited_certificate_application"), t("vip.unlimited_domain_count"), t("vip.unlimited_certificate_pipelines"), t("vip.common_deployment_plugins"), t("vip.email_webhook_notifications")],
|
||||
privilege: t("vip.free_privilege").split("\n"),
|
||||
},
|
||||
plus: {
|
||||
title: t("vip.professional_edition"),
|
||||
desc: t("vip.open_source_support"),
|
||||
type: "plus",
|
||||
privilege: [t("vip.vip_group_priority"), t("vip.unlimited_site_certificate_monitoring"), t("vip.more_notification_methods"), t("vip.plugins_fully_open")],
|
||||
privilege: t("vip.plus_privilege").split("\n"),
|
||||
trial: {
|
||||
title: t("vip.click_to_get_7_day_trial"),
|
||||
click: () => {
|
||||
@@ -227,7 +227,7 @@ const vipTypeDefine: any = {
|
||||
desc: t("vip.commercial_license"),
|
||||
type: "comm",
|
||||
icon: "vaadin:handshake",
|
||||
privilege: [t("vip.all_pro_privileges"), t("vip.allow_commercial_use_modify_logo_title"), t("vip.data_statistics"), t("vip.plugin_management"), t("vip.unlimited_multi_users"), t("vip.support_user_payment")],
|
||||
privilege: t("vip.comm_privilege").split("\n"),
|
||||
priceText: props.productInfo.comm.priceText || `¥${props.productInfo.comm.price}/${t("vip.years")}`,
|
||||
discountText: props.productInfo.comm.discountText || `¥${props.productInfo.comm.price3}/3${t("vip.years")}`,
|
||||
tooltip: props.productInfo.comm.tooltip,
|
||||
|
||||
@@ -53,6 +53,7 @@ export default {
|
||||
unlimited_certificate_pipelines: "Unlimited certificate pipelines",
|
||||
common_deployment_plugins: "Common host, cloud platform, CDN, Baota, 1Panel deployment plugins",
|
||||
email_webhook_notifications: "Email, webhook notification methods",
|
||||
free_privilege: "Unlimited certificate applications\nUnlimited domain count\nUnlimited certificate pipelines\nCommon host, cloud platform, CDN, Baota, 1Panel deployment plugins\nEmail, webhook notification methods",
|
||||
|
||||
professional_edition: "Professional Edition",
|
||||
open_source_support: "Open source requires your sponsorship support",
|
||||
@@ -60,6 +61,7 @@ export default {
|
||||
unlimited_site_certificate_monitoring: "Unlimited site certificate monitoring",
|
||||
more_notification_methods: "More notification methods",
|
||||
plugins_fully_open: "All plugins open, including Synology and more",
|
||||
plus_privilege: "Access to VIP group, your requests will have priority\nUnlimited site certificate monitoring\nMore notification methods\nAll plugins open, including Synology and more",
|
||||
click_to_get_7_day_trial: "Click to get 7-day trial",
|
||||
years: "years",
|
||||
afdian_support_vip: "Obtain the permanent professional version coupon",
|
||||
@@ -73,6 +75,7 @@ export default {
|
||||
plugin_management: "Plugin management",
|
||||
unlimited_multi_users: "Unlimited multi-users",
|
||||
support_user_payment: "Supports user payments",
|
||||
comm_privilege: "All professional edition privileges\nAllows commercial use, can modify logo and title\nData statistics\nPlugin management\nUnlimited multi-users\nSupports user payments",
|
||||
contact_author_for_trial: "Buy It Now",
|
||||
activate: "Activate",
|
||||
get_pro_code_after_support: "Go to sponsoring",
|
||||
|
||||
@@ -53,6 +53,7 @@ export default {
|
||||
unlimited_certificate_pipelines: "证书流水线数量无限制",
|
||||
common_deployment_plugins: "常用的主机、云平台、cdn、宝塔、1Panel等部署插件",
|
||||
email_webhook_notifications: "邮件、webhook通知方式",
|
||||
free_privilege: "证书申请无限制\n域名数量无限制\n证书流水线数量无限制\n常用的主机、云平台、cdn、宝塔、1Panel等部署插件\n邮件、webhook通知方式",
|
||||
|
||||
professional_edition: "专业版",
|
||||
open_source_support: "开源需要您的赞助支持,个人和企业内部使用",
|
||||
@@ -60,6 +61,7 @@ export default {
|
||||
unlimited_site_certificate_monitoring: "站点证书监控无限制",
|
||||
more_notification_methods: "更多通知方式",
|
||||
plugins_fully_open: "插件全开放,群晖等更多插件",
|
||||
plus_privilege: "可加VIP群,您的需求将优先实现\n站点证书监控无限制\n更多通知方式\n插件全开放,群晖等更多插件\n企业模式,项目管理\n域名到期监控\n第三方登录,PassKey登录",
|
||||
click_to_get_7_day_trial: "点击获取7天试用",
|
||||
years: "年",
|
||||
afdian_support_vip: "新用户开通永久专业版立享50优惠券",
|
||||
@@ -73,6 +75,7 @@ export default {
|
||||
plugin_management: "插件管理",
|
||||
unlimited_multi_users: "多用户无限制",
|
||||
support_user_payment: "支持用户支付(购买套餐,按流水线条数、域名数量、部署次数计费)",
|
||||
comm_privilege: "拥有专业版所有特权\n允许商用,可修改logo、标题\n数据统计\n插件管理\n多用户无限制\n支持用户支付(购买套餐,按流水线条数、域名数量、部署次数计费)",
|
||||
activate: "激活",
|
||||
get_pro_code_after_support: "前往获取",
|
||||
business_contact_author: "",
|
||||
|
||||
@@ -9,6 +9,7 @@ import yaml from "js-yaml";
|
||||
import { usePluginImport } from "./use-import";
|
||||
import { usePluginConfig } from "./use-config";
|
||||
import { useSettingStore } from "/src/store/settings/index";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
@@ -43,6 +44,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const { openConfigDialog } = usePluginConfig();
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const pluginStore = usePluginStore();
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
@@ -83,6 +85,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
rowKey: "name",
|
||||
remove: {
|
||||
afterRemove: async context => {
|
||||
await pluginStore.reload();
|
||||
},
|
||||
confirmMessage: "确定要删除吗?如果该插件已被使用,删除可能会导致流水线执行失败!",
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
show: true,
|
||||
minWidth: 200,
|
||||
@@ -142,9 +153,6 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
rowKey: "name",
|
||||
},
|
||||
tabs: {
|
||||
name: "type",
|
||||
show: true,
|
||||
|
||||
@@ -58,7 +58,8 @@ export class PluginController extends CrudController<PluginService> {
|
||||
|
||||
@Post('/delete', { description: 'sys:settings:edit' })
|
||||
async delete(@Query('id') id: number) {
|
||||
return super.deleteByIds([id]);
|
||||
const res = await this.service.deleteByIds([id]);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post('/deleteByIds', { description: 'sys:settings:edit' })
|
||||
|
||||
@@ -524,15 +524,12 @@ export class PluginService extends BaseService<PluginEntity> {
|
||||
id: pluginEntity.id
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async deleteByIds(ids: any[]) {
|
||||
ids = this.filterIds(ids);
|
||||
for (const id of ids) {
|
||||
await this.unRegisterById(id)
|
||||
await this.unRegisterById(id);
|
||||
await this.delete(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,7 @@ import { SpaceshipAccess } from "./access.js";
|
||||
import { PageRes, PageSearch } from "@certd/pipeline";
|
||||
|
||||
export type SpaceshipRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
content: string;
|
||||
domainId: string;
|
||||
};
|
||||
|
||||
@IsDnsProvider({
|
||||
@@ -32,7 +28,7 @@ export class SpaceshipProvider extends AbstractDnsProvider<SpaceshipRecord> {
|
||||
await this.access.getDomainInfo(domain);
|
||||
|
||||
const recordRes = await this.access.doRequest({
|
||||
url: `https://spaceship.dev/api/v1/domains/${domain}/records`,
|
||||
url: `https://spaceship.dev/api/v1/dns/records/${domain}`,
|
||||
method: "POST",
|
||||
data: {
|
||||
force: false,
|
||||
@@ -41,40 +37,33 @@ export class SpaceshipProvider extends AbstractDnsProvider<SpaceshipRecord> {
|
||||
type: type,
|
||||
value: value,
|
||||
name: hostRecord,
|
||||
ttl: 300
|
||||
ttl: 60
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
id: recordRes.items[0].id,
|
||||
name: hostRecord,
|
||||
type: type,
|
||||
content: value,
|
||||
domainId: domain
|
||||
};
|
||||
return recordRes;
|
||||
}
|
||||
|
||||
async removeRecord(options: RemoveRecordOptions<SpaceshipRecord>): Promise<void> {
|
||||
const recordRes = options.recordRes;
|
||||
this.logger.info("删除域名解析:", recordRes);
|
||||
const recordReq = options.recordReq;
|
||||
this.logger.info("删除域名解析:", recordReq);
|
||||
|
||||
await this.access.doRequest({
|
||||
url: `https://spaceship.dev/api/v1/domains/${recordRes.domainId}/records`,
|
||||
// https://spaceship.dev/api/v1/dns/records/xxx.net
|
||||
url: `https://spaceship.dev/api/v1/dns/records/${recordReq.domain}`,
|
||||
method: "DELETE",
|
||||
data: {
|
||||
Records: [
|
||||
{
|
||||
type: recordRes.type,
|
||||
value: recordRes.content,
|
||||
name: recordRes.name
|
||||
}
|
||||
]
|
||||
}
|
||||
data: [
|
||||
{
|
||||
type: recordReq.type,
|
||||
value: recordReq.value,
|
||||
name: recordReq.hostRecord
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.logger.info("删除域名解析成功:", recordRes.name);
|
||||
this.logger.info("删除域名解析成功:", JSON.stringify(recordReq));
|
||||
}
|
||||
|
||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||
|
||||
@@ -1 +1 @@
|
||||
00:29
|
||||
01:53
|
||||
|
||||
Reference in New Issue
Block a user