不二云端
https://www.2sv.cn/
欢迎来到不二云端博客. 这里记录了从PHP到前端的点点滴滴
-
导入任何Linux系统容器镜像到wsl
https://2sv.cn/archives/279/
2025-08-10T12:11:04+08:00
AI摘要:WSL支持将任何Linux容器镜像导入为子系统,适用于需要特定发行版或版本的用户。以Ubuntu 24.04为例,通过Docker拉取并导出容器镜像为tar文件,随后在Windows上使用PowerShell导入并创建新的WSL子系统。用户可以进一步设置该子系统的默认登录用户。Powered by AISummary.除了Microsoft Store中含有的Linux子系统镜像、Microsoft官方提供的Linux子系统镜像https://learn.microsoft.com/en-us/windows/wsl/install-manual以及他人制作的Linux子系统镜像可以创建为Wsl子系统外,WSL还支持将任意Linux容器镜像导入为Wsl子系统,这对于想要拥有某个特定发行版、特定版本的子系统的人说是一个福音。以Ubuntu24.04为例,由于Ubuntu24.04刚发行不久,此时Microsoft官方还没有提供该发行版,但DockerHub上已经存在Ubuntu24.04的容器镜像。按照下面的步骤,可以完成Ubuntu2404子系统的创建。前提条件:已经拥有一个docker(或者podman)并已启动windows上已经安装好wsl步骤1:通过docker拉取ubuntu2404容器镜像,并将其导出为tar归档文件(ubuntu2404.tar)sudo docker pull ubuntu:24.04
sudo docker run ubuntu:24.04
container_id=$(sudo docker container ls -a | grep -i 'ubuntu:24.04' | awk '{print $1}')
sudo docker export $container_id > ubuntu2404.tar步骤2:将导出的ubuntu2404.tar文件复制到windows上,假设存放路径为V:\wsl\ubuntu2404.tar步骤3:Windows上打开PowerShell,将ubuntu2404.tar文件导入到wsl# 假设想要将导入后创建的Ubuntu2404子系统保存在目录V:\wsl\Ubuntu24-04_v1中
# 并且该子系统的名称为Ubuntu2404
wsl --import Ubuntu2404 V:\wsl\Ubuntu24-04_v1 V:\wsl\ubuntu2404.tar现在,Ubuntu2404子系统已经创建好了(可通过wsl -l -v命令查看),已经可以进入该子系统并设置该子系统,比如设置该子系统的默认登录用户。# 登录子系统
wsl -d Ubuntu2404
# 进入子系统后,创建新用户(用户名longshuai,uid/gid为1200)并修改密码,稍后设置为该子系统的默认登录用户
# 顺便把root用户的密码也修改
root@DESKTOP:$ useradd -u 1200 -s /bin/bash -m -d /home/longshuai longshuai
root@DESKTOP:$ passwd
root@DESKTOP:$ passwd longshuai
# 如果通过下面的方式设置默认的登录用户无效,可使用工具LxRunOffline来设置
root@DESKTOP:$ echo -e "[user]\ndefault=longshuai" >> /etc/wsl.conf
root@DESKTOP:$ exit
-
WordPress 存储库的替代品来了:FAIR 包管理器——它到底是什么?
https://2sv.cn/archives/278/
2025-08-05T16:31:08+08:00
AI摘要:FAIR包管理器是由WordPress长期贡献者发起、Linux基金会支持的一个独立、去中心化的插件和主题存储库,旨在应对WordPress.org控制问题。FAIR允许开发者创建安全的插件分发平台,并为企业提供供应链安全和合规性优势。虽然其技术挑战较大,尤其是在安全性和操作复杂性方面,WordPress创始人Matt Mullenweg对其持谨慎态度,但FAIR的出现标志着WordPress治理模式的潜在转变。Powered by AISummary.最大的公告来自对 WordPress.org 主题和插件生态系统的外部威胁。这个威胁就是 FAIR 软件包管理器——一个由 WordPress 长期贡献者发起、Linux 基金会支持的项目。那么,让我们来聊聊 FAIR 究竟是什么,它目前的样子,以及更重要的是,你应该使用它吗?最后,我们再来看看 WordPress 联合创始人 Matt Mullenweg 对此的看法。FAIR 的起源和目的FAIR 这个缩写并非巧合,它的完整名称——“可信插件和主题的联合独立存储库”——也并非巧合。它是对去年 #wpdrama事件后果的直接回应,尤其是 10 月份Automattic 在生态系统中控制了 WP Engine 的 ACF 插件 slug事件。正如企业机构 Crowd Favorite 首席执行官兼该项目发起人之一 Karim Marucchi 所解释的那样:我清楚地记得那些电话。来自不同大企业的多位首席法律顾问在电话里直截了当地问我:‘Karim,如果一个人可以单方面做出危及我们供应链的改变,而且没有任何明显的制衡措施,我们为什么要信任 WordPress?’ ”卡里姆并非唯一一个听到有人担忧开源 WordPress 项目“治理问题”的人。还有很多人呼吁建立一个公平、不集权的替代方案。因此,FAIR 诞生了——联合且独立——或者换句话说,非集中式。该项目历经六个月的合作,由来自十多个组织的一百多位贡献者共同完成。为了确保治理的中立性,该团队与Linux基金会进行了接洽。该组织也负责监管Linux和Kubernetes等重要的开源项目。此外,三位德高望重的WordPress资深人士被任命领导技术指导委员会:Carrie Dils、Mika Epstein和Ryan McCue(WordPress REST API的创建者)。如果 FAIR 最终被广泛采用,它可能会挑战 Automattic 首席执行官 Matt Mullenweg 自 WordPress 创立以来对它的控制。因此,WordPress 社区的许多人将此视为 WordPress 历史时间线上的一个潜在分叉。尽管如此——这一点需要强调——从技术角度来看,FAIR 并非 WordPress 的分支。它仍然是 100% 的 WordPress。它只是消除了对 WordPress.org 插件和主题的依赖。FAIR 的意义远不止于此,但这才是重点。WordPress 切换到 FAIR 的两个选项截至撰写本文时,WordPress 可通过两种方式使用 FAIR:作为插件作为一个完整的 WordPress 安装,捆绑了 FAIR 插件(预安装)WordPress 完整安装主要面向为新客户提供 WordPress 安装选项的托管公司。大多数现有 WordPress 网站的用户可以像其他插件一样直接下载并安装该插件。使用 FAIR要尝试 FAIR 插件,您需要做的第一件事就是前往其GitHub 发布页面并下载它:然后,在 WordPress 仪表盘中,前往插件→添加插件→选择文件。在硬盘上找到fair-plugin-0.2.0.zip (请直接使用最新版本)。然后点击立即安装,然后点击激活插件:成功激活插件后,您会注意到左侧边栏中有一个新的 FAIR 设置菜单选项:目前实际上只有一个功能——可以覆盖强制使用 Gravatar设置用户头像的默认设置。该功能默认启用(标题为“FAIR Avatars”)。但是,如果您出于某种原因更喜欢使用 Gravatar,可以随时恢复使用。除此之外,您还会注意到屏幕右下角有一条微妙但重要的信息:Updates served from the FAIR Package Manager and AspirePress.相同的消息将显示在 WordPress 管理仪表板的其他区域 – 例如插件和主题页面。这只是让您知道 FAIR 正在运行,并且您主题和插件的任何未来更新都不会来自 WordPress.org。而是来自 FAIR 和 AspirePress。如果你好奇的话,AspirePress 是另一个与 FAIR 有着相似目标和价值观的开源项目。根据一位直接参与 FAIR 构建的人在 Reddit 上的帖子,Aspire 团队也参与了这个过程。在 FAIR 发布他们的插件的第二天,Aspire 也发布了他们自己的类似插件。所以看起来这两个项目正在协同工作。有什么激励?如果你不是开发人员或 WordPress 高级用户,你可能会想——我为什么要安装这个?这是一个合理的问题。对于普通用户来说,选择采用 FAIR 更多的是出于意识形态而非实用性考虑。该插件不会显著改变您日常使用 WordPress 的方式。但是,如果您关心 WordPress,并且相信转向更去中心化的系统是有益的,那么安装该插件来表达您的支持并不会损害您的网站。对于开发者来说,激励机制会更加强大,因为它们会以积极的方式影响插件的分发。这是因为 FAIR 允许开发者将免费版和付费版插件捆绑到一个加密签名的软件包中。这可以简化用户体验,并创造新的商业模式。对于企业和托管公司而言,FAIR 解决了供应链安全、法规遵从性和风险管理等关键业务问题:组织可以在防火墙后运行 FAIR,从而完全控制可访问的插件和主题。FAIR 与 GDPR 以及《网络弹性法案》等即将出台的法规更加一致。它减少了关键业务基础设施中的单点故障。该系统还引入了代码签名和改进的加密安全措施。这些都是企业客户一直要求的附加功能。这一切听起来很美好,但也有一些反面观点需要考虑。WordPress 联合创始人马特·穆伦维格 (Matt Mullenweg) 在 WordCamp Europe 的演讲中迅速指出了这些反面观点。Matt 对 FAIR 的反应FAIR 发布仅几小时后,Matt Mullenweg 与 WordPress 执行董事 Mary Hubbard 进行了“炉边谈话”,并直接被问及与该项目的潜在合作。他的回答既展现了外交上的开放态度,也体现了重大的技术担忧。“当然,我们会考虑所有因素,”穆伦维格说,“但即使如你所说,我认为也存在很多挑战。”他主要关注的几个关键领域:安全性:虽然 FAIR 旨在提高安全性,但穆伦维格认为这可能会带来新的漏洞。“目前,供应链攻击需要攻破 WordPress.org,而该网站从未被黑客入侵过,”他指出。“但现在突然之间,就有 N 个地方可能被攻破。”运营复杂性:他强调的挑战包括多个镜像可能存在正常运行时间问题、难以实施分阶段推出(例如首先与 5% 的用户测试更新)以及失去为 PHP 版本和数据库支持决策提供信息的集中式分析。信任和质量控制:Mullenweg 认为,用户不一定要求提供更多的下载位置,而是要求提供信任指标:“我怎么知道这是值得信赖的?我怎么知道这些评论是真实的?谁在审核?谁在检查这些不同评论的 IP?插件评级是多少?兼容性如何?”执行问题:他质疑如何在分布式系统中执行现有政策,例如对管理员横幅的限制。尽管如此,穆伦维格也承认了积极的一面:“我认为大家能够互相传递代码,而不是仅仅争论、聊天或写博客文章,这真是太棒了。”他进一步强调,在做出任何合作承诺之前,他希望先审查代码,并仔细考虑了该项目可能采取的方向。在我看来,考虑到他处理公告的时间很短,我觉得这是一个很好的回应。他提出的观点也很合理——虽然是双关语。更大的图景FAIR 项目是自该平台创建以来对 WordPress 基础设施进行去中心化最重要的尝试,其影响可能是深远的。它能否获得广泛采用还有待观察。然而,它的存在本身——由Linux基金会支持,并由受人尊敬的社区人物开发——标志着生态系统对治理、控制以及支撑43.5%网络的平台未来发展的看法发生了转变。Mullenweg 提出的技术挑战也是切实存在的,并非仅仅是一些转折点。他们需要解决方案。另一方面,促使 FAIR 成立的治理和供应链安全问题也同样重要。这些相互竞争的优先事项如何相互影响,很可能决定 WordPress 未来十年的未来。你觉得 FAIR 怎么样?你会在你的 WordPress 网站上安装这个插件吗?
-
什么是 LLMs.txt?它对SEO有用吗?WordPress 网站如何生成 LLMs.txt?
https://2sv.cn/archives/277/
2025-08-05T16:27:58+08:00
AI摘要:LLMs.txt 是一种专为大型语言模型(LLMs)设计的网站文件,通过提供简化和结构化的内容,帮助 AI 更高效地理解和索引网站信息。虽然它对传统 SEO 无直接影响,但能提升网站在 AI 搜索中的表现,优化 AI 问答的准确性。WordPress 网站可以通过插件轻松生成 LLMs.txt 文件,以支持 AI 模型更好地解析内容。Powered by AISummary.大型语言模型 (LLM)越来越依赖网站信息,但面临一个关键的限制:上下文窗口太小,无法完整处理大多数网站。将包含导航、广告和 JavaScript 的复杂 HTML 页面转换为 LLM 友好的纯文本既困难又不精确。虽然网站既服务于人类读者,也服务于大型语言模型 (LLM),但后者受益于更简洁、更专业的信息,这些信息集中在一个易于访问的单一位置。这对于开发环境等用例尤为重要,因为LLM 需要快速访问编程文档和 API。为了解决以上问题,LLMs.txt 应运而生!什么是 LLMs.txt?LLMs.txt 是一种专为大型语言模型(LLMs)设计的网站标准化文件,旨在帮助 AI 更高效、准确地理解和索引网站内容。其核心特点与作用如下:一、本质与定位AI 专用索引文件类似 sitemap.xml 的站点地图功能,但专注于服务 AI 模型,而非搜索引擎爬虫。它提供网站内容的精简结构化索引,通常以 Markdown 格式存储于网站根目录(如 https://example.com/llms.txt)。与传统文件的区别VS robots.txt:后者控制爬虫访问权限,而 LLMs.txt 提供内容导航与语义说明,不限制访问。VS sitemap.xml:LLMs.txt 包含内容摘要、分类标签及更新状态等上下文信息,而非仅页面链接。二、核心功能提升 AI 理解效率通过剥离网页中的 HTML/CSS/JavaScript 等视觉渲染元素,直接提供纯文本核心内容,减少信息噪音与 Token 消耗,加速 AI 处理响应。结构化内容导航为 LLM 标注关键页面属性(如用途、分类、重要度),引导模型优先抓取高价值内容(如 API 文档、最新公告)。支持 Markdown 版本访问网站可为每个页面提供 Markdown 版本(例如在原始 URL 后追加 .md),供 AI 直接解析纯净文本。三、典型应用场景版权与合规声明:部分实现方案允许声明内容使用权限,规范 AI 模型的数据抓取行为AI 问答优化:当用户向 ChatGPT、Claude 等询问网站信息时,LLMs.txt 帮助模型快速定位准确答案,避免因网页噪音导致错误。开发者文档适配:如 Apifox、VitePress 等技术平台通过集成 LLMs.txt,显著提升 AI 对 API 文档的解析精度。LLMs.txt 对SEO有用吗?LLMs.txt 对 传统搜索引擎优化(SEO)没有直接影响,但可能通过间接方式影响网站在 AI 搜索环境中的表现,具体分析如下:一、对传统 SEO 无直接作用谷歌官方明确否定其 SEO 价值谷歌搜索倡导者 John Mueller 将 LLMs.txt 类比为已废弃的“关键词元标签”(keywords meta tag),强调其不被搜索引擎用作排名信号,且存在滥用风险。与搜索引擎爬虫无关LLMs.txt 专为大型语言模型(如 ChatGPT、Gemini)设计,不控制搜索引擎爬虫的访问权限或索引行为,传统 SEO 规则(如关键词密度、反向链接)仍独立生效。二、潜在间接影响与 AI 场景价值提升 AI 搜索结果可见性通过为 LLM 提供结构化内容摘要,可优化网站在 AI 问答中的引用准确性(例如 ChatGPT 回答用户问题时更易调取正确信息),间接扩大内容触达范围。降低 AI 误解风险剥离广告、导航等噪音的 Markdown 格式能避免 AI 因抓取冗余内容导致错误解读,尤其对技术文档(如 API 说明)、实时资讯类站点效果显著。优化 AI 索引效率标注页面优先级与分类标签(如 #安全 #必备),可引导 LLM 优先抓取高价值内容,突破上下文长度限制,尤其利于大型网站。三、实操建议无需替代传统 SEO 工作:LLMs.txt 应视为补充而非替代,需同步维护 robots.txt 和 sitemap.xml。技术文档站点建议部署:API 服务商(如 Apifox)、开源项目(如 Vue/Vite)可通过 LLMs.txt 显著提升 AI 助手解析效率。避免滥用风险:堆砌无关关键词或操纵 AI 行为可能降低内容可信度,需遵循简洁、真实原则。总结:LLMs.txt 当前对 Google 等传统搜索引擎的 SEO 排名无影响,但能增强内容在 AI 生态中的可见性与准确性,是面向未来搜索场景的策略性补充。WordPress 网站如何生成 LLMs.txt?WordPress 网站要生成 LLMs.txt 非常简单,只需要安装相关的插件即可,比如 Website LLMs.txt 或 LLMs.txt and LLMs-Full.txt Generator 都是不错的插件,二选一安装使用即可,如果你有使用 Yoast SEO 插件,貌似它已经集成了这个功能,就不需要借助其他插件了。
-
如何修复 WordPress 错误:Non-existent changeset UUID
https://2sv.cn/archives/276/
2025-08-05T16:26:02+08:00
AI摘要:遇到WordPress错误“Non-existent changeset UUID”时,问题通常出在“设置 – 常规”中两个网址不一致。解决方法是将这两个网址修改为一致,即可修复该错误。Powered by AISummary.访问 外观 – 自定义 界面的时候,看到错误提示 Non-existent changeset UUID,如下图所示:说真的,倡萌也是第一次看到这个错误,搜索了一下,原来问题出在 设置 – 常规 中设置的两个网址不一致导致的:解决办法就是将这两个网址修改为一样的就好了!
-
HTTP常见的状态码详解
https://2sv.cn/archives/274/
2025-08-01T17:44:46+08:00
AI摘要:HTTP状态码分为五类:1xx(提示信息)、2xx(成功)、3xx(重定向)、4xx(客户端错误)、5xx(服务器错误)。每类状态码代表不同的请求处理结果。例如,200表示成功处理,404表示资源未找到,500表示服务器内部错误。状态码帮助客户端和服务器沟通,指示请求的处理状态以及后续操作。Powered by AISummary.状态码目前 RFC 标准里规定的状态码是三位数,所以取值范围就是从 000 到 999。但如果把代码简单地从 000 开始顺序编下去就显得有点太“low”,不灵活、不利于扩展,所以状态码也被设计成有一定的格式。RFC 标准把状态码分成了五类,用数字的第一位表示分类,而 0~99 不用,这样状态码的实际可用范围就大大缩小了,由 000~999 变成了 100~599。这五类的具体含义是:1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
2××:成功,报文已经收到并被正确处理;
3××:重定向,资源位置发生变动,需要客户端重新发送请求;
4××:客户端错误,请求报文有误,服务器无法处理;
5××:服务器错误,服务器在处理请求时内部发生了错误。在 HTTP 协议中,正确理解和应用状态码并非客户端或服务器一方的单方面责任,而是双方共同的责任。客户端作为请求的发起者,接收服务器返回的响应报文后,需要通过状态码来判断请求是否被正确处理。如果请求成功,客户端可以继续后续操作;如果请求失败,则需要根据状态码的具体含义,判断是否需要再次发送请求,或进行相应的错误处理。例如,如果状态码为 404,则表示请求的资源不存在,客户端需要重新检查请求地址或采取其他措施。服务器端作为请求的接收者,也应该合理运用状态码,向客户端清晰地传达处理结果,并指示客户端下一步应该采取的行动。在处理请求时,服务器应该选择最恰当的状态码来回复客户端,避免使用过于笼统的状态码,例如 400 或 500,这些状态码的含义过于模糊,无法为客户端提供足够的信息。1xx类1xx 类状态码属于提示信息,协议处理的中间状态101(切换协议):请求者已要求服务器切换协议,服务器已确认并准备切换。它的意思是客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发生状态码 101,但这之后的数据传输就不会再使用 HTTP 了。2xx类2xx类表示服务器成功处理了客户端的请求200(成功) : 最常见的成功状态码,表示一切正常,服务器如客户端所期待的返回了处理结果,如果不是 HEAD 请求,通常在响应头后都会有 body 数据
201(已创建):请求成功并且服务器创建了新的资源
202(已接受):服务器已接受请求,但尚未处理
203(非授权信息):服务器已经成功处理了请求,但返回的信息可能来自于另一个来源
204(无内容):另一个常见的成功状态码,服务器成功处理了请求,但没有返回任何内容,也就是响应头后没有 body 数据
206(部分内容):服务器成功处理了部分 GET 请求,服务器返回的 body 数据是资源的一部分3xx类3xx类 状态码表示客户端请求的资源的路径发生了转变,需要客户端用新的 URL 来获取资源,也就是重定向301(永久重定向):请求的资源已经永久移动到了新的位置。值得注意的是严格来讲这种重定向并不是服务器跳转,而是客户端跳转,“跳”的动作实际上客户端根据服务器返回的状态码 301 后完成跳转的
302(临时重定向):说明请求路径的资源还在,但暂时需要另一个 URL 来访问
304(未修改):说明自从上次请求后,请求的网页未修改过,服务器返回此响应时,不会返回网页内容,而是重定向到客户端本地浏览器的缓存资源中重定向301 和 302 的区别?重定向301是永久重定向,客户端收到301状态码后,会记忆重定向后的新URL,这样下一次访问的时候,就不会再访问旧的URL,而是直接访问新的URL重定向302是临时重定向,客户端收到302状态码后,不会记录重定向后的新URL,这样下一次访问的时候,还需要访问旧的URL,再重定向到新URL4xx类4xx类 转态码表示客户端发送的请求报文错误400(错误请求):表示请求报文错误,但具体是数据格式错误、请求头缺少还是 URI 长度过大没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”
401(未授权):请求要求身份验证,对于需要登录的网页,服务器可能返回此响应
403(禁止):服务器拒绝请求,没有权限去访问服务器的资源或者请求的 IP 地址被封掉了
404(未找到)代表服务器上没有该资源,或者说服务器找不到客户端请求的资源,是最常见的请求错误码5xx类5xx类 状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码500(服务器内部错误):代表服务器程序错误,也就是说请求的网页程序本身报错了。在服务器端的网页程序出错。由于现在的浏览器都会对 500 做一定的处理,所以在一般情况下会返回一个定制的错误页面
501(尚未实施):服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码
502(错误网关):通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误
503(服务不可用):表示服务器当前无法使用(超载或者停机维护),我们上网时遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503
504(网关超时):服务器作为网关或者代理,但是没有及时从上游服务器收到请求。
-
支持MySQL、PostgreSQL和Redis集群部署,1Panel开源面板v2.0.5版本发布
https://2sv.cn/archives/273/
2025-08-01T17:26:23+08:00
AI摘要:1Panel v2.0.5版本发布,新增MySQL、PostgreSQL和Redis集群部署功能,支持一键部署和可视化操作,降低技术门槛。此外,增加了邮件告警功能,实现短信和邮件双通道告警,提升告警的覆盖范围。版本还支持主从节点灵活切换,确保系统高可用性和简化维护流程。Powered by AISummary.2025年7月24日,现代化、开源的Linux服务器运维管理面板1Panel正式发布v2.0.5版本。在这一版本中,1Panel新增数据库集群部署、邮件告警和主从节点灵活切换三项功能,聚焦为企业级运维场景提供更优使用体验。1Panel v2.0.5版本是1Panel开源面板项目在交付企业级集群化和高可用能力方面迈出的关键一步,也是对社区用户长期支持与反馈的回应。从单机到多节点,从单实例数据库到数据库集群,从单一告警到邮件短信双通道,1Panel正在努力成为“可用、好用、值得信赖”的下一代Linux服务器运维面板。■ 新增MySQL、PostgreSQL和Redis集群部署功能数据库是数字化业务的核心。针对用户的集群化部署需求,1Panel v2.0.5版本在原有的数据库一键部署的基础之上,新增了MySQL、PostgreSQL和Redis的集群部署功能。MySQL、PostgreSQL和Redis集群部署的功能优势主要体现在以下三方面:一键部署MySQL主从集群,轻松实现读写分离;快速搭建PostgreSQL集群,支持事务一致性;使用Redis Cluster,满足高并发和大数据量缓存场景需要。更加重要的是,无需借助繁琐的命令行,用户仍然可以通过可视化的“傻瓜式”操作来进行MySQL、PostgreSQL和Redis的集群安装部署,从而大幅降低搭建高可用数据库集群的技术门槛。■ 新增邮件告警,关键事件不错过在系统运行中,可靠的告警机制对及时发现系统风险尤为关键。1Panel v2.0.5版本在原有短信告警的基础上,新增了邮件告警功能。现在,1Panel能够同时通过邮件和短信双通道,将告警信息第一时间推送给系统运维人员。相比单一通道,双通道告警能够覆盖更多场景,保障告警信息触达用户的即时性,为用户构建更为完善的监控闭环。■ 支持主从节点灵活切换在分布式和多节点管理场景下,如何在主节点发生故障时快速切换节点,保障业务的正常可用,一直是高可用系统运维的核心。本次更新后,1Panel正式支持主从节点切换模式。运维人员可以根据需要,将某个从节点升级为主节点,或将主节点降级为从节点重新加入集群。主从节点切换为用户带来的价值包括:紧急情况下从节点可以快速接管,保证业务持续可用;主从节点切换操作简单快捷,有效降低系统维护成本;角色信息清晰,管理流程更加可控。
-
MaxKB+MinerU:通过API实现PDF文档解析并存储至知识库
https://2sv.cn/archives/272/
2025-08-01T17:22:33+08:00
AI摘要:MinerU v2.0是一款开源的高性能PDF文档解析工具,支持将PDF转换为Markdown和JSON格式,并提升了复杂图表的解析效果。通过与MaxKB结合,用户可以实现自动化文档解析与上传,构建高效的知识库。MinerU提供API接口,支持将PDF文档解析、下载和上传到MaxKB,增强知识库问答系统的性能。文章详细介绍了如何通过API集成MinerU与MaxKB,构建完整的解析与存储流程。Powered by AISummary.MinerU是一款开源的高质量数据提取工具,能够将PDF文档转换为Markdown和JSON格式。2025年6月13日,MinerU发布了v2.0版本,相较于v1.0版本实现了架构和功能的全面重构与升级。在优化代码结构和交互方式的同时,v2.0版本还集成了小参数量、高性能多模态文档解析模型,能够实现端到端的高速、高精度文档理解。实际测试表明,新版本对复杂图表的解析效果较上一版本有明显提升,目前已经能够满足90%以上的复杂文档解析需求。值得一提的是,MinerU出色的PDF文档解析能力特别适合与MaxKB开源项目配合使用。通过"MinerU+MaxKB"的组合方案,用户不仅能够获得高质量的文档解析效果,还能显著提升知识库问答系统的性能。为方便用户集成,MinerU项目现已提供API对接服务(https://mineru.net/apiManage)。接下来,本文将详细介绍如何通过MinerU在线API实现与MaxKB的对接。一、实现方法当用户提供文件地址后,系统会将该地址赋值给file_url变量,并作为参数传递给MinerU文件解析服务。MinerU在完成文件解析后,会返回一个任务ID(task_id)。系统会将其传入MinerU的查询接口,当检测到任务处理完成时,自动获取结果文件的下载链接(full_url)。随后,系统执行文件下载操作,将结果文件保存到MaxKB容器的/opt/maxkb/download目录下。最后,系统会自动完成文件上传和智能分段处理,将内容存储到知识库中。二、MaxKB函数创建我们需要在MaxKB的函数库中创建四个核心功能函数,其用途分别为:调用MinerU单个文件解析;从MinerU获取任务结果;通过URL链接下载文件至服务器;将解析后的ZIP文件上传至知识库。相关的代码说明如下:■ MinerU单个文件解析函数:负责调用MinerU的单文件解析服务,通过传入PDF文档的在线地址来创建解析任务,并返回对应的task_id;import requests
def create_task(file_url):
url = 'https://mineru.net/api/v4/extract/task'
token = '自己申请的 Token'
header = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
data = {
'url': file_url,
'is_ocr': True, #是否启动 ocr 功能,默认 false
'enable_formula': True, #是否开启公式识别,默认 true
'enable_table': True, #是否开启表格识别,默认 true
'language': "ch", #指定文档语言,默认 ch,可以设置为auto
'model_version': "v2", #mineru模型版本,两个选项:v1、v2,默认v1。
}
res = requests.post(url,headers=header,json=data,timeout=5)
res_data = res.json()
task_id_data = res_data["data"]["task_id"]
return task_id_data■ MinerU获取任务结果函数:用于查询任务状态,通过传入task_id获取解析结果,成功后将返回ZIP格式解析文件的下载地址;import time
import requests
def querybyid(task_id,max_retries=100,retry_interval=5):
url = f'https://mineru.net/api/v4/extract/task/{task_id}'
token = '申请的Token'
header = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
retries = 0
while retries < max_retries:
try:
res = requests.get(url, headers=header, timeout=5)
res.raise_for_status() # 检查请求是否成功
data = res.json()
if "data" in data and "full_zip_url" in data["data"] and data["data"]["full_zip_url"]:
return data["data"]["full_zip_url"]
else:
print(f"full_zip_url 为空,正在等待任务完成。已重试 {retries + 1} 次,共 {max_retries} 次。")
time.sleep(retry_interval)
retries += 1
except requests.exceptions.RequestException as e:
print(f"请求失败,错误信息:{e}。正在重试...")
time.sleep(retry_interval)
retries += 1
raise Exception(f"在 {max_retries} 次重试后,仍未获取到有效的 full_zip_url。")■ 文件下载函数:根据提供的ZIP文件下载链接,将文件保存至容器内的/opt/maxkb/download目录。需要注意的是,MaxKB默认使用sandbox用户运行,需确保该用户对/opt/maxkb/download目录有读写权限;import os
import requests
from urllib.parse import urlparse
def download_file(download_url, save_dir='/opt/maxkb/download'):
os.makedirs(save_dir, exist_ok=True)
# 获取文件名
parsed_url = urlparse(download_url)
filename = os.path.basename(parsed_url.path)
save_path = os.path.join(save_dir, filename) # 文件下载后保存的目录,需要默认用户对此目录有读写权限
# 下载文件
try:
response = requests.get(download_url, stream=True)
response.raise_for_status() # 检查请求是否成功
total_size = int(response.headers.get('content-length', 0))
block_size = 1024 # 1KB
progress = 0
print(f"开始下载 {filename} 到 {save_dir}")
with open(save_path, 'wb') as f:
for data in response.iter_content(block_size):
f.write(data)
progress += len(data)
# 打印下载进度
print(f"下载进度: {progress / total_size * 100:.2f}%", end='\r')
print(f"\n下载完成: {save_path}")
return save_path
except requests.exceptions.RequestException as e:
print(f"下载失败: {e}")
return None■ ZIP文件上传至知识库:通过MaxKB API将服务器上的ZIP解析文件上传至知识库存储。import json
import logging
import requests
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def initialize(file_path):
config = {
# MaxKB API密钥
'authorization_apikey': 'user-ac86ec515de17969f2f8a9c8ab21e52f',
# 文件分段处理的API地址
'split_url': 'http://10.1.11.58:8080/api/dataset/document/split',
# 目标知识库的API地址
'upload_url': 'http://10.1.11.58:8080/api/dataset/3d1d5d4e-5576-11f0-bc5c-0242ac120003/document/_bach',
'file_path': rf'{file_path}',
'file_name': '函数库上传文档分段'
}
return config
def upload_file(config):
headers = {
'accept': 'application/json',
'AUTHORIZATION': f'{config["authorization_apikey"]}'
}
try:
files = {'file': open(config["file_path"], 'rb')}
response = requests.post(config["split_url"], headers=headers, files=files)
response.raise_for_status()
response_data = response.json()
map_content = {}
for item in response_data.get("data", []):
for content_item in item.get("content", []):
map_content[content_item.get("title", "")] = content_item.get("content", "")
return map_content
except requests.exceptions.RequestException as e:
logging.error(f"文件分段上传失败: {e}")
return {}
except Exception as e:
logging.error(f"处理文件内容时出错: {e}")
return {}
def send_post_request(config, map_content):
headers = {
"Content-Type": "application/json",
"Authorization": f'{config["authorization_apikey"]}'
}
paragraphs = [{"title": key, "content": value} for key, value in map_content.items()]
document_wrapper = {
"name": config["file_name"],
"paragraphs": paragraphs
}
json_body = json.dumps([document_wrapper])
try:
response = requests.post(config["upload_url"], headers=headers, data=json_body)
response.raise_for_status()
logging.info(f"上传文件响应: {response.text}")
return True
except requests.exceptions.RequestException as e:
logging.error(f"上传文件失败: {e}")
return False
def main(file_path):
config = initialize(file_path)
map_content = upload_file(config)
if not map_content:
logging.error("文件分段上传失败或内容为空,程序终止")
return False
if not send_post_request(config, map_content):
logging.error("文件上传失败,程序终止")
return False
return "文件已上传成功,并保持在知识库中"三、在MaxKB中创建应用在上述四个函数创建完成后,我们可以在MaxKB中尝试创建高级应用。输入或提取上传文件的链接后,按照前文顺序依次添加MinerU单个文件解析函数节点→从MinerU获取任务结果函数节点→下载文件函数节点→文件上传函数节点。小助手提示“文件上传成功”,即可回到知识库页面,在目标知识库中看到新上传的文档。总结来说,MinerU v2.0是一款开源、高性能的PDF文档解析工具,具备强大的多模态处理能力。通过MaxKB与MinerU的深度联动,可以基于函数调用构建清晰高效的 “文件地址→解析→下载→上传” 自动化流程,无缝衔接原始文档与结构化知识库的构建。“MinerU+MaxKB”的组合方案,不仅可以显著提升文档解析的精度与效率,更能大幅增强知识库问答系统的能力与效果。
-
腾讯网页视频下载mp4格式到本地
https://2sv.cn/archives/271/
2025-07-31T17:50:37+08:00
AI摘要:这篇文章介绍了如何下载腾讯视频并将其保存为MP4格式,以便在PPT中使用。方法是通过创建一个HTML文件,粘贴特定代码,并按照提示操作。用户需要从视频中提取“vid”并输入代码框,点击“获取下载地址”按钮获取视频下载链接,之后可以通过浏览器下载视频到本地。该方法经过测试,仅适用于腾讯视频。Powered by AISummary.今天我要从腾讯网上弄个视频,放在 PPT 中讲课使用,我最先想到的方式是把链接放 PPT 文档中,讲课时点击链接打开即可,可我在试讲的时候,发现每次点击链接打开视频都要播放一段 30 秒的广告,不是会员不能跳过,然后页面还充斥着很多广告;其次,如果到时候讲课的环境没有网络的话,不就无法演示了吗?想到这里,我觉得还是要把视频下载下来比较好,放在本地,可以直接插入到 PPT 中,然后我就想怎么才能下载下来,首先就是在页面鼠标右键,去看页面代码查找,发现没找到;然后就是去找猴油脚本,又遇到墙的问题;最后不得已下载了腾讯的客户端,一顿操作终于下载了,然后又卸载了腾讯视频客户端。为了一个视频折腾一顿,真心没必要,有没有简单好用的方法呢?经过多次测试比较,我发现了这个,还是很实用的,后面我打算做个视频教程。使用方法在电脑本地建立一个 HTML 格式文件,复制下面代码到文件里,双击打开文件,按页面提示操作即可。<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta author="kevy tian">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<title> 腾讯视频下载 </title>
<style>
#res {
display: inline-block;
width: 500px;
height: 30px;
font-size: 14px;
line-height: 30px;
padding: 0;
}
.container {padding-top: 50px;}
#isVid {
display: inline-block;
width: 120px;
height: 30px;
font-size: 14px;
line-height: 30px;
}
#download {
display: inline-block;
width: 100px;
height: 30px;
font-size: 14px;
line-height: 25px;
text-align: center;
padding: 0;
margin-bottom: 4px;
}
</style>
</head>
<body>
<div class="container">
<p> 首先在视频上 <b> 点击播放视频 --> 鼠标右键 --> 视频信息 --> 复制 --> 粘贴到文本编辑器 --> 复制 vid 前半部分 </b>,然后将复制的 vid 粘贴到下方输入框:</p>
<div>
<select id="isVid" disabled>
<option style="display:none" value="1"> 视频页面地址 </option>
<option value="2" selected> 视频 vid</option>
</select>
<input id="res" type="text" placeholder="请输入视频 vid"/>
<button id="download" class="btn btn-large btn-primary" type="button"> 获取下载地址 </button>
</div>
<p style="width:100%;padding:20px;word-wrap:break-word;" id="result"></p>
</div>
<script type="text/javascript">
var downloadUrl = "";
var link = "";
var isVid = "";
var vids = "";
$("#download").click(function () {link = $("#res").val();
isVid = $("#isVid").val();
if (isVid == "1") {if (checkUrl(link)) {vids = getVids(link);
process()}
else {alert("视频链接不正确");
}
}
else {
vids = link;
if (vids != "") {process();
}
}
});
function checkUrl(url) {var reg = /(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&:/~+#]*[w-@?^=%&/~+#])?/;
if (url != ""&& reg.test(url) && url.indexOf("page/") != -1 && url.indexOf(".html") != -1) {return true;}
return false;
}
function getVids(url) {url = url.split(".html")[0].split("/");
return url[url.length - 1];
}
function process() {var bqApi = "http://vv.video.qq.com/getinfo?vids={vids}&platform=101001&charge=0&otype=json&defn=shd";
bqApi = bqApi.replaceAll("{vids}", vids);
var api1Json = {};
$.ajax({
type: "get",
url: bqApi,
dataType: "jsonp",
jsonpCallback: "cb",
success: function (data) {
api1Json = data;
downloadUrl = api1Json.vl.vi[0].ul.ui[0].url + api1Json.vl.vi[0].fn + "?vkey=" + api1Json.vl.vi[0].fvkey;
$("#result").html("<span style='font-weight:bold;font-size:20px;'> 下载方式:</span><br/> 方式一:" + "<a target='_blank'href='" + downloadUrl + "'> 点击此处 </a> 打开视频,在视频上右键另存为保存到本地。<br /> 方式二:如果您的浏览器比较新,直接 <a href='javascript:void(0)'onclick='dw(""+ downloadUrl +"")'> 点击此处 </a> 下载视频。");
}
});
}
function dw(url) {
try{fetch(url).then(res => res.blob().then(blob => {var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = "kevy-download";
a.click();
window.URL.revokeObjectURL(url);
}));
}
catch(err){alert("您的浏览器不支持该方式,请使用方式一");
}
}
</script>
</body>
</html>注意事项本人亲测目前仅支持腾讯视频。
-
Kangle一键脚本
https://2sv.cn/archives/270/
2025-07-31T17:48:00+08:00
AI摘要:Kangle一键脚本是一个集成Kangle、Easypanel、MySQL、PHP的Linux脚本,支持多版本PHP与MySQL的安装。它提供国内外下载节点、CDN专用安装模式,并优化了Easypanel面板。脚本可自定义安装参数,如数据库密码和错误页面,还支持工具箱功能,如更换Yum源、DNS和清理垃圾。Easypanel优化包括SSL设置、PHP版本切换、IP白名单等。支持多种Linux系统,推荐CentOS 7。Powered by AISummary.脚本简介彩虹 Kangle 一键脚本,是一款可以一键安装 Kangle+Easypanel+MySQL+PHP 集合的 Linux 脚本。脚本本身集成:PHP5.3~8.2、MYSQL5.6~8.0,支持极速安装和编译安装 2 种模式,支持 CDN 专属安装模式。同时也对 Easypanel 面板进行了大量优化。脚本特点有国内和国外 2 个文件下载节点,提升安装速度自带 Kangle 商业版最新版本免费使用支持 EP 前台自由切换 PHP5.3-8.2安装前可选 MySQL5.6、5.7、8.0 版本预先设置各 PHP 版本 PHP.ini 安全问题安装前可自定义数据库密码,避免安装完成后再设置的麻烦支持自定义 403.404.503.504 等错误页面脚本中可切换其它几套 EP 用户后台模板脚本中集成 Linux 工具箱,可一键更换 Yum 源、更换 DNS、设置 Swap、同步时间、清理垃圾等修改 kangle 二进制文件以提升错误页加载速度EP 基于原版的优化内容:0.EP 源码全解密并升级 smarty 框架1.SSL 证书可同步到 cdn 节点2.SSL 配置页面新增 ”HTTP 跳转到 HTTPS” 选项3.SSL 配置页面新增 ” 开启 HTTP2″ 选项4.CDN 可以给单个域名设置 SSL 证书增加独立的 PHP 版本切换页面6.EP 管理员后台增加选项:默认 PHP 版本、允许域名泛绑定修复带有空格的文件名无法解压和重命名的问题9.CDN 绑定域名可以自定义回源协议,增加 tcp 四层转发优化防 CC 设置页面,支持设置 IP 和 URL 白名单清除缓存页面支持批量清除支持设置 URL 黑名单绑定域名页面新增编辑按钮EP 升级方法:脚本主菜单选择单独安装 / 更新组件,然后选择更新 Easypanel支持的系统:CentOS 7(推荐)CentOS 8CentOS Stream 8Rocky Linux 8AlmaLinux 8OpenCloudOS 8CentOS 6(不支持安装 PHP7.4 及以上版本)安装方式:请复制以下指令到 ssh 连接软件粘贴执行yum -y install wget;wget http://kangle.cccyun.cn/start;sh start
-
利用 Cloudflare Worker 镜像 GitHub 站点实现在墙内访问
https://2sv.cn/archives/269/
2025-07-31T17:45:28+08:00
AI摘要:本文介绍了如何利用 Cloudflare Worker 实现 GitHub 站点的镜像,并通过自有域名实现墙内访问。首先,创建并部署 Cloudflare Worker,使用 JavaScript 代码将 GitHub 站点内容转发到自定义域名。接着,配置访问控制和屏蔽特定地区与IP,最终通过绑定自定义域名绕过 DNS 污染,确保能够在墙内顺利访问镜像站。Powered by AISummary.Cloudflare Worker 是 Cloudflare 公司提供的一项服务,它允许开发者在 Cloudflare 的边缘服务器上运行自定义的 JavaScript 代码。通俗地说,它就像是一个小型的服务器,可以在互联网上的不同地点快速执行你编写的代码。Cloudflare Worker 的主要用途包括网站加速和优化、路由请求、访问控制、CDN 功能增强、网站镜像与转发等。本文将使用其网站镜像和访问控制的能力,实现 GitHub 站点的镜像,并通过自有域名实现在墙内的访问。镜像 GitHub 站点创建 Worker首先,登录 Cloudflare,切换至 Workers & Pages 菜单,点击 创建 按钮:等待页面刷新,再点击点击 创建 Worker按钮新建一个 Worker:接着,设置一个三级域名 https://demo.93653142.workers.dev,点击 部署 按钮:再接着点击 编辑代码 按钮进行代码编辑最后,粘贴以下代码片段后,右边就可以显示访问后的效果展示,点击 部署 完成部署,然后通过域名 https://demo.93653142.workers.dev/ 访问镜像站:
// 你要镜像的网站.
const upstream = 'github.com'
// 镜像网站的目录,比如你想镜像某个网站的二级目录则填写二级目录的目录名,镜像 google 用不到,默认即可.
const upstream_path = '/'
// 镜像站是否有手机访问专用网址,没有则填一样的.
const upstream_mobile = 'github.com'
// 屏蔽国家和地区.
const blocked_region = ['KP', 'SY', 'PK', 'CU']
// 屏蔽 IP 地址.
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']
// 镜像站是否开启 HTTPS.
const https = true
// 文本替换.
const replace_dict = {'$upstream': '$custom_domain', '//github.com': ''}
// 以下保持默认,不要动
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
const region = request.headers.get('cf-ipcountry').toUpperCase()
const ip_address = request.headers.get('cf-connecting-ip')
const user_agent = request.headers.get('user-agent')
let response = null
let url = new URL(request.url)
let url_hostname = url.hostname
if (https == true) {
url.protocol = 'https:'
} else {
url.protocol = 'http:'
}
if (await device_status(user_agent)) {
var upstream_domain = upstream
} else {
var upstream_domain = upstream_mobile
}
url.host = upstream_domain
if (url.pathname == '/') {
url.pathname = upstream_path
} else {
url.pathname = upstream_path + url.pathname
}
if (blocked_region.includes(region)) {
response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
status: 403
})
} else if (blocked_ip_address.includes(ip_address)) {
response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
status: 403
})
} else {
let method = request.method
let request_headers = request.headers
let new_request_headers = new Headers(request_headers)
new_request_headers.set('Host', url.hostname)
new_request_headers.set('Referer', url.hostname)
let original_response = await fetch(url.href, {
method: method,
headers: new_request_headers
})
let original_response_clone = original_response.clone()
let original_text = null
let response_headers = original_response.headers
let new_response_headers = new Headers(response_headers)
let status = original_response.status
new_response_headers.set('access-control-allow-origin', '*')
new_response_headers.set('access-control-allow-credentials', true)
new_response_headers.delete('content-security-policy')
new_response_headers.delete('content-security-policy-report-only')
new_response_headers.delete('clear-site-data')
const content_type = new_response_headers.get('content-type')
if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname)
} else {
original_text = original_response_clone.body
}
response = new Response(original_text, {
status,
headers: new_response_headers
})
}
return response
}
async function replace_response_text(response, upstream_domain, host_name) {
let text = await response.text()
var i, j
for (i in replace_dict) {
j = replace_dict[i]
if (i == '$upstream') {
i = upstream_domain
} else if (i == '$custom_domain') {
i = host_name
}
if (j == '$upstream') {
j = upstream_domain
} else if (j == '$custom_domain') {
j = host_name
}
let re = new RegExp(i, 'g')
text = text.replace(re, j)
}
return text
}
async function device_status(user_agent_info) {
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]
var flag = true
for (var v = 0; v < agents.length; v++) {
if (user_agent_info.indexOf(agents[v]) > 0) {
flag = false
break
}
}
return flag
}至此,镜像 GitHub 站点已完成,我们任意搜索一个 layui 的开源库,搜索结果如下图图所示。但是由于国内 workers.dev 域名的 DNS 已污染导致无法访问,所以需要绑定一个自定义域名来绕过该问题。绑定自定义域名先返回上一次,找到对应的 名称 点击 设置,添加自定义域 添加一个自定义域名:最后,等待 DNS 解析生效,然后使用 添加的自定义域名进行访问