后端前端运维MCP 服务【免费下载链接】nginx-uiYet another WebUI for Nginx项目地址https://gitcode.com/gh_mirrors/ngi/nginx-ui点击查看免费下载导读本文围绕 Nginx UI 仓库中 自签名证书 UX 增强实现计划 与其配套设计规格展开深入讲解三项核心改造将「Custom Domains 多行编辑器」抽象为可复用的StringListInput组件并复用到自签名证书的 Domains / IP Addresses 字段在自签名表单顶部增加自动续期策略提示在前端与后端同时强制自签名证书 Name 必填。读完本文你将掌握该功能模块的组件抽象思路、payload 种子与过滤模式、Go 后端binding:required校验契约以及完整的测试与手工冒烟验证方法。背景一次手工测试暴露的三个体验问题自签名证书创建流程合并进「申请证书Issue Certificate」对话框后Nginx UI 在手工测试中发现三个不一致点构成了本次增强的动因编辑器风格不统一。同一对话框里Custom Domains 分支用的是带「Add/Remove」按钮的多行AInput列表而自签名证书的 Domains 与 IP Addresses 用的是 chip 风格的 tags 下拉ASelect modetags。同样的输入语义两种编辑模型。缺少自动续期上下文提示。用户在创建自签名证书时表单内没有任何提示说明 Nginx UI 会自动续期而续期策略取决于全局设置与单张证书的有效期用户无从知晓。空 Name 可静默通过。后端此前对空Name不报错导致证书列表出现空白行且文件系统 slug 只能回退到第一个域名或 IP。对自签名证书而言Name 应当必填。设计文档还明确划定了非目标Non-goals避免改造范围失控不做前端 IP 格式校验后端已通过binding:omitempty,dive,ip兜底不迁移已存在的空 Name 历史数据用户重新打开并保存时新校验自然生效不改名DNSIssueCertificate.vue不改变 ACMEWildcard / Custom Domains提交语义。总体架构与文件地图方案的核心分层非常清晰前端组件复用 三处消费方接线 请求边界校验。新增文件 2 个文件职责app/src/components/StringListInput/StringListInput.vue可复用的多行字符串数组输入Add / Removeapp/src/components/StringListInput/index.ts组件再导出入口修改文件 9 个文件改动app/src/views/certificate/components/DNSIssueCertificate.vueCustom Domains 改用StringListInputsubmitSelfSigned裁剪过滤并校验 Namepayload 种子[]app/src/views/certificate/components/SelfSignedCertFields.vueDomains / IPs 改用StringListInput新增续期AAlert带hideRenewalNote开关Name 必填app/src/views/certificate/components/SelfSignedCertForm.vueemptyForm()种子空行数组submit()裁剪过滤并校验 Nameapp/src/views/certificate/components/SelfSignedCertManagement.vue传hide-renewal-note避免双重提示app/src/views/certificate/CertificateEditor.vuesave()的isSelfSigned分支裁剪过滤并校验 Nameapp/src/api/cert.tstoSelfSignedPayload()种子空行数组api/certificate/self_signed.goSelfSignedCertRequest.Name增加binding:requiredapi/certificate/self_signed_test.go新增空 Name 返回 4xx 的接口测试另有明确不修改的文件app/src/views/site/site_edit/components/Cert/SelfSignedCert.vue它以defaultDomains调用SelfSignedCertForm下游改动自动传播以及 internal/cert/self_signed.go 等后端辅助代码校验统一收敛在请求边界。组件层StringListInput 的设计与实现组件 APIinterface Props { placeholder?: string addButtonText?: string // 默认: $gettext(Add Item) // v1 不提供 validator prop —— YAGNI }v-model的类型是string[]必填。设计上允许数组内含单个空字符串——这是用户在输入第一个值时的正常中间状态消费方负责在提交时过滤空值。组件行为依据 StringListInput.vue 的实现行为可归纳为每个数组元素渲染一个AInput当数组长度大于 1 时每行右侧显示红色链接按钮Remove点击后按索引splice删除该行底部一个 block 级AButton文案由addButtonText控制默认Add Item点击向数组push()追加空行组件不做任何内部校验仅用:placeholder提供输入提示。关键实现细节与计划文档的说明一致仓库代码已落地script setup langts defineProps{ placeholder?: string addButtonText?: string }() const items defineModelstring[]({ required: true }) function addItem() { items.value [...items.value, ] } function removeItem(index: number) { if (items.value.length 1) return const next [...items.value] next.splice(index, 1) items.value next } /script两点值得注意使用defineModelstring[]({ required: true })这是 Vue 3.4 的双向绑定惯用法项目内已有先例删除/追加/更新都通过展开数组生成新引用[...items.value]再写回 model保持响应式身份稳定让v-model干净地 emit——这是相对原先「通过双向绑定直接改customDomains[index]」的显式 setter 化改造。从源码结构看实际仓库中的组件实现StringListInput.vue相比计划草案还增加了:aria-label可访问性标注属于落地时的细节加强。表单层SelfSignedCertFields 的统一改造替换 Domains / IP Addresses 编辑器SelfSignedCertFields.vue 中原先的ASelect modetags被替换为AFormItem :label$gettext(Domains) StringListInput v-modeldata.domains :placeholder$gettext(Enter domain name) :add-button-text$gettext(Add Domain) / /AFormItem AFormItem :label$gettext(IP Addresses) StringListInput v-modeldata.ip_addresses :placeholder$gettext(Enter IP address) :add-button-text$gettext(Add IP Address) / /AFormItemv-model直接绑定到defineModelSelfSignedCertPayload派生出的data与原先的 tags 下拉保持同样的数据契约但交互从「chip 输入」变成「多行可增删输入」与 Custom Domains 分支完全一致。自动续期提示与 hideRenewalNote 开关表单顶部新增AAlert提示由新 prophideRenewalNote?: boolean默认false控制显隐AAlert v-if!props.hideRenewalNote classmb-4 typeinfo show-icon :title$gettext(Nginx UI will automatically renew this certificate as it approaches expiration, based on the global certificate renewal interval and this certificate\s validity period.) /消息文案明确定位了续期策略的两个决定因素全局证书续期间隔与本张证书的有效期。同时Name 字段的AFormItem增加required属性显示红色星号placeholder 从原来的Optional改为Enter certificate name。避免双重提示SelfSignedCertManagement 的接线SelfSignedCertManagement.vue 是「编辑已存在自签名证书」的面板它本身已经有一条typesuccess的提示「This self-signed certificate is managed by Nginx UI and renewed automatically.」。若再叠加SelfSignedCertFields的 info 提示用户会看到两条相邻且语义重复的横幅。因此该处显式传hide-renewal-note关闭新提示SelfSignedCertFields v-modeldata is-key-type-readonly hide-renewal-note /这形成了清晰的提示策略新建场景显示续期策略说明编辑场景由管理面板自身的成功态提示负责。数据流Payload 种子与提交过滤模式StringListInput会在模型数组中保留一个空占位行因此三条写入路径需要做到「一进一出」的对称处理进入编辑态seed数组为空时先填充[]保证编辑器渲染出一个可输入的空行提交前filtertrim()去空白、filter(Boolean)丢弃空串再发送给 API。三个种子工厂计划文档明确要求三个 payload 工厂统一种子策略仓库代码已实现DNSIssueCertificate.vue 的emptySelfSignedPayload()→domains: []、ip_addresses: []SelfSignedCertForm.vue 的emptyForm()→domains: defaultDomains?.length ? [...defaultDomains] : []、ip_addresses: []若外部传入defaultDomains则原样继承否则也种子空行cert.ts 的toSelfSignedPayload(c)→domains: c.domains?.length ? [...c.domains] : []、ip_addresses: c.self_signed_config?.ip_addresses?.length ? [...c.self_signed_config.ip_addresses] : []编辑已有证书时按存量数据决定。统一的提交前校验形状三条提交/保存路径使用完全一致的「裁剪 双重校验」模板const name (payload.name ?? ).trim() const domains payload.domains.map(d d.trim()).filter(Boolean) const ip_addresses payload.ip_addresses.map(s s.trim()).filter(Boolean) if (!name) { message.error($gettext(Please enter a name for the certificate)) return } if (domains.length 0 ip_addresses.length 0) { message.error($gettext(Please enter at least one domain or IP address)) return } await cert.generate_self_signed({ ...payload, name, domains, ip_addresses })应用此模式的三处位置触发时机调用的 APIDNSIssueCertificate.submitSelfSigned()申请证书对话框 → Self-signed → Generatecert.generate_self_signedSelfSignedCertForm.submit()站点编辑器内的自签名快捷创建cert.generate_self_signedCertificateEditor.save()isSelfSigned分支编辑已有自签名证书后保存cert.modify_self_signed校验顺序值得注意先校验 Name再校验域名/IP 至少填一项——这是刻意设计的消息优先级保证用户先收到关于必填 Name 的明确指引。对应 API 封装在 cert.ts 中generate_self_signedPOST 到/self_signed_certmodify_self_signedPOST 到/self_signed_cert/{id}。后端契约binding:required 与接口测试请求结构变更后端校验统一收敛在请求边界。api/certificate/self_signed.go 中SelfSignedCertRequest的完整结构如下Name已带binding:requiredtype SelfSignedCertRequest struct { Name string json:name binding:required Domains []string json:domains binding:omitempty IPAddresses []string json:ip_addresses binding:omitempty,dive,ip KeyType string json:key_type binding:omitempty,auto_cert_key_type ValidityDays int json:validity_days binding:omitempty,min1,max3650 SyncNodeIds []uint64 json:sync_node_ids binding:omitempty }binding:required通过cosy.BindAndValid在GenerateSelfSignedCert与ModifySelfSignedCert两个 handler 中同时生效两者绑定同一个结构体空 Name 会在进入业务逻辑前被拦截。其余字段维持既有约束IP 逐项dive,ip校验、KeyType 走自定义校验器auto_cert_key_type、有效期限制在 1–3650 天。防御性代码保留计划与设计文档都强调GenerateSelfSignedCert中selfSignedSlug的 CommonName 回退逻辑self_signed.go 中 slug 为空时回退到defaultSelfSignedSlug或首个域名/IP保留为防御性代码——虽然对新请求而言空 Name 已被拦截但直接调用selfSignedSlug的内部路径仍受保护。配套的 slug 测试如TestSelfSignedSlugConvertsIDNToPunycode验证中文域名转 punycode、TestSelfSignedSlugSanitizesPathTraversal验证路径穿越清理佐证了这一设计意图。新增接口测试api/certificate/self_signed_test.go 中已落地TestGenerateSelfSignedCertRejectsEmptyName它复用了与 rollback 测试相同的setupSelfSignedAPITest夹具gin TestMode 内存 sqlite query.SetDefault核心断言是body, err : json.Marshal(SelfSignedCertRequest{ Domains: []string{named.example}, KeyType: string(certcrypto.EC256), ValidityDays: 30, }) // POST /self_signed_cert ... if rec.Code 400 || rec.Code 500 { t.Fatalf(status %d, want a 4xx for missing name, rec.Code) } if !strings.Contains(strings.ToLower(rec.Body.String()), name) { t.Fatalf(response body %q did not mention the missing name field, rec.Body.String()) }该测试不要求 4xx 的精确状态码但断言响应体中必须提及name字段确保错误信息对用户可诊断。既有测试TestGenerateSelfSignedCertRollsBackDBOnFileWriteFailure发送的请求自带Name: rollback-test不受新约束影响buildSelfSignedOptions的单元测试走内部函数、不经过 binding 校验同样不受影响——这保证了新增校验不会破坏存量测试。验证体系三层质量门计划文档将验证拆成三层值得作为同类改造的范式参考。前端质量门cd app bun run lint bun run typecheck两命令都必须退出码 0。若perfectionist规则重排了 import 顺序项目 lint 配置的已知行为运行bun run lint:fix后复检即可。计划明确不为该视图新增组件级单测项目此视图没有组件测试先例行为由手动冒烟覆盖。后端质量门go test ./api/certificate/ -run TestGenerateSelfSignedCertRejectsEmptyName -race # 新测试 go test ./api/certificate/ ./internal/cert/ -race # 全量回归格式化方面gofmt是硬性门槛goimports若未安装可忽略。设计文档的完整回归范围是go test ./api/certificate/... ./internal/cert/...。手动冒烟清单计划文档为人工验收列出了 8 项检查覆盖三条入口申请证书对话框、证书编辑器、站点编辑器快捷入口与两种状态新建、编辑申请证书 → Self-signed顶部可见续期提示Name 字段带红色星号、placeholder 为Enter certificate nameDomains / IP Addresses 各渲染为「空行 Add 按钮」的多行编辑器全空点 Generate → toastPlease enter a name for the certificate只填 Name → toastPlease enter at least one domain or IP address填 Name 一个域名 → 证书创建成功、对话框关闭、列表刷新且显示名称打开已有自签名证书 → 仅显示管理面板自身的续期提示不出现第二个提示hide-renewal-note生效编辑已有证书时清空 Name → 保存被拦截直到 Name 补齐站点编辑器的自签名快捷入口 → 续期提示可见、Name 必填Custom Domains 分支 → 外观与行为与改造前一致视觉回归检查。i18n 新增字符串设计文档列出了本次新增的英文源串$gettext提取自 .vue 模板源语言为英文其他语言的 .po 文件在仓库 app/src/language 下同步维护Add Item组件默认按钮文案Add Domain沿用已有Add IP AddressEnter domain name沿用已有Enter IP addressEnter certificate namePlease enter a name for the certificateNginx UI will automatically renew this certificate as it approaches expiration, based on the global certificate renewal interval and this certificates validity period.messages.pot的重新生成属于独立的运维跟进项不在本次改造范围内。落地节奏任务分解与提交规范实现计划将整个改造拆为 6 个代码提交 1 个收尾任务顺序本身包含工程意图——先完成后端契约变更Task 2使后续前端改动始终对着新服务端契约做端到端验证任务内容产物Task 1创建StringListInput组件 re-export新增 2 文件Task 2后端binding:required 先写失败测试改self_signed.go/self_signed_test.goTask 3SelfSignedCertFields接入组件、加续期提示、Name 必填改 1 文件Task 4SelfSignedCertManagement传hide-renewal-note改 1 文件Task 5三个提交/保存路径种子 过滤 校验 Name改 4 文件Task 6Custom Domains 切换到StringListInput纯重构改 1 文件Task 7最终 lint/typecheck/go test 全量 手动冒烟交接无代码产物提交信息遵循项目的 imperative 风格并要求 sign-offCo-Authored-By开发分支为feature/self-signed-certificate计划要求直接在该分支提交、不另开子分支。Task 6 被刻意标记为「纯重构同一 UX、更少重复」放在最后单独提交便于 review 时与行为变更隔离。小结一处抽象、三处接线、两层校验本次增强的精髓可以浓缩为「一处抽象、三处接线、两层校验」一个StringListInput组件统一了 Custom Domains 与自签名 Domains / IP Addresses 的编辑模型三个 payload 工厂对称处理空行种子、三条提交路径统一裁剪过滤Name 必填在前端toast 提示与后端binding:required 接口测试双重落地。改造还通过hideRenewalNote精确控制了提示信息的展示语境避免信息噪音。无论是后续扩展新的多行字符串输入场景还是为其他表单补充请求边界校验这套「组件抽象 种子/过滤对称处理 边界契约测试」的模式都具备直接的可复用价值。赞分享后端前端运维MCP 服务【免费下载链接】nginx-uiYet another WebUI for Nginx项目地址https://gitcode.com/gh_mirrors/ngi/nginx-ui点击查看免费下载相关推荐Nginx Proxy Manager 证书全解析HTTP、DNS 与自定义证书的签发、验证与续期指南Nginx Proxy Manager 证书全解析HTTP、DNS 与自定义证书的签发、验证与续期指南 Nginx Proxy ManagerNPM为托管后端API网关nginx-proxy-manager 证书管理实战HTTP、DNS 与自定义证书的签发与自动续期nginx proxy manager 证书管理实战HTTP、DNS 与自定义证书的签发与自动续期 导读 本文围绕 nginx proxy manager 前后端API网关Sunshine 游戏串流实战指南30 分钟上手客厅大屏畅玩 PC 游戏Sunshine 游戏串流实战指南30 分钟上手客厅大屏畅玩 PC 游戏 游戏 PC 摆在书房你却在客厅想玩。给这台 PC 装上 Sunshine 就行。音视频后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
GD32F103 PWM输出实战:从原理到代码,彻底搞懂定时器PWM 先交代一下,这个系列写到第六篇了。前面几篇我们聊了环境搭建、GPIO点灯、串口打印、中断和定时器,这次终于轮到PWM了。老实说,PWM这东西是单片机入门之后最实用、最能出效果的一个外设,它既是“软件控制硬件”的典型代表… · 2026/9/23 22:24:05
新概念英语第47-48课:咖啡点单与饮食偏好表达 1. 新概念英语第一册第47课《一杯咖啡》深度解析作为一名英语教学从业者,我经常遇到初学者对基础对话场景的困惑。今天我们就来深入拆解新概念英语第一册第47课《A cup of coffee》这个看似简单却蕴含丰富语言点的经典对话。1.1 对话场景与人物关系这段对话发生在两… · 2026/9/23 22:24:05
Hermes ISerialization 深度解析:基于结构化克隆的跨运行时二进制序列化方案 语言运行时编译器移动开发 【免费下载链接】hermes A JavaScript engine optimized for running React Native. 项目地址: https://gitcode.com/gh_mirrors/hermes/hermes 点击查看 免费下载 导读
ISerialization 是 Hermes 为跨运行时传递 JS 值而设计的高效二进… · 2026/9/23 22:23:58
基于UNet与UNet++的细胞医学图像分割Python实现源码 简介:这份源码面向计算机相关专业的毕业设计、课程设计及期末综合作业需求,提供基于UNet与UNet两种编码器-解码器架构的医学细胞图像分割完整实现,采用Python编写,原为本科三年级课程设计,在导师指导下获99分评价&… · 2026/9/24 0:11:22
边缘驱动对流原理与跨学科应用解析 1. 边缘驱动对流(EDC)的核心原理边缘驱动对流(Edge-driven convection,简称EDC)是地球物理学中描述岩石圈-软流圈系统内物质循环的重要机制。其本质是水平方向上的物理性质突变(温度、密度、粘度差异&#… · 2026/9/24 0:11:16
PyTorch从零实现贝叶斯神经网络:不确定性量化实战 简介:本资源是一份面向机器学习进阶学习者与研究者的贝叶斯神经网络实践教程代码包,聚焦于不确定性建模这一核心需求,助力读者掌握小样本学习、模型校准与置信度预测等关键能力。压缩包共12个文件,含6个Python脚本(如b… · 2026/9/24 0:10:51
极化联合特征与机器学习:海杂波中雷达目标检测的Python实现 简介:面向雷达信号处理与海面目标检测研究人员,该PDF复现了《基于极化联合特征的海面目标检测方法》的完整实现。通过Cloude分解提取极化熵与反熵,利用Krogager分解得到球、二面角、螺旋体散射归一化系数,构成5维联合特征… · 2026/9/24 0:10:45
Mosquitto 1.0.2 版本解析:$SYS 持久化缺陷修复与配套工具链改进 后端消息队列消息路由 【免费下载链接】mosquitto Eclipse Mosquitto - An open source MQTT broker 项目地址: https://gitcode.com/gh_mirrors/mos/mosquitto 点击查看 免费下载 本篇技术指南围绕 Eclipse Mosquitto 1.0.2 版本发布公告展开,逐一拆解… · 2026/9/24 0:10:32
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44