mirror of https://github.com/usual2970/certimate
				
				
				
			feat: reserve accesses for ca or notification
							parent
							
								
									193a19b79c
								
							
						
					
					
						commit
						e533f9407f
					
				| 
						 | 
				
			
			@ -1098,7 +1098,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
 | 
			
		|||
 | 
			
		||||
			deployer, err := pWebhook.NewDeployer(&pWebhook.DeployerConfig{
 | 
			
		||||
				WebhookUrl:               access.Url,
 | 
			
		||||
				WebhookData:              maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.TemplateDataForDeployment),
 | 
			
		||||
				WebhookData:              maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForDeployment),
 | 
			
		||||
				Method:                   access.Method,
 | 
			
		||||
				Headers:                  mergedHeaders,
 | 
			
		||||
				AllowInsecureConnections: access.AllowInsecureConnections,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ type Access struct {
 | 
			
		|||
	Name      string         `json:"name" db:"name"`
 | 
			
		||||
	Provider  string         `json:"provider" db:"provider"`
 | 
			
		||||
	Config    map[string]any `json:"config" db:"config"`
 | 
			
		||||
	Reserve   string         `json:"reserve,omitempty" db:"reserve"`
 | 
			
		||||
	DeletedAt *time.Time     `json:"deleted" db:"deleted"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -261,12 +262,12 @@ type AccessConfigForWangsu struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type AccessConfigForWebhook struct {
 | 
			
		||||
	Url                         string `json:"url"`
 | 
			
		||||
	Method                      string `json:"method,omitempty"`
 | 
			
		||||
	HeadersString               string `json:"headers,omitempty"`
 | 
			
		||||
	AllowInsecureConnections    bool   `json:"allowInsecureConnections,omitempty"`
 | 
			
		||||
	TemplateDataForDeployment   string `json:"templateDataForDeployment,omitempty"`   // TODO:
 | 
			
		||||
	TemplateDataForNotification string `json:"templateDataForNotification,omitempty"` // TODO:
 | 
			
		||||
	Url                        string `json:"url"`
 | 
			
		||||
	Method                     string `json:"method,omitempty"`
 | 
			
		||||
	HeadersString              string `json:"headers,omitempty"`
 | 
			
		||||
	AllowInsecureConnections   bool   `json:"allowInsecureConnections,omitempty"`
 | 
			
		||||
	DefaultDataForDeployment   string `json:"defaultDataForDeployment,omitempty"`
 | 
			
		||||
	DefaultDataForNotification string `json:"defaultDataForNotification,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccessConfigForWestcn struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ func createNotifierProvider(options *notifierProviderOptions) (notifier.Notifier
 | 
			
		|||
 | 
			
		||||
			return pWebhook.NewNotifier(&pWebhook.NotifierConfig{
 | 
			
		||||
				WebhookUrl:               access.Url,
 | 
			
		||||
				WebhookData:              maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.TemplateDataForNotification),
 | 
			
		||||
				WebhookData:              maputil.GetOrDefaultString(options.ProviderExtendedConfig, "webhookData", access.DefaultDataForNotification),
 | 
			
		||||
				Method:                   access.Method,
 | 
			
		||||
				Headers:                  mergedHeaders,
 | 
			
		||||
				AllowInsecureConnections: access.AllowInsecureConnections,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to parse webhook url: %w", err)
 | 
			
		||||
	} else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook url scheme: %s", webhookUrl.Scheme)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme)
 | 
			
		||||
	} else {
 | 
			
		||||
		webhookUrl.Path = strings.ReplaceAll(webhookUrl.Path, "${DOMAIN}", url.PathEscape(certX509.Subject.CommonName))
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +94,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
 | 
			
		|||
		webhookMethod != http.MethodPut &&
 | 
			
		||||
		webhookMethod != http.MethodPatch &&
 | 
			
		||||
		webhookMethod != http.MethodDelete {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook request method: %s", webhookMethod)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 处理 Webhook 请求标头
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +114,7 @@ func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPE
 | 
			
		|||
	} else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) &&
 | 
			
		||||
		strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) &&
 | 
			
		||||
		strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook content type: %s", webhookContentType)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 处理 Webhook 请求数据
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to parse webhook url: %w", err)
 | 
			
		||||
	} else if webhookUrl.Scheme != "http" && webhookUrl.Scheme != "https" {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook url scheme: %s", webhookUrl.Scheme)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook url scheme '%s'", webhookUrl.Scheme)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 处理 Webhook 请求谓词
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
 | 
			
		|||
		webhookMethod != http.MethodPut &&
 | 
			
		||||
		webhookMethod != http.MethodPatch &&
 | 
			
		||||
		webhookMethod != http.MethodDelete {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook request method: %s", webhookMethod)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook request method '%s'", webhookMethod)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 处理 Webhook 请求标头
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ func (n *NotifierProvider) Notify(ctx context.Context, subject string, message s
 | 
			
		|||
	} else if strings.HasPrefix(webhookContentType, CONTENT_TYPE_JSON) &&
 | 
			
		||||
		strings.HasPrefix(webhookContentType, CONTENT_TYPE_FORM) &&
 | 
			
		||||
		strings.HasPrefix(webhookContentType, CONTENT_TYPE_MULTIPART) {
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook content type: %s", webhookContentType)
 | 
			
		||||
		return nil, fmt.Errorf("unsupported webhook content type '%s'", webhookContentType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 处理 Webhook 请求数据
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,7 @@ func (r *AccessRepository) castRecordToModel(record *core.Record) (*domain.Acces
 | 
			
		|||
		Name:     record.GetString("name"),
 | 
			
		||||
		Provider: record.GetString("provider"),
 | 
			
		||||
		Config:   config,
 | 
			
		||||
		Reserve:  record.GetString("reserve"),
 | 
			
		||||
	}
 | 
			
		||||
	return access, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
package migrations
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/pocketbase/pocketbase/core"
 | 
			
		||||
	m "github.com/pocketbase/pocketbase/migrations"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	m.Register(func(app core.App) error {
 | 
			
		||||
		// update collection `access`
 | 
			
		||||
		{
 | 
			
		||||
			collection, err := app.FindCollectionByNameOrId("4yzbv8urny5ja1e")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := collection.Fields.AddMarshaledJSONAt(4, []byte(`{
 | 
			
		||||
				"autogeneratePattern": "",
 | 
			
		||||
				"hidden": false,
 | 
			
		||||
				"id": "text2859962647",
 | 
			
		||||
				"max": 0,
 | 
			
		||||
				"min": 0,
 | 
			
		||||
				"name": "reserve",
 | 
			
		||||
				"pattern": "",
 | 
			
		||||
				"presentable": false,
 | 
			
		||||
				"primaryKey": false,
 | 
			
		||||
				"required": false,
 | 
			
		||||
				"system": false,
 | 
			
		||||
				"type": "text"
 | 
			
		||||
			}`)); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := app.Save(collection); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// migrate data
 | 
			
		||||
		{
 | 
			
		||||
			accesses, err := app.FindAllRecords("access")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for _, access := range accesses {
 | 
			
		||||
				changed := false
 | 
			
		||||
 | 
			
		||||
				if access.GetString("provider") == "buypass" {
 | 
			
		||||
					access.Set("reserve", "ca")
 | 
			
		||||
					changed = true
 | 
			
		||||
				} else if access.GetString("provider") == "googletrustservices" {
 | 
			
		||||
					access.Set("reserve", "ca")
 | 
			
		||||
					changed = true
 | 
			
		||||
				} else if access.GetString("provider") == "sslcom" {
 | 
			
		||||
					access.Set("reserve", "ca")
 | 
			
		||||
					changed = true
 | 
			
		||||
				} else if access.GetString("provider") == "zerossl" {
 | 
			
		||||
					access.Set("reserve", "ca")
 | 
			
		||||
					changed = true
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if access.GetString("provider") == "webhook" {
 | 
			
		||||
					config := make(map[string]any)
 | 
			
		||||
					if err := access.UnmarshalJSONField("config", &config); err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					config["method"] = "POST"
 | 
			
		||||
					config["headers"] = "Content-Type: application/json"
 | 
			
		||||
					access.Set("config", config)
 | 
			
		||||
					changed = true
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if changed {
 | 
			
		||||
					err = app.Save(access)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}, func(app core.App) error {
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -14,14 +14,14 @@ export type AccessEditDrawerProps = {
 | 
			
		|||
  data?: AccessFormProps["initialValues"];
 | 
			
		||||
  loading?: boolean;
 | 
			
		||||
  open?: boolean;
 | 
			
		||||
  range?: AccessFormProps["range"];
 | 
			
		||||
  scene: AccessFormProps["scene"];
 | 
			
		||||
  trigger?: React.ReactNode;
 | 
			
		||||
  usage?: AccessFormProps["usage"];
 | 
			
		||||
  onOpenChange?: (open: boolean) => void;
 | 
			
		||||
  afterSubmit?: (record: AccessModel) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccessEditDrawer = ({ data, loading, trigger, scene, range, afterSubmit, ...props }: AccessEditDrawerProps) => {
 | 
			
		||||
const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditDrawerProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [notificationApi, NotificationContextHolder] = notification.useNotification();
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,7 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, range, afterSubmit, .
 | 
			
		|||
        width={720}
 | 
			
		||||
        onClose={() => setOpen(false)}
 | 
			
		||||
      >
 | 
			
		||||
        <AccessForm ref={formRef} initialValues={data} range={range} scene={scene === "add" ? "add" : "edit"} />
 | 
			
		||||
        <AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
 | 
			
		||||
      </Drawer>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,14 +14,14 @@ export type AccessEditModalProps = {
 | 
			
		|||
  data?: AccessFormProps["initialValues"];
 | 
			
		||||
  loading?: boolean;
 | 
			
		||||
  open?: boolean;
 | 
			
		||||
  range?: AccessFormProps["range"];
 | 
			
		||||
  usage?: AccessFormProps["usage"];
 | 
			
		||||
  scene: AccessFormProps["scene"];
 | 
			
		||||
  trigger?: React.ReactNode;
 | 
			
		||||
  onOpenChange?: (open: boolean) => void;
 | 
			
		||||
  afterSubmit?: (record: AccessModel) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccessEditModal = ({ data, loading, trigger, scene, range, afterSubmit, ...props }: AccessEditModalProps) => {
 | 
			
		||||
const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditModalProps) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const [notificationApi, NotificationContextHolder] = notification.useNotification();
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, range, afterSubmit, ..
 | 
			
		|||
        onCancel={handleCancelClick}
 | 
			
		||||
      >
 | 
			
		||||
        <div className="pb-2 pt-4">
 | 
			
		||||
          <AccessForm ref={formRef} initialValues={data} range={range} scene={scene === "add" ? "add" : "edit"} />
 | 
			
		||||
          <AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
 | 
			
		||||
        </div>
 | 
			
		||||
      </Modal>
 | 
			
		||||
    </>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,16 +61,16 @@ import AccessFormWestcnConfig from "./AccessFormWestcnConfig";
 | 
			
		|||
import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig";
 | 
			
		||||
 | 
			
		||||
type AccessFormFieldValues = Partial<MaybeModelRecord<AccessModel>>;
 | 
			
		||||
type AccessFormRanges = "both-dns-hosting" | "ca-only" | "notify-only";
 | 
			
		||||
type AccessFormScenes = "add" | "edit";
 | 
			
		||||
type AccessFormUsages = "both-dns-hosting" | "ca-only" | "notification-only";
 | 
			
		||||
 | 
			
		||||
export type AccessFormProps = {
 | 
			
		||||
  className?: string;
 | 
			
		||||
  style?: React.CSSProperties;
 | 
			
		||||
  disabled?: boolean;
 | 
			
		||||
  initialValues?: AccessFormFieldValues;
 | 
			
		||||
  range?: AccessFormRanges;
 | 
			
		||||
  scene: AccessFormScenes;
 | 
			
		||||
  usage?: AccessFormUsages;
 | 
			
		||||
  onValuesChange?: (values: AccessFormFieldValues) => void;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ export type AccessFormInstance = {
 | 
			
		|||
  validateFields: FormInstance<AccessFormFieldValues>["validateFields"];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, style, disabled, initialValues, range, scene, onValuesChange }, ref) => {
 | 
			
		||||
const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className, style, disabled, initialValues, usage, scene, onValuesChange }, ref) => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const formSchema = z.object({
 | 
			
		||||
| 
						 | 
				
			
			@ -91,13 +91,14 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
 | 
			
		|||
      .trim(),
 | 
			
		||||
    provider: z.nativeEnum(ACCESS_PROVIDERS, {
 | 
			
		||||
      message:
 | 
			
		||||
        range === "ca-only"
 | 
			
		||||
        usage === "ca-only"
 | 
			
		||||
          ? t("access.form.certificate_authority.placeholder")
 | 
			
		||||
          : range === "notify-only"
 | 
			
		||||
          : usage === "notification-only"
 | 
			
		||||
            ? t("access.form.notification_channel.placeholder")
 | 
			
		||||
            : t("access.form.provider.placeholder"),
 | 
			
		||||
    }),
 | 
			
		||||
    config: z.any(),
 | 
			
		||||
    reserve: z.string().nullish(),
 | 
			
		||||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
  const { form: formInst, formProps } = useAntdForm({
 | 
			
		||||
| 
						 | 
				
			
			@ -105,33 +106,33 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  const providerLabel = useMemo(() => {
 | 
			
		||||
    switch (range) {
 | 
			
		||||
    switch (usage) {
 | 
			
		||||
      case "ca-only":
 | 
			
		||||
        return t("access.form.certificate_authority.label");
 | 
			
		||||
      case "notify-only":
 | 
			
		||||
      case "notification-only":
 | 
			
		||||
        return t("access.form.notification_channel.label");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return t("access.form.provider.label");
 | 
			
		||||
  }, [range]);
 | 
			
		||||
  }, [usage]);
 | 
			
		||||
  const providerPlaceholder = useMemo(() => {
 | 
			
		||||
    switch (range) {
 | 
			
		||||
    switch (usage) {
 | 
			
		||||
      case "ca-only":
 | 
			
		||||
        return t("access.form.certificate_authority.placeholder");
 | 
			
		||||
      case "notify-only":
 | 
			
		||||
      case "notification-only":
 | 
			
		||||
        return t("access.form.notification_channel.placeholder");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return t("access.form.provider.placeholder");
 | 
			
		||||
  }, [range]);
 | 
			
		||||
  }, [usage]);
 | 
			
		||||
  const providerTooltip = useMemo(() => {
 | 
			
		||||
    switch (range) {
 | 
			
		||||
    switch (usage) {
 | 
			
		||||
      case "both-dns-hosting":
 | 
			
		||||
        return <span dangerouslySetInnerHTML={{ __html: t("access.form.provider.tooltip") }}></span>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return undefined;
 | 
			
		||||
  }, [range]);
 | 
			
		||||
  }, [usage]);
 | 
			
		||||
 | 
			
		||||
  const fieldProvider = Form.useWatch("provider", formInst);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -269,6 +270,7 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
 | 
			
		|||
      getFieldsValue: () => {
 | 
			
		||||
        const values = formInst.getFieldsValue(true);
 | 
			
		||||
        values.config = nestedFormInst.getFieldsValue();
 | 
			
		||||
        values.reserve = usage === "ca-only" ? "ca" : usage === "notification-only" ? "notification" : undefined;
 | 
			
		||||
        return values;
 | 
			
		||||
      },
 | 
			
		||||
      resetFields: (fields) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -297,20 +299,20 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
 | 
			
		|||
          <Form.Item name="provider" label={providerLabel} rules={[formRule]} tooltip={providerTooltip}>
 | 
			
		||||
            <AccessProviderSelect
 | 
			
		||||
              filter={(record) => {
 | 
			
		||||
                if (range == null) return true;
 | 
			
		||||
                if (usage == null) return true;
 | 
			
		||||
 | 
			
		||||
                switch (range) {
 | 
			
		||||
                switch (usage) {
 | 
			
		||||
                  case "both-dns-hosting":
 | 
			
		||||
                    return record.usages.includes(ACCESS_USAGES.DNS) || record.usages.includes(ACCESS_USAGES.HOSTING);
 | 
			
		||||
                  case "ca-only":
 | 
			
		||||
                    return record.usages.includes(ACCESS_USAGES.CA);
 | 
			
		||||
                  case "notify-only":
 | 
			
		||||
                  case "notification-only":
 | 
			
		||||
                    return record.usages.includes(ACCESS_USAGES.NOTIFICATION);
 | 
			
		||||
                }
 | 
			
		||||
              }}
 | 
			
		||||
              disabled={scene !== "add"}
 | 
			
		||||
              placeholder={providerPlaceholder}
 | 
			
		||||
              showOptionTags={range == null || (range === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)}
 | 
			
		||||
              showOptionTags={usage == null || (usage === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)}
 | 
			
		||||
              showSearch={!disabled}
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Item>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -352,7 +352,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
                </div>
 | 
			
		||||
                <div className="text-right">
 | 
			
		||||
                  <AccessEditModal
 | 
			
		||||
                    range="both-dns-hosting"
 | 
			
		||||
                    scene="add"
 | 
			
		||||
                    trigger={
 | 
			
		||||
                      <Button size="small" type="link">
 | 
			
		||||
| 
						 | 
				
			
			@ -360,6 +359,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
                        <PlusOutlinedIcon className="text-xs" />
 | 
			
		||||
                      </Button>
 | 
			
		||||
                    }
 | 
			
		||||
                    usage="both-dns-hosting"
 | 
			
		||||
                    afterSubmit={(record) => {
 | 
			
		||||
                      const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                      if (provider?.usages?.includes(ACCESS_USAGES.DNS)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -374,6 +374,8 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
            <Form.Item name="providerAccessId" rules={[formRule]}>
 | 
			
		||||
              <AccessSelect
 | 
			
		||||
                filter={(record) => {
 | 
			
		||||
                  if (record.reserve) return false;
 | 
			
		||||
 | 
			
		||||
                  const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                  return !!provider?.usages?.includes(ACCESS_USAGES.DNS);
 | 
			
		||||
                }}
 | 
			
		||||
| 
						 | 
				
			
			@ -429,7 +431,6 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
                <div className="text-right">
 | 
			
		||||
                  <AccessEditModal
 | 
			
		||||
                    data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
 | 
			
		||||
                    range="ca-only"
 | 
			
		||||
                    scene="add"
 | 
			
		||||
                    trigger={
 | 
			
		||||
                      <Button size="small" type="link">
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +438,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
                        <PlusOutlinedIcon className="text-xs" />
 | 
			
		||||
                      </Button>
 | 
			
		||||
                    }
 | 
			
		||||
                    usage="ca-only"
 | 
			
		||||
                    afterSubmit={(record) => {
 | 
			
		||||
                      const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                      if (provider?.usages?.includes(ACCESS_USAGES.CA)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -450,9 +452,8 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
 | 
			
		|||
            <Form.Item name="caProviderAccessId" rules={[formRule]}>
 | 
			
		||||
              <AccessSelect
 | 
			
		||||
                filter={(record) => {
 | 
			
		||||
                  if (fieldCAProvider) {
 | 
			
		||||
                    return caProvidersMap.get(fieldCAProvider)?.provider === record.provider;
 | 
			
		||||
                  }
 | 
			
		||||
                  if (!!record.reserve && record.reserve !== "ca") return false;
 | 
			
		||||
                  if (fieldCAProvider) return caProvidersMap.get(fieldCAProvider)?.provider === record.provider;
 | 
			
		||||
 | 
			
		||||
                  const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                  return !!provider?.usages?.includes(ACCESS_USAGES.CA);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -409,7 +409,6 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
 | 
			
		|||
                  <div className="text-right">
 | 
			
		||||
                    <AccessEditModal
 | 
			
		||||
                      data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
 | 
			
		||||
                      range="both-dns-hosting"
 | 
			
		||||
                      scene="add"
 | 
			
		||||
                      trigger={
 | 
			
		||||
                        <Button size="small" type="link">
 | 
			
		||||
| 
						 | 
				
			
			@ -417,6 +416,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
 | 
			
		|||
                          <PlusOutlinedIcon className="text-xs" />
 | 
			
		||||
                        </Button>
 | 
			
		||||
                      }
 | 
			
		||||
                      usage="both-dns-hosting"
 | 
			
		||||
                      afterSubmit={(record) => {
 | 
			
		||||
                        const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                        if (provider?.usages?.includes(ACCESS_USAGES.HOSTING)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -430,9 +430,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
 | 
			
		|||
              <Form.Item name="providerAccessId" rules={[formRule]}>
 | 
			
		||||
                <AccessSelect
 | 
			
		||||
                  filter={(record) => {
 | 
			
		||||
                    if (fieldProvider) {
 | 
			
		||||
                      return deploymentProvidersMap.get(fieldProvider)?.provider === record.provider;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (record.reserve) return false;
 | 
			
		||||
                    if (fieldProvider) return deploymentProvidersMap.get(fieldProvider)?.provider === record.provider;
 | 
			
		||||
 | 
			
		||||
                    const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                    return !!provider?.usages?.includes(ACCESS_USAGES.HOSTING);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -228,7 +228,6 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
 | 
			
		|||
              </div>
 | 
			
		||||
              <div className="text-right">
 | 
			
		||||
                <AccessEditModal
 | 
			
		||||
                  range="notify-only"
 | 
			
		||||
                  scene="add"
 | 
			
		||||
                  trigger={
 | 
			
		||||
                    <Button size="small" type="link">
 | 
			
		||||
| 
						 | 
				
			
			@ -236,6 +235,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
 | 
			
		|||
                      <PlusOutlinedIcon className="text-xs" />
 | 
			
		||||
                    </Button>
 | 
			
		||||
                  }
 | 
			
		||||
                  usage="notification-only"
 | 
			
		||||
                  afterSubmit={(record) => {
 | 
			
		||||
                    const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                    if (provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -250,6 +250,8 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
 | 
			
		|||
          <Form.Item name="providerAccessId" rules={[formRule]}>
 | 
			
		||||
            <AccessSelect
 | 
			
		||||
              filter={(record) => {
 | 
			
		||||
                if (!!record.reserve && record.reserve !== "notification") return false;
 | 
			
		||||
 | 
			
		||||
                const provider = accessProvidersMap.get(record.provider);
 | 
			
		||||
                return !!provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION);
 | 
			
		||||
              }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,6 +56,7 @@ export interface AccessModel extends BaseModel {
 | 
			
		|||
      | AccessConfigForWestcn
 | 
			
		||||
      | AccessConfigForZeroSSL
 | 
			
		||||
    );
 | 
			
		||||
  reserve?: "ca" | "notification";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #region AccessConfig
 | 
			
		||||
| 
						 | 
				
			
			@ -310,8 +311,8 @@ export type AccessConfigForWebhook = {
 | 
			
		|||
  method: string;
 | 
			
		||||
  headers?: string;
 | 
			
		||||
  allowInsecureConnections?: boolean;
 | 
			
		||||
  templateDataForDeployment?: string;
 | 
			
		||||
  templateDataForNotification?: string;
 | 
			
		||||
  defaultDataForDeployment?: string;
 | 
			
		||||
  defaultDataForNotification?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AccessConfigForWestcn = {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,9 +18,9 @@
 | 
			
		|||
  "access.props.provider.usage.ca": "Certificate authority",
 | 
			
		||||
  "access.props.provider.usage.notification": "Notification channel",
 | 
			
		||||
  "access.props.provider.builtin": "Built-in",
 | 
			
		||||
  "access.props.range.both_dns_hosting": "Provider",
 | 
			
		||||
  "access.props.range.ca_only": "Certificate authority",
 | 
			
		||||
  "access.props.range.notify_only": "Notification channel",
 | 
			
		||||
  "access.props.usage.both_dns_hosting": "Provider",
 | 
			
		||||
  "access.props.usage.ca_only": "Certificate authority",
 | 
			
		||||
  "access.props.usage.notification_only": "Notification channel",
 | 
			
		||||
  "access.props.created_at": "Created at",
 | 
			
		||||
  "access.props.updated_at": "Updated at",
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,9 +18,9 @@
 | 
			
		|||
  "access.props.provider.usage.ca": "证书颁发机构",
 | 
			
		||||
  "access.props.provider.usage.notification": "通知渠道",
 | 
			
		||||
  "access.props.provider.builtin": "内置",
 | 
			
		||||
  "access.props.range.both_dns_hosting": "提供商",
 | 
			
		||||
  "access.props.range.ca_only": "证书颁发机构",
 | 
			
		||||
  "access.props.range.notify_only": "通知渠道",
 | 
			
		||||
  "access.props.usage.both_dns_hosting": "提供商",
 | 
			
		||||
  "access.props.usage.ca_only": "证书颁发机构",
 | 
			
		||||
  "access.props.usage.notification_only": "通知渠道",
 | 
			
		||||
  "access.props.created_at": "创建时间",
 | 
			
		||||
  "access.props.updated_at": "更新时间",
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ import { useZustandShallowSelector } from "@/hooks";
 | 
			
		|||
import { useAccessesStore } from "@/stores/access";
 | 
			
		||||
import { getErrMsg } from "@/utils/error";
 | 
			
		||||
 | 
			
		||||
type AccessRanges = AccessEditDrawerProps["range"];
 | 
			
		||||
type AccessUsageProp = AccessEditDrawerProps["usage"];
 | 
			
		||||
 | 
			
		||||
const AccessList = () => {
 | 
			
		||||
  const [searchParams] = useSearchParams();
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +87,7 @@ const AccessList = () => {
 | 
			
		|||
        <Space.Compact>
 | 
			
		||||
          <AccessEditDrawer
 | 
			
		||||
            data={record}
 | 
			
		||||
            range={filters["range"] as AccessRanges}
 | 
			
		||||
            usage={filters["usage"] as AccessUsageProp}
 | 
			
		||||
            scene="edit"
 | 
			
		||||
            trigger={
 | 
			
		||||
              <Tooltip title={t("access.action.edit")}>
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ const AccessList = () => {
 | 
			
		|||
 | 
			
		||||
          <AccessEditDrawer
 | 
			
		||||
            data={{ ...record, id: undefined, name: `${record.name}-copy` }}
 | 
			
		||||
            range={filters["range"] as AccessRanges}
 | 
			
		||||
            usage={filters["usage"] as AccessUsageProp}
 | 
			
		||||
            scene="add"
 | 
			
		||||
            trigger={
 | 
			
		||||
              <Tooltip title={t("access.action.duplicate")}>
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +126,7 @@ const AccessList = () => {
 | 
			
		|||
 | 
			
		||||
  const [filters, setFilters] = useState<Record<string, unknown>>(() => {
 | 
			
		||||
    return {
 | 
			
		||||
      range: "both-dns-hosting" satisfies AccessRanges,
 | 
			
		||||
      usage: "both-dns-hosting" satisfies AccessUsageProp,
 | 
			
		||||
      keyword: searchParams.get("keyword"),
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -160,13 +160,13 @@ const AccessList = () => {
 | 
			
		|||
        })
 | 
			
		||||
        .filter((e) => {
 | 
			
		||||
          const provider = accessProvidersMap.get(e.provider);
 | 
			
		||||
          switch (filters["range"] as AccessRanges) {
 | 
			
		||||
          switch (filters["usage"] as AccessUsageProp) {
 | 
			
		||||
            case "both-dns-hosting":
 | 
			
		||||
              return provider?.usages?.includes(ACCESS_USAGES.DNS) || provider?.usages?.includes(ACCESS_USAGES.HOSTING);
 | 
			
		||||
              return !e.reserve && (provider?.usages?.includes(ACCESS_USAGES.DNS) || provider?.usages?.includes(ACCESS_USAGES.HOSTING));
 | 
			
		||||
            case "ca-only":
 | 
			
		||||
              return provider?.usages?.includes(ACCESS_USAGES.CA);
 | 
			
		||||
            case "notify-only":
 | 
			
		||||
              return provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION);
 | 
			
		||||
              return e.reserve === "ca" && provider?.usages?.includes(ACCESS_USAGES.CA);
 | 
			
		||||
            case "notification-only":
 | 
			
		||||
              return e.reserve === "notification" && provider?.usages?.includes(ACCESS_USAGES.NOTIFICATION);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      return Promise.resolve({
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +184,7 @@ const AccessList = () => {
 | 
			
		|||
  );
 | 
			
		||||
 | 
			
		||||
  const handleTabChange = (key: string) => {
 | 
			
		||||
    setFilters((prev) => ({ ...prev, range: key }));
 | 
			
		||||
    setFilters((prev) => ({ ...prev, usage: key }));
 | 
			
		||||
    setPage(1);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +226,7 @@ const AccessList = () => {
 | 
			
		|||
        extra={[
 | 
			
		||||
          <AccessEditDrawer
 | 
			
		||||
            key="create"
 | 
			
		||||
            range={filters["range"] as AccessRanges}
 | 
			
		||||
            usage={filters["usage"] as AccessUsageProp}
 | 
			
		||||
            scene="add"
 | 
			
		||||
            trigger={
 | 
			
		||||
              <Button type="primary" icon={<PlusOutlinedIcon />}>
 | 
			
		||||
| 
						 | 
				
			
			@ -247,18 +247,18 @@ const AccessList = () => {
 | 
			
		|||
        tabList={[
 | 
			
		||||
          {
 | 
			
		||||
            key: "both-dns-hosting",
 | 
			
		||||
            label: t("access.props.range.both_dns_hosting"),
 | 
			
		||||
            label: t("access.props.usage.both_dns_hosting"),
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            key: "ca-only",
 | 
			
		||||
            label: t("access.props.range.ca_only"),
 | 
			
		||||
            label: t("access.props.usage.ca_only"),
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            key: "notify-only",
 | 
			
		||||
            label: t("access.props.range.notify_only"),
 | 
			
		||||
            key: "notification-only",
 | 
			
		||||
            label: t("access.props.usage.notification_only"),
 | 
			
		||||
          },
 | 
			
		||||
        ]}
 | 
			
		||||
        activeTabKey={filters["range"] as string}
 | 
			
		||||
        activeTabKey={filters["usage"] as string}
 | 
			
		||||
        onTabChange={(key) => handleTabChange(key)}
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue