【调整】SSH地址支持域名形式

【新增】支持自定义监控端口
【新增】通知类型-企业微信
【新增】申请证书(Buypass)、自定义ACME服务器地址
【新增】授权API管理(namesilo、Bunny、Gcore、name.com、京东云)
This commit is contained in:
chudong
2025-06-07 17:37:42 +08:00
parent 87ae1c9570
commit f0d83d23c6
106 changed files with 1570 additions and 223 deletions

View File

@@ -114,9 +114,11 @@ export default defineComponent<CAProviderSelectProps>({
class="flex-1 w-full"
options={caProviderRef.value}
renderLabel={renderLabel}
renderTag={({ option }: { option: any }) => renderSingleSelectTag({ option: option as CAProviderOption })}
renderTag={({ option }: { option: unknown }) =>
renderSingleSelectTag({ option: option as CAProviderOption })
}
filterable
filter={(pattern: string, option: any) => handleFilter(pattern, option as CAProviderOption)}
filter={(pattern: string, option: unknown) => handleFilter(pattern, option as CAProviderOption)}
placeholder={$t('t_0_1747990228780')}
value={param.value.value} // 使用 controller 中的 param.value.value
onUpdateValue={handleUpdateValue}

View File

@@ -31,7 +31,7 @@ export interface CAProviderSelectProps {
ca: string
/**
* @property email
* @description 邮箱地址,当 value 不为空时会被赋值
* @description 邮箱地址,编辑时从工作流content中传入新建时为空
*/
email: string
/**

View File

@@ -44,26 +44,36 @@ export function useCAProviderSelectController(props: CAProviderSelectProps, emit
const selectedProvider = caProviderRef.value.find((item) => item.value === param.value.value)
if (selectedProvider) {
// 对于 Let's Encrypt 和 Buypass使用传入的工作流邮件
let email = selectedProvider.email
if (selectedProvider.ca === 'letsencrypt' || selectedProvider.ca === 'buypass') {
email = props.email || selectedProvider.email
}
param.value = {
label: selectedProvider.label,
value: selectedProvider.value,
ca: selectedProvider.ca,
email: selectedProvider.email,
email: email,
}
} else if (caProviderRef.value.length > 0 && param.value.value === '') {
// 如果 param.value 为空(例如初始状态或清空后),且 caProviderRef 列表不为空,则默认选中第一个
const firstProvider = caProviderRef.value[0]
let email = firstProvider?.email || ''
if (firstProvider && (firstProvider.ca === 'letsencrypt' || firstProvider.ca === 'buypass')) {
email = props.email || firstProvider.email
}
param.value = {
label: caProviderRef.value[0]?.label || '',
value: caProviderRef.value[0]?.value || '',
ca: caProviderRef.value[0]?.ca || '',
email: caProviderRef.value[0]?.email || '',
label: firstProvider?.label || '',
value: firstProvider?.value || '',
ca: firstProvider?.ca || '',
email: email,
}
}
// 当 value 不为空时,将其赋值给 email 字段
if (param.value.value !== '') {
emit('update:email', param.value.email)
}
// 始终触发邮件更新事件,确保邮件字段能正确渲染
emit('update:email', param.value.email)
emit('update:value', { value: param.value.value, ca: param.value.ca, email: param.value.email })
}
@@ -86,15 +96,23 @@ export function useCAProviderSelectController(props: CAProviderSelectProps, emit
isLoading.value = true
errorMessage.value = ''
try {
// 添加Let's Encrypt作为首选项
// 添加Let's Encrypt作为首选项,使用传入的工作流邮件
const letsEncryptOption: CAProviderOption = {
label: "Let's Encrypt",
value: '',
ca: 'letsencrypt',
email: '',
email: props.email || '',
}
// 获取其他CA授权列表
// 添加Buypass作为第二选项使用传入的工作流邮件
const buypassOption: CAProviderOption = {
label: 'Buypass',
value: 'buypass',
ca: 'buypass',
email: props.email || '',
}
// 获取其他CA授权列表使用接口返回的邮件
const { data } = await getAllEabList({ ca: '' }).fetch()
const eabOptions: CAProviderOption[] = (data || []).map((item) => ({
label: item.name,
@@ -103,8 +121,8 @@ export function useCAProviderSelectController(props: CAProviderSelectProps, emit
email: item.mail,
}))
// 合并选项Let's Encrypt在首位
caProviderRef.value = [letsEncryptOption, ...eabOptions]
// 合并选项Let's Encrypt在首位Buypass在第二位
caProviderRef.value = [letsEncryptOption, buypassOption, ...eabOptions]
// 数据加载后,如果 props.value 有值,尝试根据 props.value 初始化 param
if (props.value) {

View File

@@ -0,0 +1,115 @@
// External Libraries
import { defineComponent } from 'vue'
import { NModal, NButton, NSpace, NScrollbar, NIcon } from 'naive-ui'
import { LogoGithub } from '@vicons/ionicons5'
// Type Imports
import type { PropType } from 'vue'
import type { UpdateLogModalProps } from './types'
import type { VersionData } from '@/types/setting'
// Relative Internal Imports - Controller
import { useUpdateLogModalController } from './useController'
/**
* @description 更新日志弹窗组件。显示版本更新信息和更新日志。
* @example
* <UpdateLogModal
* v-model:show="showModal"
* :versionData="versionData"
* />
*/
export default defineComponent({
name: 'UpdateLogModal',
props: {
/**
* 是否显示弹窗
* @default false
*/
show: {
type: Boolean as PropType<UpdateLogModalProps['show']>,
default: false,
},
/**
* 版本数据
* @default null
*/
versionData: {
type: Object as PropType<VersionData | null>,
default: null,
},
},
/**
* @event update:show - 当弹窗显示状态更新时触发
* @param {boolean} show - 弹窗显示状态
*/
emits: {
'update:show': (payload: boolean) => typeof payload === 'boolean',
},
setup(props: UpdateLogModalProps, { emit }) {
const { formattedLog, goToGitHub, handleClose } = useUpdateLogModalController(props, emit)
return () => (
<NModal
show={props.show}
onUpdateShow={(show: boolean) => emit('update:show', show)}
preset="card"
title="发现新版本"
style={{ width: '600px', maxWidth: '90vw' }}
maskClosable={false}
closable={true}
onClose={handleClose}
>
{props.versionData && (
<div class="update-log-content">
{/* 版本信息 */}
<div class="mb-[1.6rem]">
<div class="flex items-center justify-between mb-[.8rem]">
<span class="text-[1.5rem] font-medium">: {props.versionData.version}</span>
<span class="text-[1.5rem] font-medium text-primary">: {props.versionData.new_version}</span>
</div>
<div class="text-[1.4rem] text-gray-500">: {props.versionData.date}</div>
</div>
{/* 更新日志 */}
<div class="mb-[2.4rem]">
<h3 class="text-[1.6rem] font-medium mb-[1.2rem]"></h3>
<NScrollbar style={{ maxHeight: '300px' }}>
<div class="update-log-list">
{formattedLog.value.map((line, index) => (
<div key={index} class="mb-[.8rem]">
{line.startsWith('■') ? (
<div class="text-[1.4rem] font-medium text-primary mb-[.4rem]">{line}</div>
) : line.startsWith('新增:') || line.startsWith('调整:') ? (
<div class="ml-[1.6rem] text-[1.3rem] text-green-600">{line}</div>
) : (
<div class="ml-[1.6rem] text-[1.3rem] text-gray-700">{line}</div>
)}
</div>
))}
</div>
</NScrollbar>
</div>
{/* 操作按钮 */}
<div class="flex justify-end">
<NSpace size="medium">
<NButton size="medium" onClick={handleClose}>
<span class="text-[1.4rem]"></span>
</NButton>
<NButton size="medium" type="primary" onClick={goToGitHub}>
<div class="flex items-center">
<NIcon size="18" class="mr-[.8rem]">
<LogoGithub />
</NIcon>
<span class="text-[1.4rem]">GitHub下载</span>
</div>
</NButton>
</NSpace>
</div>
</div>
)}
</NModal>
)
},
})

View File

@@ -0,0 +1,44 @@
// Type Imports
import type { Ref } from 'vue'
import type { VersionData } from '@/types/setting'
/**
* @description 更新日志弹窗组件的 Props 定义
*/
export interface UpdateLogModalProps {
/**
* 是否显示弹窗
* @default false
*/
show: boolean
/**
* 版本数据
* @default null
*/
versionData: VersionData | null
}
/**
* @description 更新日志弹窗组件的 Emits 定义
*/
export interface UpdateLogModalEmits {
(e: 'update:show', show: boolean): void
}
/**
* @description 更新日志弹窗控制器暴露给视图的数据和方法
*/
export interface UpdateLogModalControllerExposes {
/**
* 格式化后的更新日志行数组
*/
formattedLog: Ref<string[]>
/**
* 跳转到GitHub的方法
*/
goToGitHub: () => void
/**
* 关闭弹窗的方法
*/
handleClose: () => void
}

View File

@@ -0,0 +1,50 @@
// External Libraries
import { computed } from 'vue'
// Type Imports
import type { UpdateLogModalProps, UpdateLogModalEmits, UpdateLogModalControllerExposes } from './types'
/**
* @description 更新日志弹窗组件的控制器逻辑
* @param props - 组件的 props
* @param emit - 组件的 emit 函数
* @returns {UpdateLogModalControllerExposes} 暴露给视图的响应式数据和方法
*/
export function useUpdateLogModalController(
props: UpdateLogModalProps,
emit: (event: 'update:show', payload: boolean) => void,
): UpdateLogModalControllerExposes {
/**
* @description 处理更新日志文本,将 \r\n 和 \n 转换为换行
*/
const formattedLog = computed(() => {
if (!props.versionData?.log) return []
return props.versionData.log
.replace(/\\r\\n/g, '\n')
.replace(/\\n/g, '\n')
.split('\n')
.filter(line => line.trim() !== '')
})
/**
* @description 跳转到GitHub
*/
const goToGitHub = (): void => {
window.open('https://github.com/allinssl/allinssl', '_blank')
emit('update:show', false)
}
/**
* @description 关闭弹窗
*/
const handleClose = (): void => {
emit('update:show', false)
}
return {
formattedLog,
goToGitHub,
handleClose,
}
}

View File

@@ -114,6 +114,9 @@ nodeOptions[APPLY] = () =>
provider_id: '',
algorithm: 'RSA2048',
skip_check: 0,
close_cname: 0,
max_wait: undefined,
ignore_check: 0,
},
childNode: null,
},

View File

@@ -217,6 +217,9 @@ export interface ApplyNodeConfig {
name_server: string // DNS递归服务器
skip_check: number // 跳过检查
algorithm: string // 数字证书算法
close_cname: number // 禁用CNAME支持0关闭1开启默认0
max_wait?: number // 预检查超时时间,单位秒(可选)
ignore_check: number // 忽略预检查结果1继续0停止默认0
// 高级功能
// algorithm: 'RSA2048' | 'RSA3072' | 'RSA4096' | 'RSA8192' | 'EC256' | 'EC384' // 数字证书算法
// dnsServer?: string // 指定DNS解析服务器