mirror of https://github.com/certd/certd
pref: 支持子域名托管的域名证书申请
parent
f68af7dcf2
commit
67f956d4a0
|
@ -4,5 +4,5 @@ export * from "./config.js";
|
|||
export * from "./url.js";
|
||||
export * from "./emit.js";
|
||||
export type IServiceGetter = {
|
||||
get: (name: string) => Promise<any>;
|
||||
get: <T>(name: string) => Promise<T>;
|
||||
};
|
||||
|
|
|
@ -217,4 +217,20 @@ export abstract class BaseService<T> {
|
|||
}
|
||||
throw new PermissionException('权限不足');
|
||||
}
|
||||
|
||||
async batchDelete(ids: number[], userId: number) {
|
||||
if(userId >0){
|
||||
const list = await this.getRepository().find({
|
||||
where: {
|
||||
// @ts-ignore
|
||||
id: In(ids),
|
||||
userId,
|
||||
},
|
||||
})
|
||||
// @ts-ignore
|
||||
ids = list.map(item => item.id)
|
||||
}
|
||||
|
||||
await this.delete(ids);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export type DnsProviderContext = {
|
|||
logger: ILogger;
|
||||
http: HttpClient;
|
||||
utils: typeof utils;
|
||||
domainParser: IDomainParser;
|
||||
};
|
||||
|
||||
export interface IDnsProvider<T = any> {
|
||||
|
@ -35,3 +36,11 @@ export interface IDnsProvider<T = any> {
|
|||
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||
setCtx(ctx: DnsProviderContext): void;
|
||||
}
|
||||
|
||||
export interface ISubDomainsGetter {
|
||||
getSubDomains(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export interface IDomainParser {
|
||||
parse(fullDomain: string): Promise<string>;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
import { dnsProviderRegistry } from "./registry.js";
|
||||
import { Decorator } from "@certd/pipeline";
|
||||
import { HttpClient, ILogger } from "@certd/basic";
|
||||
|
@ -16,6 +14,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
|||
this.http = ctx.http;
|
||||
}
|
||||
|
||||
async parseDomain(fullDomain: string) {
|
||||
return await this.ctx.domainParser.parse(fullDomain);
|
||||
}
|
||||
|
||||
abstract createRecord(options: CreateRecordOptions): Promise<T>;
|
||||
|
||||
abstract onInstance(): Promise<void>;
|
||||
|
@ -23,14 +25,6 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
|||
abstract removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||
}
|
||||
|
||||
export function parseDomain(fullDomain: string) {
|
||||
const parsed = psl.parse(fullDomain) as psl.ParsedDomain;
|
||||
if (parsed.error) {
|
||||
throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
|
||||
}
|
||||
return parsed.domain as string;
|
||||
}
|
||||
|
||||
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
|
||||
const { dnsProviderType, context } = opts;
|
||||
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { IDomainParser, ISubDomainsGetter } from "./api";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
|
||||
export class DomainParser implements IDomainParser {
|
||||
subDomainsGetter: ISubDomainsGetter;
|
||||
constructor(subDomainsGetter: ISubDomainsGetter) {
|
||||
this.subDomainsGetter = subDomainsGetter;
|
||||
}
|
||||
|
||||
parseDomain(fullDomain: string) {
|
||||
const parsed = psl.parse(fullDomain) as psl.ParsedDomain;
|
||||
if (parsed.error) {
|
||||
throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
|
||||
}
|
||||
return parsed.domain as string;
|
||||
}
|
||||
|
||||
async parse(fullDomain: string) {
|
||||
const subDomains = await this.subDomainsGetter.getSubDomains();
|
||||
if (subDomains && subDomains.length > 0) {
|
||||
for (const subDomain of subDomains) {
|
||||
if (fullDomain.endsWith(subDomain)) {
|
||||
//找到子域名托管
|
||||
return subDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.parseDomain(fullDomain);
|
||||
}
|
||||
}
|
|
@ -5,8 +5,9 @@ import * as _ from "lodash-es";
|
|||
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||
import { IContext } from "@certd/pipeline";
|
||||
import { ILogger, utils } from "@certd/basic";
|
||||
import { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
|
||||
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
|
||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
||||
|
||||
export type CnameVerifyPlan = {
|
||||
type?: string;
|
||||
domain: string;
|
||||
|
@ -61,6 +62,8 @@ type AcmeServiceOptions = {
|
|||
privateKeyType?: PrivateKeyType;
|
||||
signal?: AbortSignal;
|
||||
maxCheckRetryCount?: number;
|
||||
userId: number;
|
||||
domainParser: IDomainParser;
|
||||
};
|
||||
|
||||
export class AcmeService {
|
||||
|
@ -174,7 +177,7 @@ export class AcmeService {
|
|||
this.logger.info("Triggered challengeCreateFn()");
|
||||
|
||||
const fullDomain = authz.identifier.value;
|
||||
let domain = parseDomain(fullDomain);
|
||||
let domain = await this.options.domainParser.parse(fullDomain);
|
||||
this.logger.info("主域名为:" + domain);
|
||||
|
||||
const getChallenge = (type: string) => {
|
||||
|
@ -240,7 +243,7 @@ export class AcmeService {
|
|||
const cname = cnameVerifyPlan[fullDomain];
|
||||
if (cname) {
|
||||
dnsProvider = cname.dnsProvider;
|
||||
domain = parseDomain(cname.domain);
|
||||
domain = await this.options.domainParser.parse(cname.domain);
|
||||
fullRecord = cname.fullRecord;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -4,12 +4,13 @@ import { utils } from "@certd/basic";
|
|||
import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, HttpVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js";
|
||||
import { AcmeService } from "./acme.js";
|
||||
import * as _ from "lodash-es";
|
||||
import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js";
|
||||
import { createDnsProvider, DnsProviderContext, IDnsProvider, ISubDomainsGetter } from "../../dns-provider/index.js";
|
||||
import { CertReader } from "./cert-reader.js";
|
||||
import { CertApplyBasePlugin } from "./base.js";
|
||||
import { GoogleClient } from "../../libs/google.js";
|
||||
import { EabAccess } from "../../access";
|
||||
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
||||
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
||||
export * from "./base.js";
|
||||
export type { CertInfo };
|
||||
export * from "./cert-reader.js";
|
||||
|
@ -314,7 +315,10 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
|||
}
|
||||
}
|
||||
this.eab = eab;
|
||||
const subDomainsGetter = await this.ctx.serviceGetter.get<ISubDomainsGetter>("subDomainsGetter");
|
||||
const domainParser = new DomainParser(subDomainsGetter);
|
||||
this.acme = new AcmeService({
|
||||
userId: this.ctx.user.id,
|
||||
userContext: this.userContext,
|
||||
logger: this.logger,
|
||||
sslProvider: this.sslProvider,
|
||||
|
@ -325,8 +329,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
|||
privateKeyType: this.privateKeyType,
|
||||
signal: this.ctx.signal,
|
||||
maxCheckRetryCount: this.maxCheckRetryCount,
|
||||
// cnameProxyService: this.ctx.cnameProxyService,
|
||||
// dnsProviderCreator: this.createDnsProvider.bind(this),
|
||||
domainParser,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -387,7 +390,8 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
|||
}
|
||||
|
||||
async createDnsProvider(dnsProviderType: string, dnsProviderAccess: any): Promise<IDnsProvider> {
|
||||
const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils };
|
||||
const domainParser = this.acme.options.domainParser;
|
||||
const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils, domainParser };
|
||||
return await createDnsProvider({
|
||||
dnsProviderType,
|
||||
context,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/cname/record";
|
||||
const subDomainApiPrefix = "/pi/subDomain";
|
||||
|
||||
export type CnameRecord = {
|
||||
id?: number;
|
||||
|
@ -18,7 +19,7 @@ export type DomainGroupItem = {
|
|||
export async function GetList() {
|
||||
return await request({
|
||||
url: apiPrefix + "/list",
|
||||
method: "post"
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,8 +29,8 @@ export async function GetByDomain(domain: string) {
|
|||
method: "post",
|
||||
data: {
|
||||
domain,
|
||||
createOnNotFound: true
|
||||
}
|
||||
createOnNotFound: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,17 @@ export async function DoVerify(id: number) {
|
|||
url: apiPrefix + "/verify",
|
||||
method: "post",
|
||||
data: {
|
||||
id
|
||||
}
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function ParseDomain(fullDomain: string) {
|
||||
return await request({
|
||||
url: subDomainApiPrefix + "/parseDomain",
|
||||
method: "post",
|
||||
data: {
|
||||
fullDomain,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,26 +32,14 @@
|
|||
<div class="form-item">
|
||||
<span class="label">DNS类型:</span>
|
||||
<span class="input">
|
||||
<fs-dict-select
|
||||
v-model:value="item.dnsProviderType"
|
||||
size="small"
|
||||
:dict="dnsProviderTypeDict"
|
||||
placeholder="DNS提供商"
|
||||
@change="onPlanChanged"
|
||||
></fs-dict-select>
|
||||
<fs-dict-select v-model:value="item.dnsProviderType" size="small" :dict="dnsProviderTypeDict" placeholder="DNS提供商" @change="onPlanChanged"></fs-dict-select>
|
||||
</span>
|
||||
</div>
|
||||
<a-divider type="vertical" />
|
||||
<div class="form-item">
|
||||
<span class="label">DNS授权:</span>
|
||||
<span class="input">
|
||||
<access-selector
|
||||
v-model="item.dnsProviderAccessId"
|
||||
size="small"
|
||||
:type="item.dnsProviderType"
|
||||
placeholder="请选择"
|
||||
@change="onPlanChanged"
|
||||
></access-selector>
|
||||
<access-selector v-model="item.dnsProviderAccessId" size="small" :type="item.dnsProviderType" placeholder="请选择" @change="onPlanChanged"></access-selector>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,28 +68,27 @@ import { dict, FsDictSelect } from "@fast-crud/fast-crud";
|
|||
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||
import CnameVerifyPlan from "./cname-verify-plan.vue";
|
||||
import HttpVerifyPlan from "./http-verify-plan.vue";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
import { Form } from "ant-design-vue";
|
||||
import { DomainsVerifyPlanInput } from "./type";
|
||||
import { CnameRecord, DomainGroupItem } from "./api";
|
||||
import { DomainGroupItem, ParseDomain } from "./api";
|
||||
|
||||
defineOptions({
|
||||
name: "DomainsVerifyPlanEditor"
|
||||
name: "DomainsVerifyPlanEditor",
|
||||
});
|
||||
|
||||
const challengeTypeOptions = ref<any[]>([
|
||||
{
|
||||
label: "DNS验证",
|
||||
value: "dns"
|
||||
value: "dns",
|
||||
},
|
||||
{
|
||||
label: "CNAME验证",
|
||||
value: "cname"
|
||||
value: "cname",
|
||||
},
|
||||
{
|
||||
label: "HTTP验证",
|
||||
value: "http"
|
||||
}
|
||||
value: "http",
|
||||
},
|
||||
]);
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -122,7 +109,7 @@ function fullscreenExit() {
|
|||
}
|
||||
const planRef = ref<DomainsVerifyPlanInput>(props.modelValue || {});
|
||||
const dnsProviderTypeDict = dict({
|
||||
url: "pi/dnsProvider/dnsProviderTypeDict"
|
||||
url: "pi/dnsProvider/dnsProviderTypeDict",
|
||||
});
|
||||
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
|
@ -139,7 +126,7 @@ function showError(error: string) {
|
|||
|
||||
type DomainGroup = Record<string, DomainGroupItem>;
|
||||
|
||||
function onDomainsChanged(domains: string[]) {
|
||||
async function onDomainsChanged(domains: string[]) {
|
||||
if (domains == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -147,12 +134,7 @@ function onDomainsChanged(domains: string[]) {
|
|||
const domainGroups: DomainGroup = {};
|
||||
for (let domain of domains) {
|
||||
const keyDomain = domain.replace("*.", "");
|
||||
const parsed = psl.parse(keyDomain);
|
||||
if (parsed.error) {
|
||||
showError(`域名${domain}解析失败: ${JSON.stringify(parsed.error)}`);
|
||||
continue;
|
||||
}
|
||||
const mainDomain = parsed.domain;
|
||||
const mainDomain = await ParseDomain(keyDomain);
|
||||
if (mainDomain == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -161,7 +143,7 @@ function onDomainsChanged(domains: string[]) {
|
|||
group = {
|
||||
domain: mainDomain,
|
||||
domains: [],
|
||||
keySubDomains: []
|
||||
keySubDomains: [],
|
||||
} as DomainGroupItem;
|
||||
domainGroups[mainDomain] = group;
|
||||
}
|
||||
|
@ -180,7 +162,7 @@ function onDomainsChanged(domains: string[]) {
|
|||
//@ts-ignore
|
||||
cnameVerifyPlan: {},
|
||||
//@ts-ignore
|
||||
httpVerifyPlan: {}
|
||||
httpVerifyPlan: {},
|
||||
};
|
||||
planRef.value[domain] = planItem;
|
||||
}
|
||||
|
@ -196,7 +178,7 @@ function onDomainsChanged(domains: string[]) {
|
|||
if (!cnameOrigin[subDomain]) {
|
||||
//@ts-ignore
|
||||
planItem.cnameVerifyPlan[subDomain] = {
|
||||
id: 0
|
||||
id: 0,
|
||||
};
|
||||
} else {
|
||||
planItem.cnameVerifyPlan[subDomain] = cnameOrigin[subDomain];
|
||||
|
@ -205,14 +187,14 @@ function onDomainsChanged(domains: string[]) {
|
|||
if (!cnamePlan[subDomain]) {
|
||||
//@ts-ignore
|
||||
cnamePlan[subDomain] = {
|
||||
id: 0
|
||||
id: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (!httpOrigin[subDomain]) {
|
||||
//@ts-ignore
|
||||
planItem.httpVerifyPlan[subDomain] = {
|
||||
domain: subDomain
|
||||
domain: subDomain,
|
||||
};
|
||||
} else {
|
||||
planItem.httpVerifyPlan[subDomain] = httpOrigin[subDomain];
|
||||
|
@ -221,7 +203,7 @@ function onDomainsChanged(domains: string[]) {
|
|||
if (!httpPlan[subDomain]) {
|
||||
//@ts-ignore
|
||||
httpPlan[subDomain] = {
|
||||
domain: subDomain
|
||||
domain: subDomain,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +237,7 @@ watch(
|
|||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -98,6 +98,17 @@ export const certdResources = [
|
|||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "子域名托管设置",
|
||||
name: "SubDomain",
|
||||
path: "/certd/pipeline/subDomain",
|
||||
component: "/certd/pipeline/sub-domain/index.vue",
|
||||
meta: {
|
||||
icon: "ion:link-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "流水线分组管理",
|
||||
name: "PipelineGroupManager",
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// @ts-ignore
|
||||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/pi/subDomain";
|
||||
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
export async function AddObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export async function UpdateObj(obj: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
export async function DelObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetObj(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetDetail(id: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/detail",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
|
||||
export async function DeleteBatch(ids: any[]) {
|
||||
return await request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids },
|
||||
});
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
import * as api from "./api";
|
||||
import { Ref, ref } from "vue";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
form.id = row.id;
|
||||
const res = await api.UpdateObj(form);
|
||||
return res;
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
const res = await api.AddObj(form);
|
||||
return res;
|
||||
};
|
||||
|
||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||
context.selectedRowKeys = selectedRowKeys;
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
plugins: {
|
||||
//这里使用行选择插件,生成行选择crudOptions配置,最终会与crudOptions合并
|
||||
rowSelection: {
|
||||
enabled: true,
|
||||
order: -2,
|
||||
before: true,
|
||||
// handle: (pluginProps,useCrudProps)=>CrudOptions,
|
||||
props: {
|
||||
multiple: true,
|
||||
crossPage: true,
|
||||
selectedRowKeys,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
// tabs: {
|
||||
// name: "status",
|
||||
// show: true,
|
||||
// },
|
||||
rowHandle: {
|
||||
minWidth: 200,
|
||||
fixed: "right",
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 80,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
domain: {
|
||||
title: "子域名",
|
||||
type: "text",
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
editForm: {
|
||||
component: {
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
title: "是否禁用",
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: false, label: "启用", color: "green" },
|
||||
{ value: true, label: "禁用", color: "gray" },
|
||||
],
|
||||
}),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
form: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
createTime: {
|
||||
title: "创建时间",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 160,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
updateTime: {
|
||||
title: "更新时间",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<fs-page class="page-cert">
|
||||
<template #header>
|
||||
<div class="title">
|
||||
子域名托管
|
||||
<span class="sub"> 当你的域名设置了子域名托管,需要在此处创建记录,否则申请证书将失败 </span>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #pagination-left>
|
||||
<a-tooltip title="批量删除">
|
||||
<fs-button icon="DeleteOutlined" @click="handleBatchDelete"></fs-button>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { DeleteBatch } from "./api";
|
||||
|
||||
defineOptions({
|
||||
name: "CnameRecord",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
||||
|
||||
const selectedRowKeys = context.selectedRowKeys;
|
||||
const handleBatchDelete = () => {
|
||||
if (selectedRowKeys.value?.length > 0) {
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要批量删除这${selectedRowKeys.value.length}条记录吗`,
|
||||
async onOk() {
|
||||
await DeleteBatch(selectedRowKeys.value);
|
||||
message.info("删除成功");
|
||||
crudExpose.doRefresh();
|
||||
selectedRowKeys.value = [];
|
||||
},
|
||||
});
|
||||
} else {
|
||||
message.error("请先勾选记录");
|
||||
}
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
<style lang="less"></style>
|
|
@ -1,3 +1,19 @@
|
|||
ALTER TABLE pi_plugin ADD COLUMN "pluginType" varchar(100);
|
||||
ALTER TABLE pi_plugin ADD COLUMN "metadata" varchar(40960);
|
||||
ALTER TABLE pi_plugin ADD COLUMN "author" varchar(100);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE "pi_sub_domain"
|
||||
(
|
||||
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
"user_id" integer,
|
||||
"domain" varchar(100),
|
||||
"disabled" boolean NOT NULL DEFAULT (false),
|
||||
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX "index_sub_domain_user_id" ON "pi_sub_domain" ("user_id");
|
||||
CREATE INDEX "index_sub_domain_domain" ON "pi_sub_domain" ("domain");
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import {ALL, Body, Controller, Inject, Post, Provide} from '@midwayjs/core';
|
||||
import {AccessGetter, AccessService, BaseController, Constants} from '@certd/lib-server';
|
||||
import {AccessService, BaseController, Constants} from '@certd/lib-server';
|
||||
import {
|
||||
AccessRequestHandleReq,
|
||||
IAccessService,
|
||||
ITaskPlugin,
|
||||
newAccess,
|
||||
newNotification,
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
import {EmailService} from '../../../modules/basic/service/email-service.js';
|
||||
import {http, HttpRequestConfig, logger, mergeUtils, utils} from '@certd/basic';
|
||||
import {NotificationService} from '../../../modules/pipeline/service/notification-service.js';
|
||||
import {TaskServiceBuilder} from "../../../modules/pipeline/service/task-service-getter.js";
|
||||
|
||||
@Provide()
|
||||
@Controller('/api/pi/handle')
|
||||
|
@ -23,6 +25,8 @@ export class HandleController extends BaseController {
|
|||
@Inject()
|
||||
emailService: EmailService;
|
||||
|
||||
@Inject()
|
||||
taskServiceBuilder: TaskServiceBuilder;
|
||||
|
||||
@Inject()
|
||||
notificationService: NotificationService;
|
||||
|
@ -82,8 +86,6 @@ export class HandleController extends BaseController {
|
|||
//@ts-ignore
|
||||
const instance = plugin as ITaskPlugin;
|
||||
|
||||
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
|
||||
|
||||
const download = async (config: HttpRequestConfig, savePath: string) => {
|
||||
await utils.download({
|
||||
http,
|
||||
|
@ -93,13 +95,9 @@ export class HandleController extends BaseController {
|
|||
});
|
||||
};
|
||||
|
||||
const serviceContainer:any = {
|
||||
}
|
||||
const serviceGetter = {
|
||||
get:(name: string) => {
|
||||
return serviceContainer[name]
|
||||
}
|
||||
}
|
||||
const taskServiceGetter = this.taskServiceBuilder.create({userId})
|
||||
|
||||
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService")
|
||||
//@ts-ignore
|
||||
const taskCtx: TaskInstanceContext = {
|
||||
pipeline: undefined,
|
||||
|
@ -125,7 +123,7 @@ export class HandleController extends BaseController {
|
|||
// }),
|
||||
// signal: this.abort.signal,
|
||||
utils,
|
||||
serviceGetter
|
||||
serviceGetter:taskServiceGetter
|
||||
};
|
||||
instance.setCtx(taskCtx);
|
||||
mergeUtils.merge(plugin, body.input);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import {ALL, Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core';
|
||||
import {Constants, CrudController} from '@certd/lib-server';
|
||||
import {SubDomainService, SubDomainsGetter} from "../../../modules/pipeline/service/sub-domain-service.js";
|
||||
import {DomainParser} from '@certd/plugin-cert/dist/dns-provider/domain-parser.js';
|
||||
|
||||
/**
|
||||
* 子域名托管
|
||||
*/
|
||||
@Provide()
|
||||
@Controller('/api/pi/subDomain')
|
||||
export class SubDomainController extends CrudController<SubDomainService> {
|
||||
@Inject()
|
||||
service: SubDomainService;
|
||||
|
||||
getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
@Post('/parseDomain', { summary: Constants.per.authOnly })
|
||||
async parseDomain(@Body("fullDomain") fullDomain:string) {
|
||||
const userId = this.getUserId()
|
||||
const subDomainGetter = new SubDomainsGetter(userId, this.service)
|
||||
const domainParser = new DomainParser(subDomainGetter)
|
||||
const domain = await domainParser.parse(fullDomain)
|
||||
return this.ok(domain);
|
||||
}
|
||||
|
||||
|
||||
@Post('/page', { summary: Constants.per.authOnly })
|
||||
async page(@Body(ALL) body) {
|
||||
body.query = body.query ?? {};
|
||||
delete body.query.userId;
|
||||
const buildQuery = qb => {
|
||||
qb.andWhere('user_id = :userId', { userId: this.getUserId() });
|
||||
};
|
||||
const res = await this.service.page({
|
||||
query: body.query,
|
||||
page: body.page,
|
||||
sort: body.sort,
|
||||
buildQuery,
|
||||
});
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post('/list', { summary: Constants.per.authOnly })
|
||||
async list(@Body(ALL) body) {
|
||||
body.query = body.query ?? {};
|
||||
body.query.userId = this.getUserId();
|
||||
return super.list(body);
|
||||
}
|
||||
|
||||
@Post('/add', { summary: Constants.per.authOnly })
|
||||
async add(@Body(ALL) bean) {
|
||||
bean.userId = this.getUserId();
|
||||
return super.add(bean);
|
||||
}
|
||||
|
||||
@Post('/update', { summary: Constants.per.authOnly })
|
||||
async update(@Body(ALL) bean) {
|
||||
await this.service.checkUserId(bean.id, this.getUserId());
|
||||
delete bean.userId;
|
||||
return super.update(bean);
|
||||
}
|
||||
@Post('/info', { summary: Constants.per.authOnly })
|
||||
async info(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
return super.info(id);
|
||||
}
|
||||
|
||||
@Post('/delete', { summary: Constants.per.authOnly })
|
||||
async delete(@Query('id') id: number) {
|
||||
await this.service.checkUserId(id, this.getUserId());
|
||||
return super.delete(id);
|
||||
}
|
||||
|
||||
@Post('/batchDelete', { summary: Constants.per.authOnly })
|
||||
async batchDelete(@Body('ids') ids: number[]) {
|
||||
await this.service.batchDelete(ids, this.getUserId());
|
||||
return this.ok({});
|
||||
}
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseService, PlusService, ValidateException } from '@certd/lib-server';
|
||||
import { CnameRecordEntity, CnameRecordStatusType } from '../entity/cname-record.js';
|
||||
import { createDnsProvider, IDnsProvider, parseDomain } from '@certd/plugin-cert';
|
||||
import { CnameProvider, CnameRecord } from '@certd/pipeline';
|
||||
import { cache, http, logger, utils } from '@certd/basic';
|
||||
|
||||
import { AccessService } from '@certd/lib-server';
|
||||
import { isDev } from '@certd/basic';
|
||||
import { walkTxtRecord } from '@certd/acme-client';
|
||||
import { CnameProviderService } from './cname-provider-service.js';
|
||||
import { CnameProviderEntity } from '../entity/cname-provider.js';
|
||||
import { CommonDnsProvider } from './common-provider.js';
|
||||
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
|
||||
import {InjectEntityModel} from '@midwayjs/typeorm';
|
||||
import {Repository} from 'typeorm';
|
||||
import {AccessService, BaseService, PlusService, ValidateException} from '@certd/lib-server';
|
||||
import {CnameRecordEntity, CnameRecordStatusType} from '../entity/cname-record.js';
|
||||
import {createDnsProvider, IDnsProvider} from '@certd/plugin-cert';
|
||||
import {CnameProvider, CnameRecord} from '@certd/pipeline';
|
||||
import {cache, http, isDev, logger, utils} from '@certd/basic';
|
||||
import {walkTxtRecord} from '@certd/acme-client';
|
||||
import {CnameProviderService} from './cname-provider-service.js';
|
||||
import {CnameProviderEntity} from '../entity/cname-provider.js';
|
||||
import {CommonDnsProvider} from './common-provider.js';
|
||||
import {SubDomainService, SubDomainsGetter} from "../../pipeline/service/sub-domain-service.js";
|
||||
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
|
||||
|
||||
type CnameCheckCacheValue = {
|
||||
validating: boolean;
|
||||
|
@ -40,6 +39,9 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
@Inject()
|
||||
plusService: PlusService;
|
||||
|
||||
@Inject()
|
||||
subDomainService: SubDomainService;
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
|
@ -74,17 +76,20 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
} else {
|
||||
cnameProvider = await this.cnameProviderService.info(param.cnameProviderId);
|
||||
}
|
||||
this.cnameProviderChanged(param, cnameProvider);
|
||||
await this.cnameProviderChanged(param.userId,param, cnameProvider);
|
||||
|
||||
param.status = 'cname';
|
||||
const { id } = await super.add(param);
|
||||
return await this.info(id);
|
||||
}
|
||||
|
||||
private cnameProviderChanged(param: any, cnameProvider: CnameProviderEntity) {
|
||||
private async cnameProviderChanged(userId:number,param: any, cnameProvider: CnameProviderEntity) {
|
||||
param.cnameProviderId = cnameProvider.id;
|
||||
|
||||
const realDomain = parseDomain(param.domain);
|
||||
const subDomainGetter = new SubDomainsGetter(userId, this.subDomainService)
|
||||
const domainParser = new DomainParser(subDomainGetter);
|
||||
|
||||
const realDomain = await domainParser.parse(param.domain);
|
||||
const prefix = param.domain.replace(realDomain, '');
|
||||
let hostRecord = `_acme-challenge.${prefix}`;
|
||||
if (hostRecord.endsWith('.')) {
|
||||
|
@ -111,7 +116,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
}
|
||||
if (old.cnameProviderId !== param.cnameProviderId) {
|
||||
const cnameProvider = await this.cnameProviderService.info(param.cnameProviderId);
|
||||
this.cnameProviderChanged(param, cnameProvider);
|
||||
await this.cnameProviderChanged(old.userId,param, cnameProvider);
|
||||
param.status = 'cname';
|
||||
}
|
||||
return await super.update(param);
|
||||
|
@ -185,6 +190,9 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
return true;
|
||||
}
|
||||
|
||||
const subDomainGetter = new SubDomainsGetter(bean.userId, this.subDomainService)
|
||||
const domainParser = new DomainParser(subDomainGetter);
|
||||
|
||||
const cacheKey = `cname.record.verify.${bean.id}`;
|
||||
|
||||
let value: CnameCheckCacheValue = cache.get(cacheKey);
|
||||
|
@ -219,7 +227,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
}
|
||||
|
||||
const access = await this.accessService.getById(cnameProvider.accessId, cnameProvider.userId);
|
||||
const context = { access, logger, http, utils };
|
||||
const context = { access, logger, http, utils,domainParser };
|
||||
const dnsProvider: IDnsProvider = await createDnsProvider({
|
||||
dnsProviderType: cnameProvider.dnsProviderType,
|
||||
context,
|
||||
|
@ -239,7 +247,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
return false;
|
||||
}
|
||||
|
||||
const originDomain = parseDomain(bean.domain);
|
||||
|
||||
const originDomain = await domainParser.parse(bean.domain);
|
||||
const fullDomain = `${bean.hostRecord}.${originDomain}`;
|
||||
|
||||
logger.info(`检查CNAME配置 ${fullDomain} ${testRecordValue}`);
|
||||
|
@ -285,7 +294,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
|
|||
ttl: ttl,
|
||||
});
|
||||
|
||||
const domain = parseDomain(bean.recordValue);
|
||||
const domain = await domainParser.parse(bean.recordValue);
|
||||
const fullRecord = bean.recordValue;
|
||||
const hostRecord = fullRecord.replace(`.${domain}`, '');
|
||||
const req = {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* 子域名托管
|
||||
*/
|
||||
@Entity('pi_sub_domain')
|
||||
export class SubDomainEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'user_id', comment: 'UserId' })
|
||||
userId: number;
|
||||
|
||||
@Column({ name: 'domain', comment: '子域名' })
|
||||
domain: string;
|
||||
|
||||
@Column({ name: 'disabled', comment: '禁用' })
|
||||
disabled: boolean;
|
||||
|
||||
@Column({
|
||||
name: 'create_time',
|
||||
comment: '创建时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
createTime: Date;
|
||||
@Column({
|
||||
name: 'update_time',
|
||||
comment: '修改时间',
|
||||
default: () => 'CURRENT_TIMESTAMP',
|
||||
})
|
||||
updateTime: Date;
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
|
||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { In, MoreThan, Repository } from "typeorm";
|
||||
import {Config, Inject, Provide, Scope, ScopeEnum, sleep} from "@midwayjs/core";
|
||||
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||
import {In, MoreThan, Repository} from "typeorm";
|
||||
import {
|
||||
AccessGetter,
|
||||
AccessService,
|
||||
BaseService,
|
||||
NeedSuiteException,
|
||||
|
@ -12,30 +11,40 @@ import {
|
|||
SysSettingsService,
|
||||
SysSiteInfo
|
||||
} from "@certd/lib-server";
|
||||
import { PipelineEntity } from "../entity/pipeline.js";
|
||||
import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
|
||||
import { Executor, Pipeline, ResultType, RunHistory, RunnableCollection, SysInfo, UserInfo } from "@certd/pipeline";
|
||||
import { DbStorage } from "./db-storage.js";
|
||||
import { StorageService } from "./storage-service.js";
|
||||
import { Cron } from "../../cron/cron.js";
|
||||
import { HistoryService } from "./history-service.js";
|
||||
import { HistoryEntity } from "../entity/history.js";
|
||||
import { HistoryLogEntity } from "../entity/history-log.js";
|
||||
import { HistoryLogService } from "./history-log-service.js";
|
||||
import { EmailService } from "../../basic/service/email-service.js";
|
||||
import { UserService } from "../../sys/authority/service/user-service.js";
|
||||
import { CnameRecordService } from "../../cname/service/cname-record-service.js";
|
||||
import { CnameProxyService } from "./cname-proxy-service.js";
|
||||
import { PluginConfigGetter } from "../../plugin/service/plugin-config-getter.js";
|
||||
import {PipelineEntity} from "../entity/pipeline.js";
|
||||
import {PipelineDetail} from "../entity/vo/pipeline-detail.js";
|
||||
import {
|
||||
Executor,
|
||||
IAccessService,
|
||||
ICnameProxyService,
|
||||
INotificationService,
|
||||
Pipeline,
|
||||
ResultType,
|
||||
RunHistory,
|
||||
RunnableCollection,
|
||||
SysInfo,
|
||||
UserInfo
|
||||
} from "@certd/pipeline";
|
||||
import {DbStorage} from "./db-storage.js";
|
||||
import {StorageService} from "./storage-service.js";
|
||||
import {Cron} from "../../cron/cron.js";
|
||||
import {HistoryService} from "./history-service.js";
|
||||
import {HistoryEntity} from "../entity/history.js";
|
||||
import {HistoryLogEntity} from "../entity/history-log.js";
|
||||
import {HistoryLogService} from "./history-log-service.js";
|
||||
import {EmailService} from "../../basic/service/email-service.js";
|
||||
import {UserService} from "../../sys/authority/service/user-service.js";
|
||||
import {CnameRecordService} from "../../cname/service/cname-record-service.js";
|
||||
import {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js";
|
||||
import dayjs from "dayjs";
|
||||
import { DbAdapter } from "../../db/index.js";
|
||||
import { isComm } from "@certd/plus-core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { UrlService } from "./url-service.js";
|
||||
import { NotificationService } from "./notification-service.js";
|
||||
import { NotificationGetter } from "./notification-getter.js";
|
||||
import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
|
||||
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
|
||||
import {DbAdapter} from "../../db/index.js";
|
||||
import {isComm} from "@certd/plus-core";
|
||||
import {logger} from "@certd/basic";
|
||||
import {UrlService} from "./url-service.js";
|
||||
import {NotificationService} from "./notification-service.js";
|
||||
import {UserSuiteEntity, UserSuiteService} from "@certd/commercial-core";
|
||||
import {CertInfoService} from "../../monitor/service/cert-info-service.js";
|
||||
import {TaskServiceBuilder} from "./task-service-getter.js";
|
||||
|
||||
const runningTasks: Map<string | number, Executor> = new Map();
|
||||
|
||||
|
@ -65,6 +74,9 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
@Inject()
|
||||
pluginConfigGetter: PluginConfigGetter;
|
||||
|
||||
@Inject()
|
||||
taskServiceBuilder: TaskServiceBuilder;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
|
@ -473,20 +485,19 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
role: userIsAdmin ? 'admin' : 'user',
|
||||
};
|
||||
|
||||
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
|
||||
const cnameProxyService = new CnameProxyService(userId, this.cnameRecordService.getWithAccessByDomain.bind(this.cnameRecordService));
|
||||
const notificationGetter = new NotificationGetter(userId, this.notificationService);
|
||||
|
||||
const sysInfo: SysInfo = {};
|
||||
if (isComm()) {
|
||||
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
|
||||
sysInfo.title = siteInfo.title;
|
||||
}
|
||||
const serviceContainer = {}
|
||||
const serviceGetter = {
|
||||
get:(name: string) => {
|
||||
return serviceContainer[name]
|
||||
}
|
||||
}
|
||||
|
||||
const taskServiceGetter = this.taskServiceBuilder.create({
|
||||
userId,
|
||||
})
|
||||
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService")
|
||||
const notificationGetter =await taskServiceGetter.get<INotificationService>("notificationService")
|
||||
const cnameProxyService =await taskServiceGetter.get<ICnameProxyService>("cnameProxyService")
|
||||
const executor = new Executor({
|
||||
user,
|
||||
pipeline,
|
||||
|
@ -500,7 +511,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||
notificationService: notificationGetter,
|
||||
fileRootDir: this.certdConfig.fileRootDir,
|
||||
sysInfo,
|
||||
serviceGetter
|
||||
serviceGetter:taskServiceGetter
|
||||
});
|
||||
try {
|
||||
runningTasks.set(historyId, executor);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
|
||||
import {BaseService, SysSettingsService} from '@certd/lib-server';
|
||||
import {InjectEntityModel} from '@midwayjs/typeorm';
|
||||
import {Repository} from 'typeorm';
|
||||
import {SubDomainEntity} from '../entity/sub-domain.js';
|
||||
import {EmailService} from '../../basic/service/email-service.js';
|
||||
import {ISubDomainsGetter} from "@certd/plugin-cert";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class SubDomainService extends BaseService<SubDomainEntity> {
|
||||
@InjectEntityModel(SubDomainEntity)
|
||||
repository: Repository<SubDomainEntity>;
|
||||
|
||||
@Inject()
|
||||
emailService: EmailService;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
async getListByUserId(userId:number):Promise<string[]>{
|
||||
if (!userId) {
|
||||
return [];
|
||||
}
|
||||
const list = await this.find({
|
||||
where: {
|
||||
userId,
|
||||
disabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
return list.map(item=>item.domain);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export class SubDomainsGetter implements ISubDomainsGetter {
|
||||
userId: number;
|
||||
subDomainService: SubDomainService;
|
||||
|
||||
constructor(userId: number, subDomainService: SubDomainService) {
|
||||
this.userId = userId;
|
||||
this.subDomainService = subDomainService;
|
||||
}
|
||||
|
||||
async getSubDomains() {
|
||||
return await this.subDomainService.getListByUserId(this.userId)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import {IServiceGetter} from "@certd/pipeline";
|
||||
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||
import {SubDomainService, SubDomainsGetter} from "./sub-domain-service.js";
|
||||
import {AccessGetter, AccessService} from "@certd/lib-server";
|
||||
import {CnameProxyService} from "./cname-proxy-service.js";
|
||||
import {NotificationGetter} from "./notification-getter.js";
|
||||
import {NotificationService} from "./notification-service.js";
|
||||
import {CnameRecordService} from "../../cname/service/cname-record-service.js";
|
||||
|
||||
export class TaskServiceGetter implements IServiceGetter{
|
||||
serviceContainer:Record<string, any>;
|
||||
constructor(serviceContainer:Record<string, any>) {
|
||||
this.serviceContainer = serviceContainer;
|
||||
}
|
||||
async get<T>(serviceName: string): Promise<T> {
|
||||
const ret = this.serviceContainer[serviceName] as T;
|
||||
if(!ret){
|
||||
throw new Error(`service ${serviceName} not found`)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
export type TaskServiceCreateReq = {
|
||||
userId: number;
|
||||
}
|
||||
|
||||
export type TaskServiceContainer = {
|
||||
subDomainsGetter:SubDomainsGetter;
|
||||
accessService: AccessGetter;
|
||||
cnameProxyService: CnameProxyService;
|
||||
notificationService: NotificationGetter;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class TaskServiceBuilder {
|
||||
|
||||
@Inject()
|
||||
subDomainService: SubDomainService;
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
@Inject()
|
||||
cnameRecordService: CnameRecordService;
|
||||
@Inject()
|
||||
notificationService: NotificationService;
|
||||
|
||||
|
||||
create(req:TaskServiceCreateReq){
|
||||
|
||||
const userId = req.userId;
|
||||
const accessGetter = new AccessGetter(userId, this.accessService.getById.bind(this.accessService));
|
||||
const cnameProxyService = new CnameProxyService(userId, this.cnameRecordService.getWithAccessByDomain.bind(this.cnameRecordService));
|
||||
const notificationGetter = new NotificationGetter(userId, this.notificationService);
|
||||
|
||||
const serviceContainer:TaskServiceContainer = {
|
||||
subDomainsGetter:new SubDomainsGetter(req.userId, this.subDomainService),
|
||||
accessService: accessGetter,
|
||||
cnameProxyService:cnameProxyService,
|
||||
notificationService:notificationGetter
|
||||
}
|
||||
return new TaskServiceGetter(serviceContainer)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue