mirror of https://github.com/certd/certd
				
				
				
			chore: test优化
							parent
							
								
									cac949de56
								
							
						
					
					
						commit
						31f82e58b5
					
				|  | @ -14,7 +14,7 @@ import { usePreferences } from "/@/vben/preferences"; | |||
| import { LocalStorage } from "/@/utils/util.storage"; | ||||
| 
 | ||||
| import { FsEditorCode } from "@fast-crud/editor-code"; | ||||
| import "@fast-crud/editor-code/dist/style.css" | ||||
| import "@fast-crud/editor-code/dist/style.css"; | ||||
| 
 | ||||
| class ColumnSizeSaver { | ||||
|   save: (key: string, size: number) => void; | ||||
|  |  | |||
|  | @ -19,6 +19,10 @@ div#app { | |||
|   height: 100%; | ||||
| } | ||||
| 
 | ||||
| pre.pre{ | ||||
|    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||||
| } | ||||
| 
 | ||||
| h1, | ||||
| h2, | ||||
| h3, | ||||
|  |  | |||
|  | @ -54,8 +54,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>( | |||
|           ); | ||||
|       }, | ||||
|       { | ||||
|         inheritAttrs: false, | ||||
|         name: "VbenParentModal", | ||||
|         inheritAttrs: false, | ||||
|       } | ||||
|     ); | ||||
|     return [Modal, extendedApi as ExtendedModalApi] as const; | ||||
|  | @ -104,8 +104,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>( | |||
|         ); | ||||
|     }, | ||||
|     { | ||||
|       inheritAttrs: false, | ||||
|       name: "VbenModal", | ||||
|       inheritAttrs: false, | ||||
|     } | ||||
|   ); | ||||
|   injectData.extendApi?.(extendedApi); | ||||
|  |  | |||
|  | @ -57,4 +57,3 @@ export async function DeleteBatch(ids: any[]) { | |||
|     data: { ids }, | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -385,10 +385,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat | |||
|           column: { | ||||
|             conditionalRender: false, | ||||
|             cellRender({ row }) { | ||||
|               const { | ||||
|                 certEffectiveTime: effectiveTime, | ||||
|                 certExpiresTime: expiresTime, | ||||
|               } = row || {}; | ||||
|               const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row || {}; | ||||
|               if (!expiresTime) { | ||||
|                 return "-"; | ||||
|               } | ||||
|  |  | |||
|  | @ -366,10 +366,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys | |||
|           }, | ||||
|           column: { | ||||
|             cellRender({ row }) { | ||||
|               const { | ||||
|                 certEffectiveTime: effectiveTime, | ||||
|                 certExpiresTime: expiresTime, | ||||
|               } = row?.lastVars || {}; | ||||
|               const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row?.lastVars || {}; | ||||
|               if (!expiresTime) { | ||||
|                 return "-"; | ||||
|               } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| <template> | ||||
|   <div class="domain-test-card"> | ||||
|     <div class="card-header"> | ||||
|     <div class="card-header flex flex-wrap justify-start"> | ||||
|       <div v-if="title">{{ title }}</div> | ||||
|       <a-form v-if="editing" layout="inline" :model="formData"> | ||||
|         <a-form-item label="域名"> | ||||
|           <a-input v-model:value="formData.domain" placeholder="请输入要测试的域名或IP" style="width: 240px" /> | ||||
|  | @ -29,18 +30,27 @@ | |||
|         <!-- Telnet测试结果 --> | ||||
|         <test-case ref="telnetTestRef" title="Telnet测试" :port="getCurrentPort()" :test-method="() => createTelnetTestMethod()" :disabled="!getCurrentDomain() || !getCurrentPort()" /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="summary"> | ||||
|         <a-alert :message="testSummary.title" :type="testSummary.status === 'success' ? 'success' : testSummary.status === 'failed' ? 'error' : 'warning'" show-icon :closable="false"> | ||||
|           <template v-if="testSummary.text" #description> | ||||
|             <pre class="summary-text pre">{{ testSummary.text }}</pre> | ||||
|           </template> | ||||
|         </a-alert> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ref, reactive, computed, onMounted } from "vue"; | ||||
| import { ref, reactive, computed, onMounted, watch } from "vue"; | ||||
| import { message } from "ant-design-vue"; | ||||
| import { DomainResolve, PingTest, TelnetTest } from "./api"; | ||||
| import TestCase from "./TestCase.vue"; | ||||
| 
 | ||||
| // 组件属性 | ||||
| const props = defineProps<{ | ||||
|   title?: string; | ||||
|   domain?: string; | ||||
|   port?: number; | ||||
|   autoStart?: boolean; | ||||
|  | @ -92,6 +102,91 @@ const getCurrentPort = () => { | |||
|   return formData.port; | ||||
| }; | ||||
| 
 | ||||
| // 获取各测试用例的状态 | ||||
| const getTestStatus = (testRef: any) => { | ||||
|   const result = testRef?.getResult(); | ||||
|   if (!result) { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   const isNetTestResult = typeof result === "object" && result !== null && "success" in result && "message" in result; | ||||
| 
 | ||||
|   return { | ||||
|     success: isNetTestResult ? result.success : false, | ||||
|     message: isNetTestResult ? result.message : "测试失败", | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| // 生成测试总结 | ||||
| const testSummary = computed(() => { | ||||
|   if (loading.value) { | ||||
|     return { status: "waiting", title: "测试中,请稍后..." }; | ||||
|   } | ||||
|   // 通过computed获取各测试结果 | ||||
|   const domainResolveResult = getTestStatus(domainResolveRef.value); | ||||
|   const pingTestResult = getTestStatus(pingTestRef.value); | ||||
|   const telnetTestResult = getTestStatus(telnetTestRef.value); | ||||
| 
 | ||||
|   // 检查是否有测试结果 | ||||
|   const testDone = domainResolveResult != null && pingTestResult != null && telnetTestResult != null; | ||||
|   if (!testDone) { | ||||
|     return { status: "waiting", title: '请点击"开始测试"按钮进行网络测试' }; | ||||
|   } | ||||
| 
 | ||||
|   // 详细分析不同的测试结果组合 | ||||
|   // 1. 三个测试都失败 | ||||
|   if (domainResolveResult?.success === false && pingTestResult?.success === false && telnetTestResult?.success === false) { | ||||
|     return { | ||||
|       status: "failed", | ||||
|       title: "所有测试均未通过", | ||||
|       text: `这表明应用容器内的网络可能完全不通。建议:\n1. 检查宿主机的网络连接状态\n2. 确认容器网络配置是否正确\n3. 检查防火墙设置是否阻止了网络访问`, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // 2. 域名解析成功,但Ping不通 | ||||
|   if (domainResolveResult?.success === true && pingTestResult?.success === false) { | ||||
|     return { | ||||
|       status: "partial", | ||||
|       title: "域名解析成功,但Ping不通", | ||||
|       text: `可能原因:\n1. DNS被劫持,解析到了错误的IP地址\n2. 目标服务器禁止了Ping请求\n3. 目标服务器IP被墙\n4. 目标服务器网络不通或已下线`, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // 3. 域名解析和Ping都成功,但Telnet连接失败 | ||||
|   if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === false) { | ||||
|     return { | ||||
|       status: "partial", | ||||
|       title: "域名解析和Ping测试均通过,但Telnet连接失败", | ||||
|       text: `可能原因:\n1. 端口号输入错误,请确认目标服务使用的正确端口\n2. 目标服务器上该端口未开放或服务未启动\n3. 防火墙或安全组限制了该端口的访问\n4. 目标网站被墙`, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // 4. 域名解析失败,但其他测试可能成功或未执行 | ||||
|   if (domainResolveResult?.success === false) { | ||||
|     return { | ||||
|       status: "partial", | ||||
|       title: "域名解析失败", | ||||
|       text: `可能原因:\n1. 域名输入错误或不存在\n2. DNS服务器配置问题\n3. 本地网络DNS解析故障\n4. 域名已过期或被注销`, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // 5. 所有测试都成功 | ||||
|   if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === true) { | ||||
|     return { | ||||
|       status: "success", | ||||
|       title: "所有测试均通过", | ||||
|       text: `域名${formData.domain}解析正常,能够正常Ping通,且端口${formData.port}可访问。`, | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   // 6. 其他部分成功的情况 | ||||
|   return { | ||||
|     status: "partial", | ||||
|     title: "部分测试未通过", | ||||
|     text: `请结合具体测试结果进行分析:\n- 域名解析:${domainResolveResult ? (domainResolveResult.success ? "成功" : "失败") : "未执行"}\n- Ping测试:${pingTestResult ? (pingTestResult.success ? "成功" : "失败") : "未执行"}\n- Telnet测试:${telnetTestResult ? (telnetTestResult.success ? "成功" : "失败") : "未执行"}`, | ||||
|   }; | ||||
| }); | ||||
| 
 | ||||
| // 运行全部测试 | ||||
| async function runAllTests() { | ||||
|   const domain = getCurrentDomain(); | ||||
|  | @ -121,64 +216,73 @@ onMounted(() => { | |||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| <style lang="less"> | ||||
| .domain-test-card { | ||||
|   border: 1px solid #e8e8e8; | ||||
|   border-radius: 4px; | ||||
|   overflow: hidden; | ||||
|   background-color: #fff; | ||||
| } | ||||
| 
 | ||||
| .card-header { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   padding: 12px 16px; | ||||
|   background-color: #fafafa; | ||||
|   border-bottom: 1px solid #e8e8e8; | ||||
| } | ||||
|   .card-header { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     align-items: center; | ||||
|     padding: 12px 16px; | ||||
|     background-color: #fafafa; | ||||
|     border-bottom: 1px solid #e8e8e8; | ||||
|   } | ||||
| 
 | ||||
| .card-header h3 { | ||||
|   margin: 0; | ||||
|   font-size: 16px; | ||||
|   font-weight: 500; | ||||
| } | ||||
|   .card-header h3 { | ||||
|     margin: 0; | ||||
|     font-size: 16px; | ||||
|     font-weight: 500; | ||||
|   } | ||||
| 
 | ||||
| .card-content { | ||||
|   padding: 16px; | ||||
| } | ||||
|   .card-content { | ||||
|     padding: 16px; | ||||
|   } | ||||
| 
 | ||||
| .input-form { | ||||
|   margin-bottom: 12px; | ||||
|   padding: 12px; | ||||
|   background-color: #fafafa; | ||||
|   border-radius: 4px; | ||||
| } | ||||
|   .input-form { | ||||
|     margin-bottom: 12px; | ||||
|     padding: 12px; | ||||
|     background-color: #fafafa; | ||||
|     border-radius: 4px; | ||||
|   } | ||||
| 
 | ||||
| .domain-info { | ||||
|   padding: 5.5px 12px; | ||||
|   background-color: #f0f0f0; | ||||
|   border-radius: 4px; | ||||
|   display: flex; | ||||
|   gap: 16px; | ||||
|   font-size: 14px; | ||||
|   color: #666; | ||||
| } | ||||
|   .domain-info { | ||||
|     padding: 5.5px 12px; | ||||
|     background-color: #f0f0f0; | ||||
|     border-radius: 4px; | ||||
|     display: flex; | ||||
|     gap: 16px; | ||||
|     font-size: 14px; | ||||
|     color: #666; | ||||
|   } | ||||
| 
 | ||||
| .test-buttons { | ||||
|   display: flex; | ||||
|   gap: 8px; | ||||
|   margin-bottom: 16px; | ||||
| } | ||||
|   .test-buttons { | ||||
|     display: flex; | ||||
|     gap: 8px; | ||||
|     margin-bottom: 16px; | ||||
|   } | ||||
| 
 | ||||
| .test-results { | ||||
|   margin-top: 0px; | ||||
| } | ||||
|   .test-results { | ||||
|     margin-top: 0px; | ||||
|   } | ||||
| 
 | ||||
| /* 调整按钮大小 */ | ||||
| .ant-btn { | ||||
|   font-size: 12px; | ||||
|   padding: 2px 8px; | ||||
|   height: 24px; | ||||
|   .summary { | ||||
|     margin-top: 16px; | ||||
|     padding: 12px; | ||||
|     background-color: #f8f9fa; | ||||
|     border-radius: 4px; | ||||
|     .summary-text { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /* 调整按钮大小 */ | ||||
|   .ant-btn { | ||||
|     font-size: 12px; | ||||
|     padding: 2px 8px; | ||||
|     height: 24px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -77,6 +77,7 @@ const runTest = async () => { | |||
| // 暴露方法给父组件 | ||||
| defineExpose({ | ||||
|   test: runTest, | ||||
|   getResult: () => result.value, | ||||
| }); | ||||
| 
 | ||||
| // 辅助计算属性,用于模板中显示结果 | ||||
|  |  | |||
|  | @ -1,19 +1,21 @@ | |||
| <template> | ||||
|   <fs-page class="page-sys-nettest"> | ||||
|     <template #header> | ||||
|       <div class="title">网络测试</div> | ||||
|       <div class="title"> | ||||
|         网络测试 | ||||
|         <span class="sub">测试您的服务器容器网络连接是否正常</span> | ||||
|       </div> | ||||
|     </template> | ||||
|     <div class="nettest-container"> | ||||
|       <!-- 服务端信息 --> | ||||
|       <server-info-card /> | ||||
| 
 | ||||
|       <!-- 测试区域 --> | ||||
|       <div class="test-areas"> | ||||
|         <!-- 用户输入域名测试 --> | ||||
|         <domain-test-card class="w-50%" /> | ||||
| 
 | ||||
|       <div class="test-areas flex-wrap md:flex-nowrap"> | ||||
|         <!-- 百度域名测试 (用于对比) --> | ||||
|         <domain-test-card class="w-50%" :domain="'baidu.com'" :port="443" :auto-start="true" /> | ||||
|         <domain-test-card class="test-card" :domain="'baidu.com'" :port="443" :auto-start="true" /> | ||||
|         <!-- 用户输入域名测试 --> | ||||
|         <domain-test-card class="test-card" :title="'自定义域名测试'" /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </fs-page> | ||||
|  | @ -36,5 +38,9 @@ import ServerInfoCard from "./ServerInfoCard.vue"; | |||
|     gap: 16px; | ||||
|     margin-top: 16px; | ||||
|   } | ||||
| 
 | ||||
|   .test-card { | ||||
|     min-width: 50%; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|  |  | |||
|  | @ -55,15 +55,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat | |||
|           groups: { | ||||
|             base: { | ||||
|               header: t("certd.basicInfo"), | ||||
|               columns: ["title", "type", "disabled", "order", "supportBuy", "intro"] | ||||
|               columns: ["title", "type", "disabled", "order", "supportBuy", "intro"], | ||||
|             }, | ||||
|             content: { | ||||
|               header: t("certd.packageContent"), | ||||
|               columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"] | ||||
|               columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"], | ||||
|             }, | ||||
|             price: { | ||||
|               header: t("certd.price"), | ||||
|               columns: ["durationPrices"] | ||||
|               columns: ["durationPrices"], | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|  |  | |||
|  | @ -87,7 +87,7 @@ export class NetTestService { | |||
|       // 判断测试是否成功
 | ||||
|       const success = this.isWindows() | ||||
|         ? output.includes('TTL=') | ||||
|         : output.includes('0% packet loss'); | ||||
|         : output.includes('time='); | ||||
| 
 | ||||
|       return { | ||||
|         success, | ||||
|  | @ -100,7 +100,7 @@ export class NetTestService { | |||
|       return { | ||||
|         success: false, | ||||
|         message: 'Ping测试执行失败', | ||||
|         testLog: errorMessage, | ||||
|         testLog: error.stderr|| error.stdout ||  errorMessage, | ||||
|         error: errorMessage | ||||
|       }; | ||||
|     } | ||||
|  | @ -146,7 +146,7 @@ export class NetTestService { | |||
|       return { | ||||
|         success: false, | ||||
|         message: '域名解析测试执行失败', | ||||
|         testLog: errorMessage, | ||||
|         testLog: error.stdoout ||  error.stderr || errorMessage, | ||||
|         error: errorMessage | ||||
|       }; | ||||
|     } | ||||
|  | @ -201,12 +201,13 @@ export class NetTestService { | |||
|       }); | ||||
|       const line = extDnsServers.trim() | ||||
|       if (line.includes('ExtServers') && line.includes('[')) { | ||||
|         const extDns = extDnsServers.trim().split(' ')[1].replace('[', '').replace(']', '').split(' '); | ||||
|         dnsServers = dnsServers.concat(extDns); | ||||
|         const extDns = line.substring(line.indexOf('[') + 1, line.indexOf(']')).split(' '); | ||||
|         const dnsList = extDns.map(item=>`Ext:${item}`) | ||||
|         dnsServers = dnsServers.concat(dnsList);  | ||||
|       } | ||||
|     } catch (error) { | ||||
|       logger.error('获取DNS服务器失败', error); | ||||
|       dnsServers.push(error instanceof Error ? error.message : String(error)); | ||||
|       logger.error('获取DNS ExtServers 服务器失败', error); | ||||
|       // dnsServers.push(error instanceof Error ? error.message : String(error));
 | ||||
|     } | ||||
|     return dnsServers; | ||||
|   } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 xiaojunnuo
						xiaojunnuo