2025-07-19 20:06:00
上次骑行是在6月24日,转眼半个月过去了,忍不住又开始怀念行驶在路上的感觉。
7月17日 清晨,一时心血来潮,换上骑行服,简单带了件便装,便推车下楼。目的地:杭州。
2025-07-10 02:26:00
晚上逛 #UNTAG 发现一个有意思的项目 —— RSS.Beauty
一款开源 RSS 美化工具。支持 RSS 2.0 和 Atom 1.0 格式,可将原始订阅源转换加以渲染。
此外,根据项目文档,作者还提供了四种食用方法,包括:
只需提供 RSS 地址,即可通过服务端转换美化:
https://rss.lhasa.icu/rss?url=https://lhasa.icu/rss.xml
下载模板:
# RSS 2.0
wget https://rss.lhasa.icu/rss.xsl
# Atom 1.0
wget https://rss.lhasa.icu/atom.xsl
在 RSS 文件头部添加如下声明:
<?xml-stylesheet href="/style/rss.xsl" type="text/xsl"?>
适合不想托管样式文件,可以将样式直接内嵌进 RSS 文件中
步骤如下:
https://rss.lhasa.icu
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="data:text/xsl;base64,PD94bWw..." type="text/xsl"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
扒拉下来,启动:
docker pull ghcr.io/ccbikai/rss.beauty:main
docker run -d --name rss-beauty -p 4321:4321 ghcr.io/ccbikai/rss.beauty:main
Nginx 反代:
# /etc/nginx/conf.d/rss.conf
server {
listen 80;
server_name rss.lhasa.icu;
location / {
proxy_pass http://127.0.0.1:4321;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# 重载 Nginx
sudo nginx -t && sudo nginx -s reload
Node.js 部署就不赘述了,天天接触,不新鲜。
我小改了一下配色,不过目前还不支持输出文章配图和全文。等明天下班再来折腾吧。
2025-06-25 20:53:00
农历五月二十九,二十五岁生日到了
一边上班,一边创业,这样的活法让我骑车的时间越来越少。这不是我想要的状态,但为了三十岁之前出发环球骑行的梦想,我没有办法。
前几日申请了调休,可算睡个安稳觉。早上睡到十点钟,吃完午饭,我觉得今天不能再碰电脑了,我现在很想出去骑车。可只剩下一个下午的时间了,太远去不了,近的没意思。打开高德地图扒拉半天,去横店。
2025-06-12 13:07:00
我们生命中最美好的时刻,并非是那些接受给予、放松享受的时刻,而是那些为了完成一件困难而有价值的事情,自愿将身心发挥到极限的时刻。
像咱这种人,我觉得只有一种死法 —— 猝死。
从6月10日早上九点,一直干到6月11日下午一点半,眼睛都没怎么合过。足足三十多个小时,一口气用Go,把整个企业级的RESTful API给撸完了!
这可不是简单的CtrlCV,为了支持硬件,软件下了点功夫:MySQL做了主从复制架构 + Redis缓存 + 负载均衡。支持 Docker 一键部署。还没做压力测试,自我感觉并发二三十万如喝水。
上午测完接口,兴奋的饭都顾不上吃,更别说休息了。立马开始写文档。接口文档、数据库文档…
先用Cursor生成基本文档,再改改。每份大概3000字800行左右,足足写了八份。
还有商业计划书的思维导图,2M的大小,不放大到190%字体都看不清楚,内容密度可想而知。
真的要特别感谢 Cursor,这AI工具真是赶上了好时代!要是没有它,就凭这些工作量,外包团队没一个月起步都别想搞定。
不敢想象,我竟然在三十多个小时里,就搞定了这么多事,而且落地质量高!
接下来,还剩微信小程序对接接口,以及和硬件的联调。先给这些小卡拉米放放假,我要好好休息一个下午。
此刻,精力充沛去吃个早饭骑骑车。
实际上,我做的这些事情有没有公司都能推进,但没办法,就为了微信小程序后续的企业认证需求.
在浙江线上办理,全程可以不出门。
項目 | 內容 |
---|---|
企业名称 | 浪泳科技(义乌)有限公司 |
自主申报预选号 | [2025]****** |
拟登记机关 | 义乌市市场监督管理局 |
住所所在地 | ****** |
生产经营地 | ****** |
法定代表人 | ****** |
从业人数 | 2 |
联系电话 | 186****7426 |
邮政编码 | 322000 |
注册资本 | 0.001万元 |
企业类型 | 有限责任公司(自然人独资) |
行业 | (6513) 应用软件开发 |
是否一照多址 | 否 |
经营范围 | 写不下 |
提交申请还不到俩小时,我就收到了驳回通知:
您申报的“浪泳科技(义乌)有限公司”设立登记申请,预审未通过,请补齐材料后再次申报。
注册资本一元行不通,调整到了一万
经营范围调整为:
2025-06-02 23:27:00
版本:v1.1.1
经过两个月的偷懒,EasyFill 迎来了 v1.1.1 版本的重大更新。这次更新主要在匹配算法上进行大幅度优化,全面提升匹配效率
我发现有些评论系统通过 Shadow DOM 来实现封装,导致 v1.0 版本无法识别 Shadow DOM 生成的表单。在 v1.1.1 中,EasyFill 新增了对动态创建的 Shadow DOM 的完整支持。
function traverseShadowDOM(root: Document | ShadowRoot | Element) {
const inputs = root.querySelectorAll('input, textarea');
elements.push(...Array.from(inputs));
const allElements = root.querySelectorAll('*');
allElements.forEach(element => {
if (element.shadowRoot) {
logger.info('发现 Shadow DOM,正在遍历', {
tagName: element.tagName,
shadowRootMode: element.shadowRoot.mode
});
traverseShadowDOM(element.shadowRoot);
}
});
}
在 v1.0 版本,EasyFill 只支持 name 字段识别。为了更加准确的匹配字段,引入了全新三种字段识别方式,确保在各种稀奇古怪的表单都可以识别:
通过分析输入框的 placeholder
属性来识别字段类型:
<input placeholder="请输入您的姓名" />
<input placeholder="邮箱地址" />
<input placeholder="个人网站" />
基于 HTML5 标准的 type
属性进行智能识别:
<input type="email" />
<input type="url" />
<input type="text" name="username" />
通过元素的 id
属性进行精确匹配:
<input id="author" />
<input id="email" />
<input id="website" />
匹配策略:
inputs.forEach((input) => {
const typeAttr = (input.getAttribute("type") || "").toLowerCase();
const nameAttr = (input.getAttribute("name") || "").toLowerCase();
const idAttr = (input.getAttribute("id") || "").toLowerCase();
let valueToSet = ""; // 要填充的值
let matchedBy = ""; // 匹配方式(id, name, type)
let fieldType = ""; // 字段类型(name, email, url)
// 匹配 URL 字段
if (keywordSets.url.has(nameAttr) || keywordSets.url.has(`#${idAttr}`)) {
valueToSet = url;
matchedBy = keywordSets.url.has(`#${idAttr}`) ? "id" : "name";
fieldType = "url";
} else if (typeAttr === "url" && url) {
valueToSet = url;
matchedBy = "type";
fieldType = "url";
}
// 匹配 Email 字段
else if (keywordSets.email.has(nameAttr) || keywordSets.email.has(`#${idAttr}`)) {
valueToSet = email;
matchedBy = keywordSets.email.has(`#${idAttr}`) ? "id" : "name";
fieldType = "email";
} else if (typeAttr === "email" && email) {
valueToSet = email;
matchedBy = "type";
fieldType = "email";
}
// 匹配 Name 字段
else if ((keywordSets.name.has(nameAttr) || keywordSets.name.has(`#${idAttr}`)) && name) {
valueToSet = name;
matchedBy = keywordSets.name.has(`#${idAttr}`) ? "id" : "name";
fieldType = "name";
}
// 没有匹配上就跳过
if (!valueToSet) return;
// 设置值并触发事件
(input as HTMLInputElement).value = valueToSet;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
// 记录日志
logger.info('填充表单字段', {
name: nameAttr || "",
id: idAttr || "",
type: typeAttr || "",
matchedBy,
valueToSet,
inShadowDOM: isInShadowDOM(input)
});
});
v1.1.1 版本允许用户完全自定义关键字数据源。
该源来自我的腾讯云 COS,且由腾讯云境内 CDN 加速,基本上无延迟:
https://cos.lhasa.icu/EasyFill/keywords.json
自定义数据源格式示例:
{
"name": ["name", "author", "username", "昵称", "姓名"],
"email": ["email", "mail", "邮箱", "电子邮件"],
"url": ["url", "website", "blog", "网站", "博客"]
}
实现了基于 HTTP 标准的智能缓存系统,大幅减少不必要的网络请求:
ETag 和 Last-Modified 支持:
if (etag && !forceSync) {
headers['If-None-Match'] = etag;
}
if (lastModified && !forceSync) {
headers['If-Modified-Since'] = lastModified;
}
实现 Markdown 内容的持久化机制:
const fetchMarkdown = async (url: string) => {
try {
// 检查 localStorage 是否已有缓存
const cachedMarkdown = localStorage.getItem(url);
if (cachedMarkdown) {
logger.info(`从缓存加载 Markdown 文件: ${url}`);
return cachedMarkdown;
}
// 如果没有缓存,从网络加载
const response = await fetch(url);
const markdown = await response.text();
// 将加载的内容存入 localStorage
localStorage.setItem(url, markdown);
return marked(markdown);
} catch (error) {
logger.error(`加载 Markdown 文件失败: ${url}`, error);
}
};
实现 Markdown 内容的异步并行加载:
const loadContent = async () => {
const [aboutAuthor, recommendedPlugins, updateLog, privacyPolicy] = await Promise.all([
fetchMarkdown('/markdowns/about-author.md'),
fetchMarkdown('/markdowns/recommended-plugins.md'),
fetchMarkdown('/markdowns/UpdateLog.md'),
fetchMarkdown('/markdowns/privacy-policy.md'),
]);
setAboutAuthorContent(aboutAuthor);
setRecommendedPluginsContent(recommendedPlugins);
setUpdateLogContent(updateLog);
setPrivacyPolicyContent(privacyPolicy);
};
EasyFill v1.1.1 实现了单例日志系统,支持 INFO、WARN、ERROR 三个级别:
export enum LogLevel {
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR',
}
日志系统能够根据运行环境自动调整输出策略:
public configureByEnvironment(): Logger {
const isProd = typeof process !== 'undefined' && process.env && process.env.NODE_ENV === 'production';
if (isProd) {
// 生产环境:只显示警告和错误
this.setLevel(LogLevel.WARN);
} else {
// 开发环境:显示所有日志,并启用彩色和时间戳
this.setLevel(LogLevel.INFO)
.useColors(true)
.showTimestamp(true);
}
return this;
}
生产状态下,日志默认关闭。所以,增加了命令调试:
// 启用日志系统
EasyFillLogger.enable()
// 关闭日志系统
EasyFillLogger.disable()
// 查看当前状态
EasyFillLogger.status()
命令绑定在全局 window 对象上,重启浏览器仍有效。
在浏览器扩展环境中,使用 chrome.storage.local 来存储,在普通网页环境中,使用 localStorage。
一样的是都用 easyfill_logger_enabled 这个键来存储
支持灵活的链式配置:
logger
.setLevel(LogLevel.INFO)
.useColors(true)
.showTimestamp(true)
.setPrefix('[EasyFill]')
.setPrefixColor('color: #4CAF50; font-weight: bold');
配置选项:
v1.1.1 版本对隐私权政策进行了全面更新:
主要更新内容:
新增了直观的同步设置界面,轻松管理数据同步:
EasyFill 的每一次进步都离不开用户的支持和反馈。特别感谢:Mainbranch 的反馈与支持
如果您觉得 EasyFill 对您有帮助,欢迎:
EasyFill v1.1.1 现已在 Chrome 应用商店正式发布,您可以:
EasyFill - 简易填充,让每一次评论更自然,与你的博友互动无缝连接