G

gorpeln的个人博客 - gorpeln

gorpeln的个人博客(https://gorpeln.top/)是一个追求简洁、高效,专注记录生活点滴、分享技术成长、共享实用资源的个人优秀博客!趁年轻,做自己想做的!欢迎你来这里交流互动,一起探索精彩世界,开启知识与心灵的非凡旅程。

博客CDN流量异常

今天打开博客查看时显示异常,提示connection reset by peer。通过控制台数据回溯发现,9 月 26 日 - 9 月 30 日期间,博客流量已出现明显异常波动:访问请求量远超日常均值,且来源 IP 分散、访问行为不符合正常用户逻辑,初步判断为恶意流量攻击。所幸此前已针对博客安全做了基础防护配置 —— 开启了IP访问频率限制与流量封顶限制,所以在本次攻击中损失较小。 在检测到持续的异常流量后,CDN系统触发了安全防护机制,对博客访问进行了临时限制,但由于我此前未开启又拍云的异常事件短信/邮件提醒功能,未能第一时间收到预警,导致发现问题时已出现访问中断。截至目前,博客访问链路完全恢复,经测试各项功能均正常可用。 最后建议平时做好防护,避免出现损失: CDN 关键配置必到位:开启「异常提醒」 (短信 + 邮件),避免错过预警;同时设置 IP 访问频率限制 (如单 IP1 分钟≤60 次)、流量封顶,再给静态资源加 URL 鉴权,筑牢基础防护。 定期看数据识异常:每周花几分钟查流量,记住正常 IP 地域、请求时段 (如博客晚间高峰),若出现海外 IP 突增、凌晨流量异常,及时排查。 做好兜底应急准备:每周备份网站数据 (本地 + 云盘),留存 CDN 服务商紧急联系方式;提前配置临时备用页,避免访问异常时读者误解。

2025/9/30
articleCard.readMore

10 周年:时光为证,与你共赴新程

在博客十年之际,写一篇纪念博文很有必要。博主苦思冥想写了一篇类似 5周年 和 8周年 的博文,但是思前想后,感觉过于书面,不接地气,你也没有阅读的兴趣,就改用了现在的问答形式,简明扼要的展示想表达的内容。用来总结十年的风雨征程和见闻感悟,下一个十年,愿我们依然,对技术保持敬畏,对未知渴求探索。 博客最初搭建的时间? 2015年08月23日,时光飞逝,从大学毕业前搭建到现在已过去10个年头了。 最初为什么搭建博客? 学即将毕业走入职场时,老师说计算机专业学生出去找工作如果有一个自己的博客,将会为自己的面试加分,为了找工作轻松,在懵懂中搭建了自己的博客。 博客能否为自己求职加分呢? 能的,在同等情况下,面试官一般都会优先选择你的,因为博客是你的“专业能力 + 学习习惯 + 思维深度 + 个人特质”的综合证明,比一纸简历更有说服力,尤其是刚毕业,没有丰富的工作经验的同学。 现在还有写博客的必要吗? 有的,现在许多大的网站,小编为了完成KPI或流量,生产出大量“洗稿”或拼凑出来的内容,内容质量一言难尽,但个人博客不一样,博主写的都是自己真实的想法和见解。它像是互联网的一片自留地,安静、干净,也更有灵魂。 博客域名的由来? 刚开始博客域名使用的是我名字的全称 guanpengchen ,但是感觉太长了,体验感不好,就放弃使用了。 接着就改用了名字的缩写 gpchen ,但是 gpchen 在很多平台都被占用了,无法在全平台形成个人品牌,于是只能无奈放弃。 最后决定用自己的英语储备,将自己的名字音译为了英语,即 gorpeln ,实现全平台个人品牌,即你在任何平台搜索 gorpeln 的相关内容,都是与我相关的。 为什么博客名字使用英文而不是用中文? 为了多平台统一,形成个人的品牌效应。gorpeln 是独一无二的,这个单词对其他人没有任何意义,不存在重复可能,而中文名字很多平台有使用限制,且简短的中文名称一定有被占用的情况。 为什么选用TOP域名? 感觉走了很多弯路才最终选择使用top域名。 刚开始用的都是com域名,但是名称一直没有确定,使用过 guanpengchen、gpchen、gpchenx 等名字,2018年左右确定了名字 gorpeln ,从而形成了 gorpeln#com 域名,当时博客收录量较好,也有一定的权重,但在一个平凡的午后,一位陌生人问我域名出手吗?博主最后妥协了。博主改用 gorpeln#cn 域名,但是CN域名在使用过程中存在一些弊端,博主认为不适合自己,还是得申请国际顶级域名,COM域名一直没有释放,就只能选用TOP域名了。 博客还会修改域名吗? 应该不会了,在博客十年之际,博主将TOP域名续费了10年,正常要再用十年。 CN域名使用限制较多,后续不再考虑。 COM价格涨价频繁也不再考虑了。 COM和CN域名还可以访问? 由于域名在我使用后有一定的权重,被一些专业机构抢注用来做一些不好的事情,请大家谨防上当,随着权重降低,举报增多,域名将会失去价值,应该会逐渐释放。 坚持“无人问津”的爱好是否有意义? 针对“无人问津”的兴趣爱好,我的看法是,做好自己的本职工作,无论是学习还是在职工作,然后放手去做吧。日常生活中,博客的坚持并不会占用你太多的时间和精力,但是经过长期的坚持,将会有质的获得。另外,在变强的路上注定是孤独的,所以不必担心。 新手使用第三方博客如何? 个人极其不建议,因为你到最后肯定会走到自建博客的道路的。因为第三方平台限制多、审核时间长、广告多、收费多,体验感很不好。博主刚开始写博客也会将内容同步到CS*N等平台,因为平台有流量,但是体验感很不好,尤其是近年来广告、收费等愈发变态,博主接受不了的。当然前期你可以将文章同步到多个平台,用来提高个人博客的流量,但建议将重心放在个人博客中。博主的两个CS*N账户分别10年和7年了,现在已经彻底放弃了。CS*N 1 和 CS*N 2 对博客新手的建议? 博客的搭建十分简单,不用担心技术问题。 如果你不懂技术可以选用 Hexo、Jekyll、Hugo、WordPress 等各种博客框架,加上第三方主题,可以实现零基础搭建的。如果你懂技术,还是建议不用框架,这样才方便自己后期的自定义开发等。 博客搭建过程中最重要的就是 成本,尤其是初学者,你要尽可能压缩成本,使其无限趋近于零才是最好的,只有这样你才能走的更远。 为什么建议新手降低成本呢? 你不可能永远喜欢一件事,当你的第一波热情退去,你的博客将会摆烂的,随着着服务器、域名、CDN等到期,你又不想管,你的博客就会随之而去。但是,如果你前期使用的成本较低,如使用静态博客,几乎零成本,资源也不会到期,等你再次想搭建博客,就可以快速开始。这是博主将 来往 的成员列表中1700+个博主一个一个的看了两遍的深刻体会,很多博客就只存活了1年就结束了。 时光为证,与你共赴新程? 每个人都有独属于自己的「十年故事」。在你的十年故事里,是否也有一个无人问津,却仍坚持至今的「gorpeln's Blog」?这十年里,尽管我已成长了很多,但许多问题,诸如「奋斗的意义」、「人为何而活」,我仍然给不出一个令我感到满意的答案。但人生的这份试卷仍在继续,我们仍在行进,在不断书写属于自己的那份或许并不完美,但一定是独一无二的答案。我曾以为的「参考答案」,实际上是:略。下一个十年,愿我们依然,对技术保持敬畏,对未知渴求探索。 人生格言? 趁年轻,做自己想做的! 你一定听过「人不可能同时拥有青春和青春的感受」,因此,一定要「趁年轻,做自己想做的!」,无论结果成败,都是自己的人生财富。一旦错过,剩下的就只剩遗憾。 博客成长记录? 查看博客成长记录 博客常见问题? 查看博客常见问题 博客成长相关图片

2025/8/23
articleCard.readMore

时光本 - 数据导出

亲爱的用户,非常抱歉因业务调整,时光本应用即将下架,给你带来的不便还望谅解。 过去的日子里,感恩你的陪伴与信任,那些承载着回忆与成长的笔记,我始终珍视,即便APP落幕,我也会坚守有始有终的承诺,全力保障你的使用体验,帮你顺利迁移数据。 为确保数据安全,我准备了 iOS (适用于 iPhone) 和 macOS (适用于 Mac 电脑) 版本的导出流程,操作中若有疑问,请留言,我会协助你完成迁移,再次感谢你的理解与支持。 最后,请务必在APP下架前升级至最新版本完成导出,避免数据丢失。 iOS端 最新版本号:2.1.4 macOS端 最新版本号:1.5.4 iOS 端 一、文字流程 时光本iOS 端数据导出流程: 前往 App Store 将应用更新至最新版本。 点击进入 我的 - 云备份 - 本地备份,选择 『 笔记 』数据导出 或 『 账号 』数据导出。图 ①、图 ②、图 ③、图 ④ 导出的数据将以json格式复制到系统剪切板,你可以发送到电脑端以文本 (.txt)或json (.json)格式存储。图 ⑤、图 ⑥ 如果需要其他格式文件,请自行通过json文件转换成相应文件。以json转化为excel文件为例,方法如下: (以下方法仅供参考,请自行判断)。 访问 在线JSON转Excel 或 直接在浏览器搜索 json转excel 选择相应的在线工具或应用。图 ⑦ 将第 3 步导出的json数据复制到对应网站,点击转换,即可在线查看对应列表数据,可以点击页面下载 Excel 或 下载 CSV,下载本地保存。图 ⑧、图 ⑨ 二、图片流程 macOS 端 时光本macOS 端数据导出流程: 点击 备份到iCloud ,将数据备份到 iCloud。 在手机端 App Store 下载最新版iOS端应用。 点击进入 我的 - 云备份 - 云同步 - 从iCloud恢复,将iCloud云端数据同步到iPhone上。 参照上述 iOS 端 导出流程进行数据导出。

2025/7/20
articleCard.readMore

时光本 - 下线公告

重要通知 亲爱的时光本用户们: 展信安好。 首先,由衷地感谢大家这些年来对时光本的支持与陪伴。这款应用最初只是我在工作之余,出于兴趣开发的小小尝试 —— 我总希望能为大家提供一个纯净、便捷、免费且安全的工具,这份初心,从未改变。 但今天,我带着些许不舍却又坚定的心情,想告诉大家一个决定:时光本将于 2026 年 01 月 20 日 23:59 正式停止运营。做出这个决定并不容易,反复斟酌后,我还是希望用这样的方式,为这段旅程画上一个平静的句号。未来,我想暂时放缓脚步,去做一些真正属于自己的内容,所以此刻只能道一句:后会有期。 关于后续使用的几点说明,还请大家留意: 已安装的时光本软件仍可继续使用,但未来将不再进行任何更新与维护; 请务必及时备份好个人数据,若使用过程中出现问题,需由用户自行承担; 应用结束运营前,时光本 数据导出 方法; 最后,想把最真诚的祝福送给每一位用户:愿大家在未来的日子里,总能与美好不期而遇。 开发者:gorpeln 2025 年 07 月 20 日

2025/7/20
articleCard.readMore

整蛊代码:你屏幕上有根毛

我看评论很多人问主页上显示的这跟毛,可以直接放到自己博客,仅为娱乐,请勿滥用哦。 以下是优化后的完整 js 代码,将代码插入到你的网页中即可看到效果! 代码中的图片地址可能后续会失效,建议保存到本地使用。 /*** * 愚人节彩蛋 - 你屏幕上有根毛 * 提示:仅为娱乐,请勿滥用 * 整理:gorpeln https://gorpeln.top/ */ !function() { var bottom = Math.floor(60 * Math.random()), right = Math.floor(50 * Math.random()), rotate = Math.floor(360 * Math.random()); var foolsEgg = document.createElement("img"); foolsEgg.src = "https://img.gorpeln.top/blog/20250615173327311.png"; foolsEgg.style.position = "fixed"; foolsEgg.style.bottom = "".concat(bottom, "%"); foolsEgg.style.right = "".concat(right, "%"); foolsEgg.style.zIndex = "9999"; foolsEgg.style.pointerEvents = "none"; foolsEgg.style.width = "40%"; foolsEgg.style.maxWidth = "190px"; foolsEgg.style.transform = "".concat("rotate(", rotate, "deg)"); document.body.append(foolsEgg); } (); /*** * 愚人节彩蛋 - 你屏幕上有根毛 * 提示:仅为娱乐,请勿滥用 * 整理:gorpeln https://gorpeln.top/ */ !function() { var bottom = Math.floor(60 * Math.random()), right = Math.floor(50 * Math.random()), rotate = Math.floor(360 * Math.random()); var foolsEgg = document.createElement("img"); foolsEgg.src = "https://img.gorpeln.top/blog/20250615173327311.png"; foolsEgg.style.position = "fixed"; foolsEgg.style.bottom = "".concat(bottom, "%"); foolsEgg.style.right = "".concat(right, "%"); foolsEgg.style.zIndex = "9999"; foolsEgg.style.pointerEvents = "none"; foolsEgg.style.width = "40%"; foolsEgg.style.maxWidth = "190px"; foolsEgg.style.transform = "".concat("rotate(", rotate, "deg)"); document.body.append(foolsEgg); } ();

2025/6/15
articleCard.readMore

博客阅读进度条

将下方代码放到头部HTML代码内 <div id="percentageCounter"></div> css代码: <!-- 阅读进度条开始 --> <style type="text/css"> #percentageCounter{ position:fixed; left:0; top:0; height:3px; z-index:99999; /*绿红色*/ /*background-image: linear-gradient(to right, #339933,#FF6666);*/ /*白蓝色*/ background-image: linear-gradient(to right, #E8EAF6,#C5CAE9,#9FA8DA,#7986CB,#5C6BC0,#3F51B5,#3949AB,#303F9F,#283593,#1A237E); border-radius:5px; } </style> js代码: <script type="text/javascript"> $(window).scroll(function() { var a = $(window).scrollTop(), c = $(document).height(), b = $(window).height(); scrollPercent = a / (c - b) * 100; scrollPercent = scrollPercent.toFixed(1); $("#percentageCounter").css({ width: scrollPercent + "%" }); }).trigger("scroll"); </script> <!-- 阅读进度条结束 -->

2025/6/6
articleCard.readMore

手机真的在监听你的谈话?

许多人都遇到过这样的情况:刚跟朋友谈到某件商品,手机很快就推送了相关广告。于是,一种流行的阴谋论便开始流传:“手机在随时监听我们的对话。”但事实真的如此吗?我将从技术原理、经济逻辑、法律监管和实际证据四个维度进行全面分析,以澄清这一误解。 一、技术层面:实时监听难度巨大,成本过高 要想实现“实时监听并精准推送广告”,技术上需要解决三大难题: 高频音频采集与实时分析:如果手机要随时监听,意味着需要持续运行麦克风录音、语音识别与自然语言处理(NLP)技术。这对电池消耗、计算资源、数据处理能力都提出极高要求。目前主流手机的电池和处理器尚无法支撑如此强大的后台持续运行任务。即使是语音助手(如Siri、Google Assistant)也只会在触发特定关键词(“嘿Siri”或“OK Google”)后才启动识别。 网络传输负担巨大:若要实时上传每个用户的所有音频数据,所需的数据流量和服务器存储将达到天文数字。目前全球所有的互联网服务商也无力承担这样巨大的网络带宽需求和存储成本。 精准的上下文分析:困难重重语言中的歧义、方言、噪声干扰,都会导致语音转文字后的准确性大幅降低,更不要说实时理解上下文并匹配广告推送了。这种技术的误差率高、成本大,收益也远远不足以覆盖成本。 二、商业逻辑:监听谈话并非最佳营销策略 很多人认为“监听谈话推广告”对商家有利,但事实上: 数据精准度不足,效果有限即使商家真的监听了你的对话,由于人们的日常交谈随意性强,很多对话内容与真实需求并无必然联系。从营销角度来看,这种推送的转化率和精准度远不及用户主动搜索、电商浏览历史或社交媒体互动行为所带来的精准。 已存在更有效的数据收集手段广告商早已有更高效、合法且成本更低的方法,例如用户的搜索记录、网页浏览历史、App使用行为、地理位置信息等。这些都是经过用户协议明确授权、合法合规获得的精准信息来源,比起昂贵又敏感的“监听”要安全有效得多。 三、法律监管:监听用户属违法行为,风险极大 在绝大多数国家和地区,包括中国、美国和欧盟,未经用户许可私自监听谈话都是严重违法行为: 严格的隐私保护法规欧盟的《通用数据保护条例》(GDPR)、美国的隐私保护法案、中国的《个人信息保护法》都对手机App、服务商的权限做出严格限制,违法违规者将面临巨额罚款、声誉损失甚至刑事责任。 法律后果严重,不符合商业理性如果手机厂商或软件公司真的采用监听的方式获取信息,不仅会触犯法律,更会面临严重的法律诉讼风险和公众信任的彻底崩溃。这对于品牌形象和企业长期利益都是致命打击,任何有理性商业头脑的公司都不会冒如此巨大的风险。 四、实际证据:实证研究与官方调查均否认监听行为 实际上,多个权威机构都已对这种谣言进行调查: 科学实证研究美国东北大学研究团队在2018年进行了一项全面的实验,分析了超过1.7万个App,并未发现任何一个App在用户未授权的情况下秘密录音并上传语音数据。研究团队明确表示,目前尚无证据表明手机存在监听用户谈话的行为。 科技公司公开声明及第三方监督苹果、谷歌等科技巨头多次公开表示从未监听用户对话。若这些声明不属实,面临的法律风险和名誉损害都远远超出任何商业利益。同时,各国监管部门和第三方隐私保护机构也定期审计这些公司,未发现监听行为。 为什么会产生这种错觉? 人们之所以误以为手机在监听,很可能是因为: 选择性注意和心理暗示用户谈论了某个话题后,当看到相关广告时,更容易引起注意并加深记忆,形成“被监听”的错觉。实际上,这类广告可能早就出现,只是用户之前并未留意。 算法精准推荐的效果大数据分析和个性化推荐技术已经十分成熟,通过用户行为(搜索、位置、消费记录)就能高度准确地预测用户兴趣,因此看起来仿佛是“偷听”了对话。 话题的时效性和热度用户谈论的很多话题本身就是当前的热点(比如流行产品、时令商品),而广告系统也在同步更新热点推送内容,使用户感觉“刚说就推送”。 结论:不存在手机实时监听推送广告的行为 综上所述,“手机监听用户谈话推广告”不过是一种误解,是缺乏对技术原理、经济逻辑、法律风险、实际证据全面了解而产生的阴谋论。我们生活在一个隐私保护越来越严密的时代,手机App厂商不仅没有足够的动机、技术条件,也没有胆量去冒违法风险进行实时监听。 理解真实的推送原理和背后的技术、逻辑、法律基础,我们才能避免被不必要的焦虑和误解困扰。

2025/5/31
articleCard.readMore

使用Serv00部署在线工具箱

效果演示 在线工具箱 演示网站 在线工具箱:提供丰富的在线工具,如 JSON 格式化、美化、压缩、解析,Unix 时间戳转换,CSS 美化等多功能支持。 事前准备 域名( 非必须,你也可以使用免费域名) Serv00 ( 必须,免费注册的,没有可以自行注册但是对IP环境和邮箱要求比较大,注册提示维护一般都是IP或者邮箱不太行,邮箱建议使用谷歌的。) 开始部署 01、 登录你的 serv00 后,点击 WWW websites 再点 Add new website 添加你的域名我设置为 tool.gorpeln.top ,点 Add 添加。看到这个提示 Operation performed successfully 成功添加。 02、 点 Manage SSL certificates 可以看到这个ip地址 128.204.223.xxx 然后去你的DNS解析平台绑定。 03、 打开DNS解析平台解析。 04、 这样设置完,你打开的设置的网站域名,可以看到这样的页面就是成功。 05、 接下来正式开始打开 tool下载源码 。 06、 点击 WWW websites 再点 Add new website 再点 tool.gorpeln.top的Details ,设置你网站的路径,如图所示,修改 Open Basedir directories 中的内容,把下方代码中的 serv00的名称 替换为你的登录用户名, 自己的域名 同理,修改完复制进去保存。 /usr/home/serv00的名称/domains/自己的域名/public_html:/tmp:/usr/share:/usr/local/share:/dev:/tmp:/usr/share:/usr/local/share:/dev:/usr/home/serv00的名称/domains/自己的域名 07、 设置完毕,打开serv00 的 File manager,上传从github下载的tool源文件压缩包 08、 进到你域名这个目录,上传你之前下载好的源码。右键压缩包如图。 09、 进到 tool-main 这个目录里面,全选除public外的所有文件 10、 选择解压到你域名的目录,只要你跟我图一样就没问题。 11、 进到命名为 public 目录,全选所有文件,右键解压到域名目录中的 public_html 目录。 12、 进到 public_html 目录,将命名为 .htaccess 的文件右键打开使用text方式编辑,你如果没有我图片上的text,就选 choose other 进行选择编辑方式拉到最下面就有了。 13、 将文件内容改为这个代码 然后保存。 AddType application/x-httpd-php71 .php <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </IfModule> 14、 完成!打开你的网站就可以看到内容了。 15、 (可选项)通过访问 https://你的域名/admin,访问后台,进行相关配置修改。

2025/5/1
articleCard.readMore

自定义博客文章卡片

效果 迷幻紫 在这里输入文本 西瓜红 在这里输入文本 天空之境 在这里输入文本 小宇宙 在这里输入文本 橄榄绿 在这里输入文本 小太阳 在这里输入文本 优雅紫 在这里输入文本 深邃黑 在这里输入文本 代码 迷幻紫 <div class="post-card card-theme1">在这里输入文本</div> 西瓜红 <div class="post-card card-theme2">在这里输入文本</div> 天空之境 <div class="post-card card-theme3">在这里输入文本</div> 小宇宙 <div class="post-card card-theme4">在这里输入文本</div> 橄榄绿 <div class="post-card card-theme5">在这里输入文本</div> 小太阳 <div class="post-card card-theme6">在这里输入文本</div> 优雅紫 <div class="post-card card-theme7">在这里输入文本</div> 深邃黑 <div class="post-card card-theme8">在这里输入文本</div>

2025/4/26
articleCard.readMore

个人博客可以尝试的 100 件事

✓ 本网站已支持  ☐ 本网站暂不支持  × 本网站暂不考虑   ✓ 如果你还没有个人网站,可以搭建一个! ✓ 写一篇博客,分享你感兴趣的事、最近让你开心的经历、过去一周学到的新东西、读完一本书后的感想、你玩喜欢的游戏的攻略,或者任何你感兴趣的话题。 ☐ 分享一道最近做的美味菜谱。 ☐ 发布一张最近拍摄的照片。 ✓ 创建一个友情链接,链接你喜爱的个人网站。 ✓ 添加一个 robots.txt 文件,明确限制搜索引擎和AI抓取你网站的内容。 ☐ 为你喜欢的乐队、电视剧或其他事物创建一个特别的「纪念馆」或「粉丝小站」。 ☐ 制作图像热区图,让访客点击图片不同部分探索更多信息,比如你书桌上的物品介绍。 ✗ 分享你最喜欢的书单。 ✗ 创建电影推荐专页。 ✓ 提供一个深色模式选项,让读者更舒适地阅读。 ✓ 注册并使用一个属于你的专属域名。 ✓ 添加一个有趣的小彩蛋。 ✓ 搭建留言板,让大家畅所欲言。 ✓ 给你的网站或特定页面设置背景图片。 ☐ 设计一个网站寻宝游戏,让访客通过寻找线索探索你的网站。 ✓ 在 HTML 代码中加入清晰的注释,帮助新手理解你网页各部分的用途。 ✓ 分享最近喜欢的网页,比如文章、博客、摄影作品或游戏。 ✓ 为图片添加描述文字。 ✓ 使用 WAVE 工具检查网站的无障碍访问性。 ✓ 用 PageSpeed Insights 优化网站速度。 ☐ 如果你发布学术作品,添加引用说明。 ✗ 创建一个「反向图书馆」清单,记录你拥有但未读的书籍。 ☐ 制作一个文字小游戏。 ✓ 记录网站更新日志。 ✗ 分享你最近学到的新词列表。 ✓ 添加 RSS 订阅 功能,让访客轻松关注更新。 ☐ 创建「历史上的今天」功能,链接到以往同日期发布的博客。 ✓ 添加锚点链接,提升网站对屏幕阅读器的支持。 ✓ 制作文章归档页面。 ☐ 为网站设计一个专属吉祥物。 ✗ 制作并添加一个 88×31 像素的小按钮图标,通常用于网页、博客、论坛等地方的链接按钮或标识图标。这种按钮图标在早期互联网时代非常流行,经常用于交换链接、展示合作伙伴或赞助商的链接。 ✓ 加入一个网站联盟,比如 十年之约 和 开往 Travellings。 ✓ 翻译网站的某个页面到你熟悉或正在学习的语言,比如 时光本。 ✓ 创建打印样式表。 ✓ 确保所有链接有效无误。 ☐ 发起博客活动,邀请朋友们围绕特定主题每月发表文章,并轮流确定主题。 ☐ 添加 rel=me 链接,认证其他平台上的个人资料,比如在博客自我介绍中列出自己的 Mastodon、GitHub、Twitter 账号。 ☐ 做一些额外的惊喜内容(おまけ)。 ✓ 如果你想展示专业作品,可以创建 HTML 简历页面。 ✓ 使用语法高亮工具(如 Prism.js)美化代码片段。 ✓ 检查和优化 head 标签中的内容。 ✓ 为网站添加搜索功能。 ☐ 创建“此刻(now page)”页面,通常用来展示网站拥有者当前正在关注的事情、工作、生活状态或感兴趣的项目。 ☐ 使用迷你图(sparkline)展示文章发布频率。Sparkline 是指通过一种小巧、简洁的折线或柱状图,以极小的空间清晰地展现文章发布数量在一段时间内的变化趋势。热图也是一个很好的选择。 ☐ 尝试不同的字体风格。不同的字体适合不同的主题,可以一起调整。 ✓ 优化标题字号的层次区分。 ✓ 在文章末尾加入装饰性分隔符。 ✗ 使用 lite-youtube 组件加快视频加载速度。 ☐ 更新网站配色方案。 ✓ 创建 HTML 元素样式展示页面,辅助开发优化。 ☐ 编写趣味问答,邀请朋友参与。 ☐ 为网站添加 Webmention 支持。Webmention 是一种开放的 Web 标准,用于在不同网站之间发送和接收消息通知,使得网站可以相互引用、交互以及进行社交化沟通。 ☐ 制作共享使用的网页组件(web component)。 ✓ 为图片设置鼠标悬停效果。 ✗ 添加您说出自己名字或昵称的录音,以便大家知道如何发音。 ✓ 在特别的节日为网站换上主题装扮。 ✗ 分享你亲手绘制的画作。 ✗ 在主页添加互动式像素网格。 ☐ 标记网站内容为非 AI 生成,可参考这个项目 Not By AI 。 ☐ 分享你喜爱的播客清单。 ✓ 为你的兴趣爱好专门开设一个新的网站。 ✓ 制作网站图标(favicon)。 ✓ 写一篇纪念个人网站发展史。 ☐ 添加背景播放音频,如鸟鸣或咖啡厅氛围。 ☐ 制作个人风格的情绪板(mood board),是指通过搜集并组合图片、颜色、文字、材质等元素,来视觉化呈现个人喜好、风格偏好或某种特定氛围的过程。 ✓ 使用 HTML 的 details 和 summary 标签。 ☐ 自定义文本选中时的高亮颜色。 ☐ 帮助朋友搭建他们的网站。 ✓ 为自己撰写网站维护文档。 ☐ 为喜欢的艺术作品撰写评论。 ☐ 与朋友合写一篇共同兴趣的博客。 ✓ 更新旧博客文章。 ☐ 在文章中添加旁注(sidenotes)。 ✓ 为长文章创建目录。 ☐ 为外部链接添加识别小图标。 ☐ 添加悬浮卡片(hovercards)。悬浮卡片指的是当用户的鼠标指针悬停在某个元素上时,会弹出一个小卡片,显示额外的信息或提示。 没错。我确实承诺过要列出100个想法。但为了实现这个目标,我需要你的帮助。以上想法是我在个人网站上看到的,但我渴望听到更多人的想法。因此,我诚邀大家帮助我完成这份清单。写下一些可以在个人网站上实现的想法。将这些想法发布到你的博客上,与你的朋友们分享。 网络的魅力在于,我们可以一起分享想法,共同开发新项目。事实上,我们可以规划定义人们网络体验的路径。我们需要更多人来编织独立网络——那些在网络平台上进行实验并创造自己感兴趣的新事物的人。

2025/3/23
articleCard.readMore

MathJax的基本使用

一、引言 MathJax引擎是一个开源的JavaScript库,它允许Web开发者在网页中嵌入高质量的数学公式。通过利用Web的最新技术,MathJax引擎可以解析LaTeX、MathML和AsciiMath等数学标记语言,并将其渲染为可视化的数学公式,这些公式可以在各种浏览器和操作系统上流畅地显示。 使用MathJax引擎,网页作者可以轻松地编写包含数学内容的文档,而无需担心用户的浏览器或操作系统是否能够正确显示这些数学公式。因为MathJax引擎会自动处理这些兼容性问题,确保用户能够以清晰、准确的方式查看数学内容。 MathJax引擎的特点包括: 跨平台兼容性:它支持多种浏览器和操作系统,确保数学公式能够在各种设备上正确显示。 高质量的排版:MathJax引擎使用先进的排版算法,确保数学公式能够以清晰、准确的方式呈现。 易于使用:通过简单的标记语言,如LaTeX,用户可以轻松地编写数学公式,并将其嵌入到网页中。 高度可定制性:MathJax引擎提供了丰富的配置选项,允许用户根据自己的需求进行定制,以满足特定的排版和显示要求。 总的来说,MathJax引擎是一个功能强大、易于使用的工具,它为Web上的数学内容显示提供了高质量的解决方案。无论是科学论文、教育资料还是技术文档,都可以利用MathJax引擎来呈现复杂的数学公式,提升网页的可读性和专业性。 二、使用 1、安装 npm install mathjax@3 2、配置 MathJax = { tex: { packages: ['base'], // 要使用的扩展 inlineMath: [ // 行内数学公式的开始/结束定界符对 ['$', '$'], ['\\(', '\\)'] ], displayMath: [ // 显示数学公式的开始/结束定界符对 ['$$', '$$'], ['\\[', '\\]'] ], processEscapes: true, // 使用 \$ 来产生一个字面意义上的美元符号 processEnvironments: true, // 在数学模式之外处理 \begin{xxx}...\end{xxx} processRefs: true, // 在数学模式之外处理 \ref{...} digits: /^(?:[0-9]+(?:\{,\}[0-9]{3})*(?:\.[0-9]*)?|\.[0-9]+)/, // 用于识别数字的模式 tags: 'none', // 标签类型,可选值为 'none'、'ams' 或 'all' tagSide: 'right', // \tag 宏的位置 tagIndent: '0.8em', // 标签的缩进量 useLabelIds: true, // 使用标签名称而不是标签编号作为 ID maxMacros: 10000, // 每个表达式允许的最大宏替换次数 maxBuffer: 5 * 1024, // 内部 TeX 字符串的最大大小(5K) baseURL: (document.getElementsByTagName('base').length === 0) ? '' : String(document.location).replace(/#.*$/, '')), // 当存在 <base> 标签时,用于标签链接的 URL formatError: (jax, err) => jax.formatError(err) // 当 TeX 语法错误发生时调用的函数 }, options: { skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre', 'code', 'annotation', 'annotation-xml' ], // 不会搜索数学公式的 HTML 标签 includeHtmlTags: { br: '\n', wbr: '', '#comment': '' }, // 可以出现在数学公式内的 HTML 标签 ignoreHtmlClass: 'tex2jax_ignore', // 标记不进行搜索的标签的类名 processHtmlClass: 'tex2jax_process', // 标记应进行搜索的标签的类名 compileError: function (doc, math, err) {doc.compileError(math, err)}, // 编译错误处理函数 typesetError: function (doc, math, err) {doc.typesetError(math, err)}, // 排版错误处理函数 renderActions: {...} // 渲染操作 }, startup: { elements: null, // 要进行排版的元素(默认是文档主体) typeset: true, // 是否执行初始排版 ready: Startup.defaultReady.bind(Startup), // 组件加载完成时调用 pageReady: Startup.defaultPageReady.bind(Startup), // MathJax 和页面准备好时调用 document: document, // 要处理的文档(或片段或字符串) invalidOption: 'warn', // 无效选项是致命错误还是产生警告 optionError: OPTIONS.optionError, // 用于报告无效选项的函数 input: [], // 要使用的输入引擎的名称(从已加载的引擎中选择) output: null, // 要使用的输出引擎的名称(从已加载的引擎中选择) handler: null, // 要注册的处理程序的名称(从已加载的处理程序中选择) adaptor: null // 要使用的 DOM 适配器的名称(从已加载的适配器中选择) }, svg: { scale: 1, // 所有表达式的全局缩放因子 minScale: .5, // 要使用的最小缩放因子 mtextInheritFont: false, // 使 mtext 元素使用周围字体 merrorInheritFont: true, // 使 merror 文本使用周围字体 mathmlSpacing: false, // 为 true 时使用 MathML 间距规则,为 false 时使用 TeX 规则 skipAttributes: {}, // 不复制到输出的 RFDa 等属性 exFactor: .5, // ex 单位的默认大小(以 em 为单位) displayAlign: 'center', // 当 indentalign 设置为 'auto' 时的默认值 displayIndent: '0', // 当 indentshift 设置为 'auto' 时的默认值 fontCache: 'local', // 字体缓存设置,可选值为 'local'、'global' 或 'none' localID: null, // 用于本地字体缓存的 ID(用于单方程处理) internalSpeechTitles: true, // 插入带有语音内容的 <title> 标签 titleID: 0 // 用于 aria-labeledby 标题的初始 ID 编号 } }; 按需配置,如果你都不需要,你可以什么也不配置 3、复制 需要复制时,在对应公式部分点击 右键 -> Copy to Clipboard -> TeX Commands 即可完成复制 三、例子 1、四则运算 1)加减 $a+b-c$ 效果:$a+b-c$ 2)乘法 $a\times b$ (注意\times和b之间要有空格) 效果:$a\times b$ 3)分数 $\frac{a}{b}$ 效果:$\frac{a}{b}$ 4)括号 $(\frac{a}{b})$ $\left( \frac{a}{b} \right)$ $\left[ \frac{a}{b} \right]$ # 当使用\left和\right时,它们会根据所包含内容的大小自动调整括号的大小,使得括号能够合适地包围住内容,看起来更加美观和协调。而普通的小括号()不会自动调整大小,在一些复杂的公式中,可能会出现括号与内容大小不匹配的情况 效果: 5)四则组合 $\frac{(a+b)\times c}{(d-e)\times f}$ 效果:$\frac{(a+b)\times c}{(d-e)\times f}$ 2、比较运算 $a < b$ $a > b$ $a = b$ $a \not= b$ $a \leq b$ $a \geq b$ $a \equiv b$ 效果: 3、上标与下标 1)上标 $x^a$, $x^{ab}$, $x^ab$ # 多个字符时,要用花括号代替,不过单个字符时有无花括号是等价的 效果:$x^a$, $x^{ab}$, $x^ab$ 2)下标 $a_n$, $a_{nm}$, $a_nm$ # 花括号原理同上 效果:$a_n$, $a_{nm}$, $a_nm$ 3)次方根 $\sqrt a$, $\sqrt[n]a$, $\sqrt[nm]{ab}$ 效果:$\sqrt a$, $\sqrt[n]a$, $\sqrt[nm]{ab}$ 4)上下标组合 $a_i^2$, $C^2_{10}$ 效果:$a_i^2$, $C^2_{10}$ 4、集合间的运算 1)集合 $\left\\{ x_1,x_2 \right\\}$ # 不能直接打花括号,必须要有\left,\right # 可以写成一行,这样仅为了方便查看 效果:$\left\{ x_1,x_2 \right\}$ 2)元素与集合的关系 $A \in B$ // 元素A 属于 集合B $A \notin B$ // 元素A 不属于 集合B 效果: 3)集合与集合的关系 $A \subset B$ // 集合A是集合B的 真子集,意味着A的所有元素都在B中,但B中至少存在一个元素不在A中 $A \subseteq B$ // 集合A是集合B的 子集,即A的所有元素都在B中,A和B有可能相等 $A \supset B$ // 集合A 真包含 集合B,表明B的所有元素都在A中,但A中至少存在一个元素不在B中 $A \supseteq B$ // 集合A 包含 集合B,即B的所有元素都在A中,A和B有可能相等 效果: 4)集合的基本运算 $A \cap B$ // 集合A与集合B的 交集,由既属于A又属于B的所有元素共同组成 $A \cup B$ // 集合A与集合B的 并集,由属于A或者属于B的所有元素合并组成 $A \setminus B$ // 集合A与集合B的 差集,是由所有属于A但不属于B的元素构成的集合,也可表示为 A - B $\complement_U A$ // 集合A在全集U中的 补集,即全集中所有不属于A的元素所组成的集合 效果: 5)集合中的符号 $ \cdot $ // 一个点 $ \cdots $ // 三个点 $ \mid $ // 分隔符 效果: 6)集合组合示例 $A = \left\\{ x_1,x_2,\cdots,x_n \right\\}$ // 定义集合A,集合A由元素x_1, x_2, ..., x_n组成 $A\cap \complement_{A\cup B}B=A - B$ // 集合A与集合B在A和B的并集中的补集的交集,等于集合A与集合B的差集 效果: 5、常用特殊函数 1)三角函数 $\sin x$, $\cos x$, $\tan x$, $\cot x$ # 注意中间的空格 效果:$\sin x$, $\cos x$, $\tan x$, $\cot x$ 2)反三角函数 $\arcsin x$, $\arccos x$, $\arctan x$ 效果:$\arcsin x$, $\arccos x$, $\arctan x$ 3)双曲函数 $\sinh x$, $\cosh x$, $\tanh x$, $\coth x$ 效果:$\sinh x$, $\cosh x$, $\tanh x$, $\coth x$ 4)对数 $\log_ax$, $\ln x$, $\lg x$ 效果:$\log_ax$, $\ln x$, $\lg x$ 6、常用特殊符号 1)求和 $\sum$, $\sum_{i=1}^n$, $\sum_{i=1}^na_i$, $\sum_{i=1}^{na_i}$ 效果:$\sum$, $\sum_{i=1}^n$, $\sum_{i=1}^na_i$, $\sum_{i=1}^{na_i}$ 2)无穷 $\infty$, $+\infty$, $-\infty$ 效果:$\infty$, $+\infty$, $-\infty$ 3)箭头 $\rightarrow$, $\leftarrow$, $\uparrow$, $\downarrow$ 效果:$\rightarrow$, $\leftarrow$, $\uparrow$, $\downarrow$ 4)极限 $\lim$, $\lim_{x\rightarrow 0}$, $\lim_{x\rightarrow \infty}$ 效果:$\lim$, $\lim_{x\rightarrow 0}$, $\lim_{x\rightarrow \infty}$ 5)积分 $\int$, $\int_a^b$, \$int_{ab}^{cd}$ $ \int_a^b \left( -\frac{1}{x^2} \right) dx = \frac{1}{x}|_a^b = \frac{1}{b} - \frac{1}{a} $ 效果: 6)二重积分 $\iint$, $\iint_D$ 效果:$\iint$, $\iint_D$ 7)三重积分 $\iiint$, $\iiint_D$ # 以此类推,超过四重的积分不可用 效果:$\iiint$, $\iiint_D$ 8)偏导 $\partial$ 效果:$\partial$ 9)梯度 $\nabla$ 效果:$\nabla$ 10)其他 $ \star $ , $ \ast $, $ \oplus $, $ \circ $, $ \bullet $ 效果:$ \star $ , $ \ast $, $ \oplus $, $ \circ $, $ \bullet $ 7、常用希腊字母 $ \alpha $ // 阿尔法 $ \beta $ // 贝塔 $ \gamma $ // 伽马 $ \delta $ // 德尔塔,变化量 $ \epsilon $ // 伊普西龙 $ \zeta $ // 泽塔 $ \eta $ // 伊塔,机械效率 $ \lambda $ // 兰姆达,常数 $ \mu $ // 缪,摩擦系数 $ \pi $ // 派,圆周率 $ \rho $ // 柔,极径 $ \sigma $ // 西格马,方差 $ \tau $ // 陶,力矩 $ \phi $ // 斐,欧拉函数 $ \chi $ // 希,卡方分布 $ \omega $ // 欧米伽,电阻 效果: 更多希腊字母 8、多行公式 1)换行 $$ \begin{aligned} a \\ b,c \\ d \end{aligned} $$ # '\\'是换行 效果: 2)空格 $a \ b $ # '\'表示一空格 效果:$a \ b $ 3)对齐 $$\begin{aligned} [(n+1)!+k]\operatorname{mod}k &=(n+1)!\operatorname{mod}k+k\operatorname{mod}k \\ &=0+0 \\ &=0 \end{aligned}$$ # '&'是对齐 效果: 4)方程组 $$ \begin{cases} x_1+x_2=2 \\ x_1-x_2=0 \end{cases} $$ 效果: 5)矩阵与行列式 $$ \left( \begin{matrix} a &b \\c &d \end{matrix} \right) , \left| \begin{matrix} a &b \\c &d \end{matrix} \right| $$ 效果: 9、表格 $$\begin{array}{c|lcr} n & \text{左} & \text{中} & \text{右} \\ \hline 1 & 0.24 & 1 & 125 \\ 2 & -1 & 189 & -8 \\ 3 & -20 & 2000 & 1+10i \\ \end{array}$$ 效果: \(\begin{array}{c|lcr} n & \text{左} & \text{中} & \text{右} \\ \hline 1 & 0.24 & 1 & 125 \\ 2 & -1 & 189 & -8 \\ 3 & -20 & 2000 & 1+10i \\ \end{array}\)

2025/2/2
articleCard.readMore

Mac应用发布错误:ITMS-91109

问题 今天进行 时光本 macOS端 更新的时候,上传安装包到 App Store Connect 成功后,但是在 App Store Connect 一直无法看到相应的构架包,查看邮件后发现了一个警告邮件:Action needed: The uploaded build for 时光本-日记本·笔记本·记事本·备忘录 has one or more issues. App Store Connect Hello, We noticed one or more issues with a recent delivery for the following app: 时光本-日记本·笔记本·记事本·备忘录 Version 1.2.9 Build 129 Please correct the following issues and upload a new binary to App Store Connect. ITMS-91109: Invalid package contents - The package contains one or more files with the com.apple.quarantine extended file attribute, such as “com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/zh-Hans.lproj/Main.strings”. This attribute isn’t permitted in macOS apps distributed on TestFlight or the App Store. Please remove the attribute from all files within your app and upload again. Apple Developer Relations 在网上查找这个错误提示表明,提交到 App Store 的 macOS 应用程序包中包含一个或多个带有com.apple.quarantine扩展文件属性的文件,比如com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/zh-Hans.lproj/Main.strings,而这种属性是不被允许的。 com.apple.quarantine属性是 macOS 的一个安全特性,用于标记从不受信任的来源获取的文件,例如通过互联网下载或 AirDrop 接收的文件,以防止潜在的恶意软件。要解决这个问题,需要移除应用程序中所有文件的 com.apple.quarantine 属性。 看到网上的结果后知道是安装包中某些文件被添加了com.apple.quarantine 属性,具体可能导致相关错误的原因也很多: 下载来源的影响 Xcode 构建过程问题 系统安全机制 第三方软件干扰 但是具体怎么被添加的就不得而知了,于是就进行了漫长的排查解决过程,最后结果让自己很无奈。 解决思路 步骤一: 解压安装包 首先导出安装包,按照提示路径,找到对应文件,使用 xattr 查找该文件是否有 com.apple.quarantine ,查找结果显示的确有,并且有标记是keka解压软件,因为我是用的keka进行的安装包解压,想着keka的标记应该是该流程添加,并不是原来 com.apple.quarantine 属性添加的直接原因,于是就尝试其他方法解决该问题。 步骤二:更新 Xcode 为什么要更新 Xcode 呢,因为在提交 macOS 版本前先提交了 iOS 版本,iOS 版本提交后,提交成功页面显示有警告内容,提示马上要强制使用 Xcode 16 进行开发发布了,所以就想着是不是当前 Xcode 版本太低了导致的呢,于是就进行了更新操作,经过漫长的下载安装,问题依然存在,使用新的 Mac 用户也不行,只能想其他方法了。 步骤三:操作导出的安装包 Xcode 更新不行,想着提示有具体的安装包错误内容路径,就操作删除相关内容的 com.apple.quarantine 属性,网上也有相应的操作流程xattr -rd com.apple.quarantine /path/to/YourApp.app,操作后继续提交,还是不行,还是同样的错误。 分析这个步骤只是进行简单的安装包操作,没有对安装包里面的内容遍历操作,于是想着进行一下遍历删除操作,但是该操作又无法直接遍历安装包内部内容,于是就进行相应的解压,遍历删除相关属性,操作还算顺利,并且重新打包上传,上传直接就报错了,提示证书问题,确实重新打包没有使用证书,重新查找资料,配置相关重新打包证书,完成打包成功上传,但是苹果接着发来邮件提示证书还是有问题。 Hello, We noticed one or more issues with a recent delivery for the following app: 时光本-日记本·笔记本·记事本·备忘录 Version 1.3.4 Build 134 Please correct the following issues and upload a new binary to App Store Connect. ITMS-90237: The product archive package’s signature is invalid. Ensure that it is signed with your ‘3rd Party Mac Developer Installer’ certificate. Apple Developer Relations 查找 ITMS-90237 问题的解决方法,正确配置相关证书后依然不行,感觉这个方法不太靠谱,虽然能够成功删除 com.apple.quarantine 属性,但是重新将解压的安装包打包十分复杂,想着还是换个方法。 该步骤相关操作代码: import os import subprocess import shutil def remove_quarantine_attribute(pkg_path): extract_dir = os.path.expanduser('~/Desktop/unpacked_pkg') if os.path.exists(extract_dir): try: shutil.rmtree(extract_dir) print(f"已删除现有的 {extract_dir} 目录。") except Exception as e: print(f"删除 {extract_dir} 目录时出错: {e}") return try: os.makedirs(extract_dir) print(f"成功创建 {extract_dir} 目录。") except Exception as e: print(f"创建 {extract_dir} 目录时出错: {e}") return try: # 尝试使用 xar 解压 print("尝试使用 xar 解压 pkg 文件...") result = subprocess.run(['xar', '-xf', pkg_path, '-C', extract_dir], capture_output=True, text=True) if result.returncode != 0: print(f"使用 xar 解压失败: {result.stderr}") return else: print("使用 xar 解压成功。") # 打印解压后的目录内容,便于检查 print(f"解压后的目录 {extract_dir} 内容:") for item in os.listdir(extract_dir): print(item) pkg_inner_dir = os.path.join(extract_dir, 'com.gorpeln.xxx.pkg') payload_path = os.path.join(pkg_inner_dir, 'Payload') if os.path.exists(pkg_inner_dir) and os.path.exists(payload_path): # 进入 Payload 所在目录并进行进一步处理 os.chdir(pkg_inner_dir) try: # 执行 cat Payload | gunzip -dc | cpio -i 操作 print("正在对 Payload 进行进一步解压...") command = 'cat Payload | gunzip -dc | cpio -i' result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=60) if result.returncode == 0: print("Payload 进一步解压成功。") else: print(f"对 Payload 进一步解压时出错: {result.stderr}") except subprocess.TimeoutExpired: print("对 Payload 进一步解压时超时,请检查 Payload 文件是否正常。") except subprocess.CalledProcessError as e: print(f"对 Payload 进一步解压时出错: {e.stderr}") finally: # 切换回上级目录 os.chdir('..') # 遍历所有文件并移除属性 for root, dirs, files in os.walk(extract_dir): for file in files: file_path = os.path.join(root, file) try: subprocess.run(['xattr', '-d', 'com.apple.quarantine', file_path], check=True) print(f"成功移除 {file_path} 的 com.apple.quarantine 属性。") except subprocess.CalledProcessError: # 如果文件没有该属性,忽略错误 pass # 找到 .app 文件 app_path = None payload_extracted_dir = os.path.join(pkg_inner_dir) for item in os.listdir(payload_extracted_dir): if item.endswith('.app'): app_path = os.path.join(payload_extracted_dir, item) break if app_path is None: print("未找到有效的 .app 应用程序包。") return # 签名 .app 包内的所有文件 certificate_name_app = "3rd Party Mac Developer Application: xxxxxxxx (xxxxxxxxx)" find_command = f'find "{app_path}" -type f -exec codesign --force --deep --sign "{certificate_name_app}" \{\{\}} \;' subprocess.run(find_command, shell=True) # 签名 .app 包本身 codesign_command = f'codesign --force --deep --sign "{certificate_name_app}" "{app_path}"' subprocess.run(codesign_command, shell=True) # 重新打包成 pkg 文件 new_pkg_path = pkg_path.replace('.pkg', '_cleaned.pkg') install_path = '/Applications' certificate_name_installer = "3rd Party Mac Developer Installer: xxxxxxxx (xxxxxxxxx)" productbuild_command = [ 'productbuild', '--component', app_path, install_path, '--sign', certificate_name_installer, new_pkg_path ] result = subprocess.run(productbuild_command, capture_output=True, text=True) if result.returncode != 0: print(f"重新打包并签名 pkg 文件时出错:{result.stderr}") else: print(f"处理完成,新的签名 pkg 文件已生成: {new_pkg_path}") except Exception as e: print(f"处理过程中出现错误: {e}") finally: if os.path.exists(extract_dir): try: shutil.rmtree(extract_dir) print(f"已成功删除临时目录 {extract_dir}。") except Exception as e: print(f"删除临时目录 {extract_dir} 时出错: {e}") # 使用示例 pkg_path = '/Users/gorpeln/Desktop/123/GPNotesForMac.pkg' remove_quarantine_attribute(pkg_path) 步骤四:删除 Mac 破解软件 怀疑是电脑上安装的某些破解软件导致的,于是进行删除操作,包括删除了keka解压软件,再次尝试打包,问题依然没有解决。想想上个版本到现在电脑上也没有新安装什么应用,应该不是这个原因,只能想其他方法。 步骤五:删除代码中的相关文件 尝试直接删除提示有问题的文件,该文件在项目中是语言适配文件,做本地化处理的,删除后虽然对应用体验有一定的影响,但是也没有好的办法,只能先删了提交后再慢慢看具体问题了。删除后提交,居然又发邮件提错误了。。。 App Store Connect Hello, We noticed one or more issues with a recent delivery for the following app: 时光本-日记本·笔记本·记事本·备忘录 Version 1.3.8 Build 138 Please correct the following issues and upload a new binary to App Store Connect. ITMS-91109: Invalid package contents - The package contains one or more files with the com.apple.quarantine extended file attribute, such as “com.gorpeln.xxx.pkg/Payload/GPNotesForMac.app/Contents/Resources/MLHudAlertInfo.png”. This attribute isn’t permitted in macOS apps distributed on TestFlight or the App Store. Please remove the attribute from all files within your app and upload again. Apple Developer Relations 看到这个错误后心凉半截,删除了一个文件,但是同样错误又显示了一个新的文件,肯定不能再删除了,因为还不知道到底有多少文件被标记了 com.apple.quarantine 属性,其中被标记的还可能是核心代码,删除后影响项目运行的。并且标记的这个文件应该是一个第三方库文件,想着应该是第三方库有问题,输入搜索后没有发现对应文件,猜想可能是pod导入导致的错误。 步骤五:固定和更新第三方库 查看 Podfile 文件中第三方库都没有固定版本,想着可能是第三方库更新到新版本后出现了相关问题,于是就设置了所有库的对应版本并进行更新,提交上传,问题依旧。迷茫了,不知道该怎么办了。 步骤六:降级 Xcode 版本 脑子一昏,想着是不是 Xcode 版本高的问题,用比较旧的版本会不会好呢,于是就下载安装了。显而易见,并不会。因为在app上个版本到现在我除了这次升级,并没有操作 Xcode 版本,正常不会是 Xcode 版本问题的。 步骤七:删除源码中的相关属性 不知不觉,这个问题搞了一天了。没想法了,想着还是直接删除提示的对应文件吧,再次搜索提示的MLHudAlertInfo.png,居然看到了这个相关文件,进入查看具体内容,查找在Finder中的内容,确实存在MLHudAlertInfo.png文件。那这就好办了呀,直接查看这个文件是否包含 com.apple.quarantine 属性就行了呀,使用 xattr 查看后,的确存在,我感觉有点无语,基本确认是源码问题,直接使用 xattr 遍历删除项目文件夹下所有文件,重新提交上传,问题完美解决了。 总结 具体该问题是怎么导致的呢? 静下来想想,无轮提示的 Main.strings 还是 MLHudAlertInfo.png,都是最近甚至多个版本都没有修改过,甚至 MLHudAlert 是导入后只使用,完全没有修改过的,再根据过程中提示到的 keka 解压软件标记,我推测大概率文件相关 com.apple.quarantine 的属性在相关文件下载后使用 keka 解压的时候就已经被标记了,跟随项目多个版本,但由于苹果官方最近收紧对 com.apple.quarantine 属性的检测导致构架包一直无法通过。 总结这次问题,为什么耗费了这么长时间呢? macOS 软件开发较少,可参考的资料也较少,只能自己摸索 解决问题思路问题:一直想的是前面的版本没问题,这次不行,应该是这次上次到这次提交之间修改某些文件导致的,没想到被苹果背刺了。 第一次查找MLHudAlertInfo时,可能是因为手动输入错误导致没有成功搜索到相关文件 最后,如果感兴趣可以去查看使用一下【时光本】应用,是一款专注效率与记录的笔记工具。可以帮助你整理各种信息,包括便签、清单、图片、纪念日、地址、链接、银行卡、名片、账号、密码等

2025/1/18
articleCard.readMore

春节灯笼

想为你的博客增添浓浓的春节氛围吗?快来了解我们的独特功能!设定时间后,两个喜庆的灯笼将准时在博客右上角亮起,灯笼上 “新春快乐” 的祝福熠熠生辉,瞬间让你的博客充满节日的欢乐与温馨。这不仅是装饰,更是为访客送上真挚祝福的创意方式,快来探索如何让你的博客在春节别具一格吧。 效果 方法 <link rel="stylesheet" type="text/css" href="../assets/css/deng.css"> 注意:将文件下载到自己博客使用 html代码 <!-- 春节灯笼 --> <div style="display:none;" id="lanterns"> <div class="deng-box"> <div class="deng"> <div class="xian"></div> <div class="deng-a"> <div class="deng-b"> <div class="deng-t">快乐</div> </div> </div> <div class="shui shui-a"> <div class="shui-c"></div> <div class="shui-b"></div> </div> </div> </div> <div class="deng-box1"> <div class="deng"> <div class="xian"></div> <div class="deng-a"> <div class="deng-b"> <div class="deng-t">新春</div> </div> </div> <div class="shui shui-a"> <div class="shui-c"></div> <div class="shui-b"></div> </div> </div> </div> </div> <!-- 春节灯笼 --> <script> document.addEventListener("DOMContentLoaded", function() { // 获取当前日期 var currentDate = new Date(); // 定义开始日期和结束日期 var startDate = new Date('2025-01-22');//腊月廿三 var endDate = new Date('2025-02-12');//正月十五 // 获取 div 元素 var specialDiv = document.getElementById('lanterns'); // 检查当前日期是否在指定的日期范围内 if (currentDate >= startDate && currentDate <= endDate) { // 如果在范围内,则显示 div specialDiv.style.display = 'block'; } else { // 否则,隐藏 div specialDiv.style.display = 'none'; } }); </script>

2024/12/21
articleCard.readMore

彩带、樱花背景

飘舞的彩带,似灵动的画笔,在空中勾勒出绚丽色彩,为世界添一抹活力。 效果 方法 <!-- 樱花背景 --> <script type="text/javascript" src="/assets/js/sakura.js"></script> <!-- 丝带背景 --> <script size="150" alpha="0.3" zindex="-2" src="/assets/js/ribbon.min.js"></script> 注意:将文件下载到自己博客使用 https://gorpeln.top/assets/js/sakura.js https://gorpeln.top/assets/js/ribbon.min.js

2024/12/21
articleCard.readMore

节日倒计时与时间进度条

不仅能够精准显示距离特定节日的剩余时间,满足用户对重要节庆的期待与关注,还能直观展示今日、本周、本月乃至本年已悄然流逝的时间比例,帮助用户实时洞察时间的流转,让每一刻都能在时间的刻度中清晰呈现,实现对时间的高效感知与管理。 效果 方法 <div class="countdownNav" id="countdownNav"> <div class="card-widget card-countdown"> <div class="item-headline"><i></i><span></span></div> <div class="item-content"> <div class="cd-count-left"> <span class="cd-text">距离</span> <span class="cd-name" id="eventName">节日</span> <span class="cd-time" id="daysUntil">000</span> <span class="cd-date" id="eventDate"></span> </div> <div id="countRight" class="cd-count-right"> <div id="countRight" class="cd-count-right"> <div class="cd-count-item"> <div class="cd-item-name">今日</div> <div class="cd-item-progress"></div> </div> <div class="cd-count-item"> <div class="cd-item-name">本周</div> <div class="cd-item-progress"></div> </div> <div class="cd-count-item"> <div class="cd-item-name">本月</div> <div class="cd-item-progress"></div> </div> <div class="cd-count-item"> <div class="cd-item-name">本年</div> <div class="cd-item-progress"></div> </div> </div> </div> </div> </div> </div> 2、 countdown-1.js const CountdownTimer = (() => { const config = { targetDate: "2025-10-01", targetName: "国庆", units: { day: { text: "今日", unit: "小时" }, week: { text: "本周", unit: "天" }, month: { text: "本月", unit: "天" }, year: { text: "本年", unit: "天" } } }; const calculators = { day: () => { const hours = new Date().getHours(); return { remaining: 24 - hours, percentage: (hours / 24) * 100 }; }, week: () => { const day = new Date().getDay(); const passed = day === 0 ? 6 : day - 1; return { remaining: 6 - passed, percentage: ((passed + 1) / 7) * 100 }; }, month: () => { const now = new Date(); const total = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate(); const passed = now.getDate() - 1; return { remaining: total - passed, percentage: (passed / total) * 100 }; }, year: () => { const now = new Date(); const start = new Date(now.getFullYear(), 0, 1); const total = 365 + (now.getFullYear() % 4 === 0 ? 1 : 0); const passed = Math.floor((now - start) / 86400000); return { remaining: total - passed, percentage: (passed / total) * 100 }; } }; function updateCountdown() { const elements = ['eventName', 'eventDate', 'daysUntil', 'countRight'] .map(id => document.getElementById(id)); if (elements.some(el => !el)) return; const [eventName, eventDate, daysUntil, countRight] = elements; const now = new Date(); const target = new Date(config.targetDate); eventName.textContent = config.targetName; eventDate.textContent = config.targetDate; daysUntil.textContent = Math.round((target - now.setHours(0, 0, 0, 0)) / 86400000); countRight.innerHTML = Object.entries(config.units) .map(([key, { text, unit }]) => { const { remaining, percentage } = calculators[key](); return ` <div class="cd-count-item"> <div class="cd-item-name">${text}</div> <div class="cd-item-progress"> <div class="cd-progress-bar" style="width: ${percentage}%; opacity: ${percentage}"></div> <span class="cd-percentage ${percentage >= 46 ? 'cd-many' : ''}">${percentage.toFixed(2)}%</span> <span class="cd-remaining ${percentage >= 60 ? 'cd-many' : ''}"> <span class="cd-tip">还剩</span>${remaining}<span class="cd-tip">${unit}</span> </span> </div> </div> `; }).join(''); } let timer; const start = () => { updateCountdown(); timer = setInterval(updateCountdown, 600000); }; ['pjax:complete', 'DOMContentLoaded'].forEach(event => document.addEventListener(event, start)); document.addEventListener('pjax:send', () => timer && clearInterval(timer)); return { start, stop: () => timer && clearInterval(timer) }; })(); 优化后代码,实现倒计时包含多个时间,自动识别最近的期望时间,如果现在时间超过了所有的期望时间,就以最后一个期望时间为准。两个js二选一即可。 const CountdownTimer = (() => { const config = { events: [ { targetDate: "2025-04-04", targetName: "清明节" }, { targetDate: "2025-05-01", targetName: "劳动节" }, { targetDate: "2025-05-31", targetName: "端午节" }, { targetDate: "2025-10-01", targetName: "国庆节" }, { targetDate: "2026-02-17", targetName: "春节" } ], units: { day: { text: "今日", unit: "小时" }, week: { text: "本周", unit: "天" }, month: { text: "本月", unit: "天" }, year: { text: "本年", unit: "天" } } }; const calculators = { day: () => { const hours = new Date().getHours(); return { remaining: 24 - hours, percentage: (hours / 24) * 100 }; }, week: () => { const day = new Date().getDay(); const passed = day === 0 ? 6 : day - 1; return { remaining: 6 - passed, percentage: ((passed + 1) / 7) * 100 }; }, month: () => { const now = new Date(); const total = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate(); const passed = now.getDate() - 1; return { remaining: total - passed, percentage: (passed / total) * 100 }; }, year: () => { const now = new Date(); const start = new Date(now.getFullYear(), 0, 1); const total = 365 + (now.getFullYear() % 4 === 0 ? 1 : 0); const passed = Math.floor((now - start) / 86400000); return { remaining: total - passed, percentage: (passed / total) * 100 }; } }; function getNextEvent() { const now = new Date(); for (let i = 0; i < config.events.length; i++) { const eventDate = new Date(config.events[i].targetDate); if (eventDate > now) { return config.events[i]; } } return config.events[config.events.length - 1]; } function updateCountdown() { const elements = ['eventName', 'eventDate', 'daysUntil', 'countRight'] .map(id => document.getElementById(id)); if (elements.some(el => !el)) return; const [eventName, eventDate, daysUntil, countRight] = elements; const now = new Date(); const nextEvent = getNextEvent(); const target = new Date(nextEvent.targetDate); eventName.textContent = nextEvent.targetName; eventDate.textContent = nextEvent.targetDate; daysUntil.textContent = Math.round((target - now.setHours(0, 0, 0, 0)) / 86400000); countRight.innerHTML = Object.entries(config.units) .map(([key, { text, unit }]) => { const { remaining, percentage } = calculators[key](); return ` <div class="cd-count-item"> <div class="cd-item-name">${text}</div> <div class="cd-item-progress"> <div class="cd-progress-bar" style="width: ${percentage}%; opacity: ${percentage}"></div> <span class="cd-percentage ${percentage >= 46 ? 'cd-many' : ''}">${percentage.toFixed(2)}%</span> <span class="cd-remaining ${percentage >= 60 ? 'cd-many' : ''}"> <span class="cd-tip">还剩</span>${remaining}<span class="cd-tip">${unit}</span> </span> </div> </div> `; }).join(''); } let timer; const start = () => { updateCountdown(); timer = setInterval(updateCountdown, 600000); }; ['pjax:complete', 'DOMContentLoaded'].forEach(event => document.addEventListener(event, start)); document.addEventListener('pjax:send', () => timer && clearInterval(timer)); return { start, stop: () => timer && clearInterval(timer) }; })(); 3、 style.css /*倒计时*/ .countdownNav { margin-top: 1em; background-color: var(--box-color-light); -moz-border-radius: 10px; -webkit-border-radius: 10px; overflow: hidden; width: 100%; } .card-countdown { margin: 0.75em 1.5em; } .card-countdown .item-content { display: flex; } .cd-count-left { position: relative; display: flex; flex-direction: column; margin-right: 0.8rem; line-height: 1.5; align-items: center; justify-content: center; } .cd-count-left .cd-text { font-size: 14px; } .cd-count-left .cd-name { font-weight: bold; font-size: 18px; } .cd-count-left .cd-time { font-size: 30px; font-weight: bold; color: #dad9e6; } .cd-count-left .cd-date { font-size: 12px; opacity: 0.6; } .cd-count-left::after { content: ""; position: absolute; right: -0.8rem; width: 2px; height: 80%; background-color: #dad9e6; opacity: 0.5; } .cd-count-right { flex: 1; margin-left: .8rem; display: flex; flex-direction: column; justify-content: space-between; } .cd-count-item { display: flex; flex-direction: row; align-items: center; height: 24px; } .cd-item-name { font-size: 14px; margin-right: 0.8rem; white-space: nowrap; } .cd-item-progress { position: relative; display: flex; flex-direction: row; align-items: center; justify-content: space-between; height: 100%; width: 100%; border-radius: 8px; background-color: var(--background-color-light); overflow: hidden; } .cd-progress-bar { height: 100%; border-radius: 8px; background-color: #dad9e6; } .cd-percentage, .cd-remaining { position: absolute; font-size: 12px; margin: 0 6px; transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; } .cd-many { color: #fff; } .cd-remaining { opacity: 0; transform: translateX(10px); } .card-countdown .item-content:hover .cd-remaining { transform: translateX(0); opacity: 1; } .card-countdown .item-content:hover .cd-percentage { transform: translateX(-10px); opacity: 0; }

2024/12/21
articleCard.readMore

友链自动监测

前言 管理友链时采取手动点击检验的方式,随着时间的推移,友链数量逐渐增加,这一做法显然已不再高效。于是就需要写了一项类似API的功能,输出所有友链数据的可达性。 功能概览 github action自动定时检测友链状态,结果输出到page分支下的result.json。 友链状态展示页面,可以部署到zeabur或者vercel,加速api访问速度。 为确保兼容性,实现了两种检测方案: 非兼容:使用格式文件动态读取友链内容,实现功能,友链列表自动实时性更新。 兼容:使用TXT存储所有友链信息,兼容性好,适合所有站点,但是添加友链后可能需要手动更新文件。 API访问数据,api包含数据包括可达链接、不可达链接、可达链接数目、不可达链接数目、更新时间戳,其中链接中包含站点名称和地址,便于前端部署。 测试脚本使用python,使用Request包的get和head两种检测方式检测,尽可能减少误判概率。 前端采用本地缓存,减少api调用次数,缓存半个小时刷新,不影响实时性。 预览 博客中预览:https://friends.gorpeln.top/ 其他 部署教程:参考文章尾部的参考链接 常见问题

2024/11/8
articleCard.readMore