仓库地址:

GitHub - faryhuo/backtrader

界面预览

主要功能页面

[开源自荐] 基于 Backtrader 的量化交易 回测 / 交易 系统5
首页 [开源自荐] 基于 Backtrader 的量化交易 回测 / 交易 系统1
运行策略
[开源自荐] 基于 Backtrader 的量化交易 回测 / 交易 系统3
策略管理 [开源自荐] 基于 Backtrader 的量化交易 回测 / 交易 系统4
回测历史
[开源自荐] 基于 Backtrader 的量化交易 回测 / 交易 系统2
组合回测

功能特性

核心功能

  • 策略回测系统 - 基于 Backtrader 引擎的完整回测框架

  • 实盘 / 模拟交易 - CCXT(加密货币)和 IBKR(传统证券)适配器支持

  • Walk-Forward 参数优化 - 训练 / 验证集分离,过拟合检测

  • 在线策略编辑器 - Monaco Editor 在线编写和调试策略代码,支持语法高亮

  • 策略沙箱安全执行 - 支持 subprocess/docker 隔离模式,防止恶意代码执行

  • 多语言支持 - 中文 / 英文国际化 (i18n),完整的翻译覆盖

  • AI 智能分析 - OpenAI 集成,自动分析回测结果并提供优化建议

  • WebSocket 实时推送 - 交易状态、订单、持仓、日志实时更新

  • 多会话管理 - 支持多个策略并发运行,独立管理

  • 认证授权 - 可选的 Logto JWT 认证集成

  • 凭证加密存储 - 数据库凭证使用 Fernet 加密,支持 UI 配置

  • 组合回测 - 支持多策略、多品种组合回测分析

快速开始

前置要求

  • Python 3.11 或更高版本

  • Node.js 18 或更高版本

  • (可选) Docker & Docker Compose

方式 1:一键启动(开发模式)

克隆项目后,使用快速启动脚本:


git clone https://github.com/faryhuo/backtrader.git

cd backtrader

Windows 用户:

 # 完整构建(安装依赖 + 构建前端 + 复制静态资源)

build.bat

# 开发模式(同时启动后端和前端开发服务器)

start_dev.bat

# 仅启动后端服务器(生产模式)

start_server.bat

macOS / Linux 用户:

 # 添加执行权限(首次运行) chmod +x *.sh

# 完整构建(安装依赖 + 构建前端 + 复制静态资源)

./build.sh

# 开发模式(同时启动后端和前端开发服务器)

./start_dev.sh

# 仅启动后端服务器(生产模式)

./start_server.sh

方式 2:Docker 部署


git clone https://github.com/faryhuo/backtrader.git

 cd backtrader && bash docker-build-optimized.sh

# 后台运行

docker-compose up -d


📌 转载信息
转载时间:
2026/1/3 11:47:27

可基于 VLM,也可以直接基于 UI 树感知
1
[开源自荐] 基于 VLM/UI 感知树的 PolarisDesk - AI 桌面助手【求】2

一款集成多种主流 AI 服务的桌面助手应用,致力于让 AI 真正融入你的日常工作流程,成为随时可用的智能生产力伙伴。


主要功能

AI 对话集成

  • 支持 OpenAI、Claude、Google、DeepSeek10+ AI 服务商
  • 一个应用统一管理所有 AI 账号,告别频繁切换平台的繁琐操作

智能交互

  • 人设预设系统:快速切换不同 AI 角色,适配多种使用场景
  • 悬浮窗口模式:随时唤起 AI 助手,不打断当前工作
  • 自然语言控制系统命令:用 “说话” 的方式完成复杂操作

文档处理

  • 支持 PDF / PPT / Word 等多种文档格式解析
  • 智能截图功能:AI 可直接理解并分析截图中的内容

独特能力

  • UI 树感知技术(macOS):自动识别当前窗口的界面结构,让 AI 理解你正在使用的应用与操作上下文
  • 本地化设计:完整支持中文界面,并深度适配国内主流 AI 服务商


适用场景

适用于 程序开发、文档写作、学习研究、日常办公 等多种需要 AI 深度辅助的工作场景。


📌 转载信息
原作者:
skylertong
转载时间:
2026/1/3 11:46:03

通过node.js环境搭建tuic节点,非常简单

使用discord授权登录,左上角-订单,免费购买一款法国容器,选择node.js,每4天续期一次

打开 tuic-hy2-node.js-python/tuic+vless reality-copy at main · eishare/tuic-hy2-node.js-python · GitHub

创建两个文件粘贴代码,不需要修改ip端口,会自动识别,控制台启动即可获取链接

不需要保活,我这个已经活一天多了。

延迟200ms,速度也还可以

另一款容器,每天看广告领10个金币,10金币=一周免费,一个月40金币,也就是每个月看4天广告就行,搭建节点操作相同,这里就不赘述了。

如果觉得可以,麻烦大家点点赞


📌 转载信息
原作者:
buyi
转载时间:
2026/1/2 23:48:23

大家好!最近在安卓 Edge 浏览器上用 Tampermonkey 开发了一个简单实用的油猴脚本,专门针对视频播放的痛点:长按视频区域实现倍速调节(类似夸克浏览器的体验)。脚本支持长按左侧从 1× 开始、右侧从 2× 开始,上滑加速、下滑减速,松手恢复原速。还加了小提示和轻振动反馈。

这个脚本基于我跟 AI 助手多次迭代优化,已经基本稳定可用,能兼容大部分 H5 视频网站(B站、YouTube、腾讯视频等)。但还有两个小问题没彻底解决:全屏模式下速度提示消失与 HTML5视频播放器增强脚本(h5player)冲突导致倍速被覆盖。求大佬们帮忙修复或优化!如果能完美兼容 h5player 和全屏提示永可见,就太完美了。

脚本功能亮点

  • 长按触发:左侧从 1×、右侧从 2×,300ms 内触发,轻振动提醒。
  • 滑动调节:上滑 +0.25×/步,下滑 -0.25×/步,范围 0.25~16×。
  • 提示显示:右上角小半透明胶囊,800ms 淡出。
  • 防误触:不会弹出系统复制/分享菜单。
  • 兼容性:保留原生进度条,全屏/非全屏都可用(除提示问题外)。
  • 适用场景:追剧、学习视频倍速神器,远超 Edge 原生播放器。

安装与使用

  1. 在安卓 Edge 安装 Tampermonkey(扩展商店搜索)。
  2. 新建脚本,复制下面代码保存启用。
  3. 打开视频页面,长按视频区域测试。
  4. 可与 h5player 同时用,但有冲突时建议临时关闭 h5player。

完整脚本代码(版本 2.5,作者:Taocrypt)

// ==UserScript== // @name         手机长按倍速播放增强 // @namespace    http://tampermonkey.net/ // @version      2.5 // @description  安卓专用:长按左侧从1×、右侧从2×开始,上滑加速、下滑减速,松手恢复。 // @author       Taocrypt // @match        *://*/* // @grant        none // @run-at       document-start // @noframes // ==/UserScript==

(function () {
    'use strict';

    let currentVideo = null;
    let isLongPressing = false;
    let originalSpeed = 1.0;
    let baseSpeed = 1.0;
    let currentSpeed = 1.0;
    let startY = 0;
    const STEP = 0.25;
    const MIN_SPEED = 0.25;
    const MAX_SPEED = 16.0;
    const LONG_PRESS_TIME = 300;
    const SLIDE_THRESHOLD = 25;

    let originalSetter = null;
    let hijacked = false;

    // 小半透明右上角提示(全屏永可见) function showSpeedTip(speed) {
        let tip = document.getElementById('my-longpress-tip');
        const root = document.documentElement || document.body;

        if (!tip || !root.contains(tip)) {
            if (tip) tip.remove();
            tip = document.createElement('div');
            tip.id = 'my-longpress-tip';
            tip.style.cssText = `
position: fixed !important;
top: 20px !important; right: 20px !important;
background: rgba(0,0,0,0.6) !important;
color: #fff !important;
padding: 6px 12px !important;
border-radius: 16px !important;
font-size: 16px !important;
font-weight: bold !important;
z-index: 2147483647 !important; /* 最高z-index */
pointer-events: none !important;
transition: opacity 0.4s !important;
backdrop-filter: blur(4px) !important;
`
; root.appendChild(tip); } tip.textContent = speed.toFixed(2) + '×'; tip.style.opacity = '0.9'; clearTimeout(tip.hideTimer); tip.hideTimer = setTimeout(() => tip.style.opacity = '0', 800); } // 劫持 playbackRate(最高优先级) function enableHijack() { if (hijacked) return; originalSetter = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate')?.set; if (!originalSetter) return; Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', { set: function(value) { if (isLongPressing && this === currentVideo) { currentSpeed = value; showSpeedTip(value); } return originalSetter.call(this, value); }, configurable: true }); hijacked = true; } // 恢复原生(脚本卸载或出错时保险) function disableHijack() { if (!hijacked || !originalSetter) return; Object.defineProperty(HTMLMediaElement.prototype, 'playbackRate', { set: originalSetter, configurable: true }); hijacked = false; } function attach(video) { if (video._myLongPressAttached) return; video._myLongPressAttached = true; let pressTimer = null; video.addEventListener('touchstart', e => { if (e.touches.length !== 1 || isLongPressing) return; e.stopImmediatePropagation(); const touch = e.touches[0]; const rect = video.getBoundingClientRect(); if (rect.width < 30 || rect.height < 30) return; const x = touch.clientX - rect.left; const width = rect.width; startY = touch.clientY; if (pressTimer) clearTimeout(pressTimer); pressTimer = setTimeout(() => { isLongPressing = true; currentVideo = video; originalSpeed = video.playbackRate || 1.0; baseSpeed = (x > width * 0.5) ? 2.0 : 1.0; currentSpeed = baseSpeed; enableHijack(); // 开启最高优先级劫持 video.playbackRate = currentSpeed; // 触发显示 showSpeedTip(currentSpeed); if (navigator.vibrate) navigator.vibrate([20, 30, 20]); // 轻柔振动 }, LONG_PRESS_TIME); }, { passive: false }); video.addEventListener('touchmove', e => { if (!isLongPressing || !currentVideo) return; const touch = e.touches[0]; const deltaY = startY - touch.clientY; const steps = Math.floor(Math.abs(deltaY) / SLIDE_THRESHOLD) * (deltaY > 0 ? 1 : -1); if (steps !== 0) { currentSpeed = Math.max(MIN_SPEED, Math.min(MAX_SPEED, currentSpeed + steps * STEP)); video.playbackRate = currentSpeed; // 通过劫持setter自动显示提示 startY = touch.clientY; } e.stopImmediatePropagation(); }, { passive: true }); video.addEventListener('touchend', () => { if (pressTimer) clearTimeout(pressTimer); if (isLongPressing && currentVideo) { currentVideo.playbackRate = originalSpeed; showSpeedTip(originalSpeed); if (navigator.vibrate) navigator.vibrate(20); isLongPressing = false; currentVideo = null; } }); video.addEventListener('contextmenu', e => { e.preventDefault(); e.stopPropagation(); }); } function scan() { document.querySelectorAll('video').forEach(attach); } scan(); setTimeout(scan, 500); setTimeout(scan, 1500); setTimeout(scan, 4000); setTimeout(scan, 10000); new MutationObserver(scan).observe(document, { childList: true, subtree: true }); // 全屏变化时强制重建提示 ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'msfullscreenchange'].forEach(evt => { document.addEventListener(evt, () => { setTimeout(() => showSpeedTip(currentSpeed || originalSpeed || 1.0), 200); }); }); })();

求助:两个小问题修复

  1. 全屏模式下速度提示消失:进入视频全屏后,右上角提示会隐藏或被覆盖。希望能让提示在全屏时永可见(或许用更高优先级 DOM 或 Canvas 绘制?)。
  2. 与 h5player 脚本冲突:同时开启 h5player 时,长按倍速会被 h5player 的速度监控覆盖/恢复。求方法彻底兼容,让长按倍速优先生效。

📌 转载信息
转载时间:
2026/1/2 23:48:05

「代码审查不是为了证明你是对的,而是为了证明代码没有错。」

在传统的软件工程中,代码审查(Code Review)往往是最容易被忽视却又最关键的环节。开发者忙于交付功能,审查者碍于情面不愿直言,最终让带着缺陷的代码溜进生产环境。

今天,我要介绍一个颠覆性的 AI 代码审查工作流 —— 它不懂得「客气」,只懂得「找茬」。


一、什么是「对抗式」代码审查?

这个工作流的核心哲学很简单:NEVER accepts “looks good”(永远不要接受「看起来不错」)。

它被设计成一个持有批判立场的高级开发者,必须在每次审查中找出 3-10 个具体问题。这不是为了刁难,而是为了确保:

  • 任务标记为 [x] 的真的是完成了
  • 验收标准真的是实现了,不是糊弄
  • 代码质量经得起安全、性能、可维护性考验
description: "Perform an ADVERSARIAL Senior Developer code review
that finds 3-10 specific problems in every story. Challenges everything:
code quality, test coverage, architecture compliance, security, performance.
NEVER accepts `looks good`"


二、工作流全流程解析

步骤 1:加载故事 + 发现真相

审查的第一步是「对账」—— 对比开发者声称改了什么Git 仓库实际改了什么

git status --porcelain  # 找未提交的改动
git diff --name-only    # 看修改了哪些文件
git diff --cached --name-only  # 看暂存区的文件 

发现真相的三个维度:

  1. Git 有改动,但故事文件里没记录 → 文档不完整
  2. 故事声称改了文件,但 Git 没痕迹 → 虚假声明
  3. 有未提交改动没追踪 → 透明度问题

步骤 2:构建「攻击计划」

系统会自动提取:

  • 所有验收标准(Acceptance Criteria)
  • 所有任务及其完成状态
  • 开发者记录的文件列表

然后制定审查计划:

  1. AC 验证:每个验收标准真的实现了吗?
  2. 任务审计:每个打钩的任务真的完成了吗?
  3. 代码质量:安全、性能、可维护性
  4. 测试质量:是真测试还是占位符?

步骤 3:执行对抗式审查

这是最核心的环节。AI 会逐文件逐行检查:

🔴 CRITICAL ISSUES(必须修)
├── 任务标记 [x] 但实际没实现
├── 验收标准没有实现
├── 故事声称改了文件但 Git 无证据
└── 安全漏洞

🟡 MEDIUM ISSUES(应该修)
├── 改了文件但没记录到故事文件列表
├── 未提交的改动未追踪
├── 性能问题
├── 测试覆盖率/质量不足
└── 代码可维护性问题

🟢 LOW ISSUES(可以修)
├── 代码风格改进
├── 文档缺失
└── Git 提交信息质量

关键机制:如果发现问题少于 3 个,AI 会被要求继续深挖

<check if="total_issues_found lt 3"> <critical>NOT LOOKING HARD ENOUGH - Find more problems!</critical> <!-- 重新检查边界情况、架构违规、集成问题... --> </check> 

步骤 4:呈现发现 + 自动修复

审查结果呈现后,开发者有三种选择:

选项行动
自动修复AI 直接修改代码和测试
创建行动项将问题加入故事的待办任务
深入查看显示问题的详细解释和代码示例

步骤 5:状态同步

最后,系统会自动:

  1. 更新故事状态(done /in-progress)
  2. 同步到 sprint-status.yaml
  3. 记录审查历史到 Change Log


三、与传统 Code Review 的对比

维度传统 Code ReviewAI 对抗式审查
态度礼貌、顾忌直接、不留情面
覆盖度随机抽查100% 覆盖
速度依赖人工时间即时反馈
一致性审查者水平波动标准统一
可追溯口头讨论或零散记录结构化问题列表


四、实战案例示例

假设开发者提交了一个「用户认证」功能:

开发者声称:

[x] 实现登录 API
[x] 添加 JWT 验证
[x] 编写单元测试

AI 对抗式审查发现:

🔴 CRITICAL: 任务标记 [x] 但未实现
├── src/auth/login.ts:45 - JWT 密钥硬编码,应从环境变量读取
└── tests/auth.test.js - 所有测试都使用 t.skip() 跳过

🟡 MEDIUM: 性能问题
└── src/auth/login.ts:23 - 每次登录都查询数据库获取用户权限
   建议:使用 Redis 缓存用户权限

🟡 MEDIUM: 测试质量不足
└── tests/auth.test.js - 缺少错误场景测试(密码错误、用户不存在)

结果?开发者必须修复这些问题才能标记为「完成」。


五、真实对话记录

想看看 AI 对抗式代码审查的真实运行过程吗?

完整对话记录:对话记录 - Cascade Chat Conversation

在这个真实对话中,你可以看到 AI 如何:

  • 逐个检查验收标准的实现情况
  • 发现被标记为「完成」但实际上未完成的任务
  • 指出代码中的安全和性能问题
  • 要求开发者修复后才通过审查


六、工作流架构图

开发者提交代码

AI 对抗式审查

加载故事和 Git 变更

构建攻击计划

执行深度审查

发现问题数量 < 3?

继续深挖问题

输出审查结果

呈现问题列表

开发者选择

自动修复

创建行动项

深入查看

更新状态

同步到 sprint-status.yaml


七、如何集成到你的项目?

这个工作流是 BMAD v6 框架的一部分。基本集成步骤:

  1. 安装 BMAD v6: npx bmad-method@alpha install
  2. 实现故事/dev-story
  3. 触发审查/code-review


八、总结:为什么「找茬」很重要?

代码审查的本质是质量门禁。在 AI 辅助开发时代,我们不再需要人类做机械性的代码扫描,但我们需要一个永不妥协的质量守门员

这个 AI 代码审查工作流的独特价值在于:

  • 不讲人情:只认代码,不认关系
  • 事必躬亲:逐文件验证,不遗漏
  • 知识驱动:结合架构文档、项目上下文综合判断
  • 可自愈:发现问题时可以自动修复

正如工作流文档所说:

“YOU are so much better than the dev agent that wrote this slop”

这种「对抗」不是对抗开发者,而是对抗缺陷、对抗技术债务、对抗生产环境的故障。


延伸阅读



📌 转载信息
转载时间:
2026/1/2 21:51:33

# 博物馆级昆虫标本科普图谱 · 蝴蝶

请创建一张**博物馆展品级别的昆虫知识科普图谱**,  
聚焦展示一只真实存在过、被采集并记录的【蝴蝶】。

---

## 核心概念
整个画面是一张**单独的实体标本记录纸**,  
平铺在工作台上,从**正上方垂直俯视**。  
蝴蝶标本、昆虫针、文字、标注**共享同一真实物理平面**。

这不是插画,也不是版式设计,  
而是一页博物学家的真实记录。

---

## 中央标本(最高优先级)
- 一只真实的**蝴蝶标本**(成虫)  
- 标本直接平放在纸面上,占画面 **60–70%**
- 视角:正上方垂直俯视(top-down, orthographic feel)
- ❌ 不是照片印在纸上  
- ✅ 是实体标本固定在纸面上  

### 固定方式
- 使用 **2–4 根细长银色昆虫针**
- 主针穿过胸部
- 辅助针固定前翅、后翅边缘
- 针尖刺入纸面
- 针孔周围纸面有**细微凹陷与压痕**

### 光影
- 柔和自然光从上方照射
- 标本与针在纸面上投下**真实、贴合的阴影**
- 阴影用于表现:
  - 翅膀厚度
  - 身体体积
  - 针的高度

---

## 标本细节质感(必须可见)
- 翅膀鳞片纹理清晰
- 翅脉结构自然分叉
- 翅缘有轻微厚度与不规则
- 翅膀局部轻微透光
- 复眼低调反光
- 触角形态清晰
- 胸腹部绒毛克制但真实

---

## 标注系统设计
- 使用**细引导线**从身体部位延伸到文字
- 线条直接画在纸面上
- ❌ 不使用卡片 / UI / 模块

---

## 必需标注的身体部位(8–10 个)

### 头部 Head
- 复眼 Compound Eyes  
  用于感知光线与运动  
  🔍 对颜色高度敏感

- 触角 Antennae  
  用于嗅觉与平衡  
  💡 蝴蝶触角末端呈棒状

- 口器(喙)Proboscis  
  用于吸食花蜜  
  🔍 平时盘卷收起

---

### 胸部 Thorax
- 前胸 Prothorax  
- 中胸 Mesothorax  
- 后胸 Metathorax  
  负责运动与翅膀连接  
  🔍 翅膀连接于中胸与后胸

---

### 翅膀 Wings
- 前翅 Forewings  
- 后翅 Hindwings  
- 翅脉 Wing Veins  
- 鳞片 Scales  
  鳞片决定颜色与图案  
  💡 鳞粉脱落可帮助逃生

---

### 腹部 Abdomen
- 腹部体节 Abdominal Segments  
- 气孔 Spiracles  
  通过气孔呼吸  
  🔍 昆虫没有肺

---

## 页面其他信息(低权重)

### 基础档案
- 翼展:X–X cm  
- 栖息地:森林、草地、花田  
- 食性:花蜜(成虫)

### 生命周期
- 卵 → 幼虫(毛毛虫) → 蛹 → 成虫  
- 完全变态

### 趣味冷知识
- 💡 翅膀颜色来自鳞片结构  
- 🔍 触碰后会留下鳞粉

---

## 纸面与美学
- 标本纸:象牙白 / 浅灰白(#F8F6F0)
- 可见纸张纤维
- 配色以黑、白、低彩度为主
- 没有框架、没有版画边界

---

## 严格禁止
- ❌ 版画感  
- ❌ 展板式构图  
- ❌ 装饰性边框  
- ❌ 卡片 / UI  
- ❌ 悬浮元素  

---

## 输出要求
- 高分辨率,适合博物馆级打印
- 看起来像**一页真实存在的自然史标本记录**
- 安静、理性、可信

📌 转载信息
原作者:
cj3343
转载时间:
2026/1/2 21:42:40

开源地址:GitHub - ZyphrZero/obsidian-smart-workflow: AI tools & cross-platform terminal for Obsidian.

目前功能包含自动命名 / 终端 / 语音 / 翻译 / 写作
(元旦肝死我了 )

前端使用的 ts,后端是 rust,websocket 通信,性能优化非常好

项目中为了方便开发,我专门构建了一套快速开发的工作流脚本,避免频繁手动 push 构建产物,pnpm install:dev 就可以直接安装到 obsidian 插件目录,搭配上 Hot Reload 插件就能实现无缝的热重载

Obsidian 插件 (TypeScript)
    │
    │ WebSocket
    ▼
Smart Workflow Server (Rust)
├── PTY 终端会话
├── 音频录制 & ASR
├── LLM 流式处理
└── 语言检测

配置方式对用户很友好,支持多密钥轮询,多供应商和模型管理

本地终端:

  • 支持 Windows/macOS/Linux 跨平台终端

语音输入:

  • 语音转录:说话直接转文字
  • 同声转译:边说边翻译
  • 语音润色:说完自动优化文本
  • 支持实时流式转录

大概后面不会增加 Composer 功能(因为已经有了本地终端感觉没必要再做这类功能了)









📌 转载信息
原作者:
Cassianvale
转载时间:
2026/1/2 21:24:49

前言

最近在学习简谱,遇到了一个小痛点:每次看到 1 2 3 这些数字时,大脑总要停顿半拍才能反应过来对应的是 Do Re Mi。在实际演唱或练习时,这种延迟经常导致卡壳,影响流畅度。相信很多音乐初学者都有类似的困扰。为了解决这个问题,我利用业余时间开发了一个简谱学习小工具,希望通过游戏化的方式帮助大家快速建立数字与唱名之间的肌肉记忆。

介绍

这是一个纯网页应用,核心功能非常简洁:

  • 即开即用:无需下载安装,也不需要注册登录,打开网页就能开始练习
  • 多感官学习:点击卡片后会同时显示数字简谱和对应的唱名,并播放准确的音高,通过视觉和听觉的结合加深记忆
  • 游戏化设计:提供了多个练习模式,既可以自己训练,也可以当作音乐小游戏给孩子玩

使用场景

这个工具适合以下人群:

  1. 正在学习简谱的音乐初学者
  2. 需要提高视唱练耳能力的学生
  3. 家里有正在学琴的小朋友的家长
  4. 想快速掌握数字简谱体系的任何人

体验地址

项目地址:https://happy-numbered-notation.app/

完全免费使用,欢迎大家体验并提出宝贵意见!如果觉得有帮助,也欢迎分享给有需要的朋友。

编程体会

刚经历一次 AI 重写。

原来是厚重的 nextjs+tts api,这么点小功能要拖着 1 个多 G 的 node_modules。发布,编译都很麻烦还慢。

忍不了了,直接和 AI 讨论了下,连 AI 都建议新开坑从头写,反正又不是我写,直接开干。

几个小时后,变成了 no-build 模式,vuejs + 模板、简单本地 bun 打包去缓存、装配、优化体积、预生成音频文件、无服务器直开。

干干净净,日后修改也非常方便。旧项目直接垃圾桶去干活。

其实 vibe 这么一个小项目也不需要太洁癖花几个小时重构的,但我做这个是为了以后做类似的小应用的时候,直接复用这个项目做 AI 的上下文,这就有用多了。

而且作为一个老架构师,还是着实惊讶这种架构推翻式重构现在竟然像儿戏一样,这在以前是不可思议的。

这个小工具源于一个真实的学习痛点,通过技术手段将枯燥的记忆训练变得更加有趣和高效。希望它能帮助更多人快速建立简谱与唱名之间的联系,让音乐学习更加轻松愉快。如果你也在学习简谱,或者有 AI 编程方面的问题,欢迎在评论区交流!


📌 转载信息
原作者:
kkjames
转载时间:
2026/1/2 21:23:24

Afterglow | 余火

这首歌关于一个庆祝,庆祝什么呢,庆祝我们的遗憾,此时此刻我们是被我们过往所有的经历堆叠而成的,包括艰难的时刻,所以让我们点起火把。

———— 《当我们点起火把》 DOUDOU

开源地址

同时感谢:谷歌大善人和他的 AI Studio,Gemini-3-Flash 以及 Antigravity

UI 速览

你也可以前往 CF 部署的 web 版本体验:https://afterglow.realme.top/

首页记录 - 编辑记录设置

功能

  • 目标打卡,同时可以设置一个主目标,在首页主要区域显示。
  • 随时补卡,无需任何前置条件
  • 心情记录,记录下每一次打卡时的小感触
  • 支持英语 / 中文切换
  • 热力图(可选),查看到目前为止的数天内打卡频率
  • 一言(可选),基于 API 的每日一言(zh/en 下 API 不同)
  • webdav 备份,用于备份、恢复数据。

使用

Afterglow 使用 React 编写,且无后端服务,是一个「纯前端」打卡 APP。可以选择以下方式使用:

数据

目前所有的数据使用本地的 IndexedDB,可以通过 webdav 进行备份 / 恢复。
后续会尝试支持 Supabase/Cloudflare 部署 API+DB

WebDAV 备份

数据很重要(嗯

所以为了有效保持自己的小习惯能被很好的记录,可以使用 webdav 进行备份。

个人使用坚果云(免费)来作为 webdav 备份(https://dav.jianguoyun.com/dav/),因为可能遇到的 CORS 问题,也支持自建一个 CORS 代理来绕过可能出现的请求失败问题。

具体而言,可以使用我在 deno 部署的项目:https://cors-proxy.heerheer.deno.net/?url=,也可以采用其他的开源自部署项目,我部署的版本代码放在文末~

未来计划

  • peer review (什么赛博监视)
  • 自部署服务端与数据库支持
  • 将数据生成分享图 / 时段总结

附录

CORS-Proxy Deno
// main.ts — Deno Deploy CORS Proxy (safe-ish) // Deploy: https://deno.com/deploy // Usage:  /?url=https%3A%2F%2Fexample.com%2Fapi const ALLOWED_ORIGINS = ["*"]; // 你也可以改成 ["https://yourdomain.com"] const ALLOWED_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS","MKCOL","PROPFIND"];

// 目标站点白名单:只允许代理这些域名(强烈建议填写) // 例: ["api.github.com", "v1.hitokoto.cn"] const TARGET_HOST_ALLOWLIST = new Set<string>([
  "v1.hitokoto.cn",
  "thequoteshub.com",
  // "api.example.com",
]);

// 允许转发的请求头(避免转发一些危险/无意义的头) const FORWARD_REQ_HEADERS = new Set([
  "accept",
  "accept-language",
  "content-type",
  "authorization",
  "x-requested-with",
  "user-agent",
]);

// 允许暴露给浏览器端读取的响应头 const EXPOSE_HEADERS = [
  "content-type",
  "content-length",
  "date",
  "etag",
  "cache-control",
  "expires",
  "last-modified",
  "x-request-id",
];

function pickCorsOrigin(origin: string | null) {
  if (!origin) return "*";
  if (ALLOWED_ORIGINS.includes("*")) return "*";
  return ALLOWED_ORIGINS.includes(origin) ? origin : "null";
}

function isAllowedTarget(url: URL) {
  // 仅允许 http/https if (!["http:", "https:"].includes(url.protocol)) return false;
  // // 白名单校验 // return TARGET_HOST_ALLOWLIST.has(url.host); return true;
}

function buildCorsHeaders(origin: string | null) {
  const allowOrigin = pickCorsOrigin(origin);
  return new Headers({
    "access-control-allow-origin": allowOrigin,
    "access-control-allow-methods": ALLOWED_METHODS.join(", "),
    "access-control-allow-headers": "Content-Type, Authorization, X-Requested-With,Depth",
    "access-control-expose-headers": EXPOSE_HEADERS.join(", "),
    "access-control-max-age": "86400",
    "vary": "Origin",
  });
}

function filterRequestHeaders(req: Request) {
  const headers = new Headers();
  for (const [k, v] of req.headers.entries()) {
    const key = k.toLowerCase();
    if (FORWARD_REQ_HEADERS.has(key)) headers.set(k, v);
  }
  return headers;
}

Deno.serve(async (req) => {
  const url = new URL(req.url);
  const origin = req.headers.get("origin");

  // 预检请求直接返回 if (req.method === "OPTIONS") {
    return new Response(null, { status: 204, headers: buildCorsHeaders(origin) });
  }

  if (!ALLOWED_METHODS.includes(req.method)) {
    const h = buildCorsHeaders(origin);
    return new Response("Method Not Allowed", { status: 405, headers: h });
  }

  // 读取目标 URL const targetParam = url.searchParams.get("url");
  if (!targetParam) {
    const h = buildCorsHeaders(origin);
    return new Response("Missing ?url= encoded target URL", { status: 400, headers: h });
  }

  let targetUrl: URL;
  try {
    targetUrl = new URL(decodeURIComponent(targetParam));
  } catch {
    const h = buildCorsHeaders(origin);
    return new Response("Invalid target url", { status: 400, headers: h });
  }

  if (!isAllowedTarget(targetUrl)) {
    const h = buildCorsHeaders(origin);
    return new Response("Target host not allowed", { status: 403, headers: h });
  }

  // 把当前请求的 query(除了 url)拼到目标上(可选) // 例如 /?url=...&a=1 => target?a=1 for (const [k, v] of url.searchParams.entries()) {
    if (k === "url") continue;
    targetUrl.searchParams.set(k, v);
  }

  // 构造转发请求 const forwardHeaders = filterRequestHeaders(req);

  // 如果是 GET/HEAD 不带 body,其它方法允许透传 body const hasBody = !["GET", "HEAD"].includes(req.method);
  const body = hasBody ? req.body : undefined;

  let upstreamResp: Response;
  try {
    upstreamResp = await fetch(targetUrl.toString(), {
      method: req.method,
      headers: forwardHeaders,
      body,
      redirect: "follow",
    });
  } catch (e) {
    const h = buildCorsHeaders(origin);
    return new Response(`Upstream fetch failed: ${String(e)}`, { status: 502, headers: h });
  }

  // 复制响应头(过滤掉 hop-by-hop 以及冲突的 CORS 头) const respHeaders = new Headers();
  for (const [k, v] of upstreamResp.headers.entries()) {
    const key = k.toLowerCase();
    if (key === "set-cookie") continue; // 浏览器也读不到,且有安全风险 if (key.startsWith("access-control-")) continue; // 我们自己控制 CORS
    respHeaders.set(k, v);
  }

  // 注入 CORS 头 const corsHeaders = buildCorsHeaders(origin);
  for (const [k, v] of corsHeaders.entries()) respHeaders.set(k, v);

  return new Response(upstreamResp.body, {
    status: upstreamResp.status,
    statusText: upstreamResp.statusText,
    headers: respHeaders,
  });
});

📌 转载信息
原作者:
Harmog
转载时间:
2026/1/2 21:18:21

不针对个案,讨论的是整体趋势。

在 Vibe 编程时代之前,独立开发者可以针对各种长尾、碎片需求进行开发,确实有一定的生存空间。
但 AI 时代下,人人都会 Vibe 编程,是不是独立开发者已经没有其存在空间了。

于此同时,相反的说法是,会越来越多的出现所谓“超级个体”,用 AI 武装到牙齿的个体户。

新年伊始,一个老程序员不知道何去何从。。。

是不是得等七天?

这两天用了一下 gemini 3 fresh,竟然给我的感觉比我用 claude code sonnet 4.5 要好得多,任务都很快就按照我的要求就完成了,但是问题是我用了一天就超限了,所有的模型都超 limit 。

我换了一个 google 账号,又用了一天。但是它得多久重置啊? 好像听说是 7 天? 那这样的话根本没法用来写大项目啊。

我这两天还真的感觉 Antigravity 挺好用的。

目前简中的说法都是"谷歌账号归属地须与支持 Antigravity 的节点 IP 一致"才能用 Antigravity 。

我刚才用个超级干净的账号测试,发现只要账号是支持地区的就行,并不完全要求"一致",例如 JP 账号配 US 节点也能用。

超级干净定义:谷歌账号从注冊、使用到支付都没接触过不支持 Antigravity 的地区 (CN / MO / HK 等)。

账号:台湾注冊 + EULA 地区台湾 + Play 地区日本 + 无付款方式 + 15 年老号

节点:奥地利

  1. 网络环境:选择美区节点(新加坡节点也有成功案例),确定后不要频繁更换其他国家节点,避免被检测。
  2. 登录验证:用谷歌账号登录 Gemini 学生优惠认证页面,若显示大学生优惠一年的页面则可继续;若显示 “无法享受此优惠”,大概率是网络节点问题,更换美区不同子节点尝试。
  3. 学生认证:点击领取学生优惠进入认证环节,无需填写信息,复制页面网址到 1key.me 网站验证(排队 5 - 10 分钟),验证成功后返回认证页面刷新,即可获得学生资格。
  4. 支付绑定:可在二手平台(文中 “海鲜市场”)购买约 10 元的卡绑定,若用自己的信用卡绑定,支付后需取消订阅,避免后续产生大额支出。

📌 转载信息
原作者:
AIcoding
转载时间:
2026/1/2 21:04:53

混元 Motion 1.0

一款基于 Diffusion Transformer(DiT)模型架构与流匹配机制的十亿参数量级文生 3D 动作模型,支持从自然语言描述生成流畅自然、类别覆盖广泛的 3D 角色动画,可以无缝集成到美术 3D 动画管线。

开源时间:2025 年 12 月 30 日


📌 转载信息
原作者:
WindChimeEcho
转载时间:
2026/1/2 16:19:22

当知道 Cloudflare 大善人 有 R2 的时候,我就想搞,后面发现要绑定卡,搞了一个人人卡,总算是绑定上了,既然绑定上了,那肯定要给他用起来呀,然后我就再 github 上遨游了一会,发现

这个项目,然后我就按照教程部署了一下,发现界面有点不太喜欢,于是我就 CC 启动,给他美化,美化完想着,能不能再加一些功能呢,于是又加了一些功能。
原本也想过用 openList,部署了后想着如果只用 R2 的话还不如就用这个更方便以及定制化会好很多。

核心功能

功能说明
文件管理上传、下载、重命名、移动、删除
文件夹支持创建文件夹、文件夹重命名、文件夹下载(打包 ZIP)
高级搜索支持文件名、类型、扩展名、大小多维度搜索
缩略图预览图片和视频自动生成缩略图
响应式设计完美支持 PC 和移动端
大文件上传支持最大 5GB 文件(分片上传)

现代化 UI

功能说明
主题切换支持亮色 / 暗色模式,跟随系统或手动切换
卡片布局类 Notion 风格的现代化界面
统计面板存储概览、文件统计、类型分布
操作统计R2 A 类 / B 类操作次数统计(需配置)

批量操作

功能说明
多选模式批量选择文件和文件夹
批量下载多文件打包 ZIP 下载
批量删除一键删除多个文件
批量移动批量移动到指定目录

文件分享

功能说明
分享链接生成文件分享链接
时效控制支持 1 小时 / 1 天 / 7 天 / 30 天 / 永久 / 自定义
密码保护可选设置访问密码
下载限制可选限制最大下载次数
wget 命令自动生成 wget 下载命令

权限系统

功能说明
多用户支持支持多管理员账户
目录授权为不同用户分配不同目录权限
只读用户支持只能查看和下载的只读账户
访客模式可配置访客可访问的目录(仅查看)
现代化登录自定义登录界面,非浏览器弹窗


界面预览

主界面 - 亮色主题

主界面 - 暗色主题

文件上传

高级搜索

文件分享

统计面板

登录界面

分享管理

移动端适配


快速部署

前置要求

  • Cloudflare 账户(免费即可)
  • GitHub 账户

部署步骤

第一步:Fork 仓库

点击本仓库右上角的 Fork 按钮,将仓库复制到你的 GitHub 账户。

第二步:创建 R2 存储桶

  1. 登录 Cloudflare 控制台
  2. 左侧菜单选择 R2 对象存储
  3. 点击 创建存储桶
  4. 输入存储桶名称(如 my-drive),选择地区,点击创建
  5. 进入存储桶 → 设置公开访问
  6. 点击 允许访问,复制 公共存储桶 URL(格式如:https://pub-xxx.r2.dev

建议:同时设置 对象生命周期规则,添加 "中止未完成的分段上传(1 天)",避免上传中断产生的垃圾数据。

第三步:创建 Pages 项目

  1. 进入 Cloudflare 控制台 → Workers 和 Pages
  2. 点击 创建Pages连接到 Git
  3. 选择你 Fork 的仓库
  4. 项目名称可自定义(如 my-drive
  5. 框架预设 保持默认(无)
  6. 展开 环境变量(高级),添加以下变量:
变量名称说明
PUBURLhttps://pub-xxx.r2.dev你的公共存储桶 URL
GUESTpublic/访客可访问目录(留空则禁止)
admin:你的密码*管理员账户,* 表示所有目录权限
  1. 点击 保存并部署

第四步:绑定 R2 存储桶

  1. 部署完成后,进入 Pages 项目
  2. 设置函数R2 存储桶绑定
  3. 点击 添加绑定
  4. 变量名称填写:BUCKET
  5. R2 存储桶选择你创建的存储桶
  6. 点击保存

第五步:绑定 KV 命名空间(分享功能需要)

  1. 进入 Cloudflare 控制台 → Workers 和 PagesKV
  2. 点击 创建命名空间,名称填写 ossShares
  3. 回到 Pages 项目 → 设置函数KV 命名空间绑定
  4. 点击 添加绑定
  5. 变量名称填写:ossShares
  6. KV 命名空间选择刚创建的 ossShares
  7. 点击保存

第六步:重新部署

  1. 进入 部署 页面
  2. 找到最新的部署,点击右侧 重试部署
  3. 等待部署完成,访问你的域名即可使用


环境变量配置

配置概览

变量名必需说明示例
PUBURLR2 公共存储桶 URLhttps://pub-xxx.r2.dev
BUCKETR2 存储桶绑定(在函数设置中配置)-
ossSharesKV 命名空间绑定(分享功能需要)-
FILE_BASE_URL前端文件访问 URL(CDN 回源场景)https://cdn.example.com
GUEST访客可访问目录public/
GUEST_UPLOAD_PASSWORD访客上传密码your_password
CF_ACCOUNT_IDCloudflare 账户 ID(操作统计需要)abc123...
CF_API_TOKENAPI Token(操作统计需要)xxx...
R2_BUCKET_NAME指定统计的存储桶名称my-drive

基础配置

PUBURL(必需)

R2 公共存储桶的访问 URL,用于服务端获取文件。

PUBURL = https://pub-xxx.r2.dev

获取方式:进入 R2 存储桶 → 设置 → 公开访问 → 复制公共存储桶 URL

FILE_BASE_URL(可选)

前端文件访问的基础 URL。用于 CDN 回源场景,如果不配置则使用 Pages Function 代理。

FILE_BASE_URL = https://cdn.example.com

用户与权限配置

用户账户配置

用户以 用户名:密码 格式作为变量名,权限配置作为

变量名(用户名:密码)值(权限)说明
admin:123456*管理员,拥有所有权限
user1:password1普通用户,可读写指定目录
viewer:password2readonly, public/只读用户,只能查看和下载

权限值说明

权限值说明
*管理员,拥有所有目录的读写权限
path1/, path2/普通用户,可读写指定目录(多个用逗号分隔)
readonly, path/只读用户,只能查看和下载指定目录

访客配置

变量名说明
GUESTpublic/, shared/访客可访问的目录(仅查看和下载)
GUEST_UPLOAD_PASSWORDyour_password访客上传密码(可选)

注意事项

  • 目录路径不要/ 开头
  • 目录路径建议/ 结尾
  • 访客默认只能查看和下载,不能上传
  • 配置 GUEST_UPLOAD_PASSWORD 后,访客输入正确密码可上传文件
  • 访客看不到统计面板

配置示例

# 管理员账户
admin:MySecurePassword123 = *

# 普通用户 - 可以读写 photos 和 documents 目录
alice:alice123 = photos/, documents/

# 只读用户 - 只能查看 public 目录
bob:bob456 = readonly, public/

# 访客可访问的目录
GUEST = public/, shared/

# 访客上传密码(可选)
GUEST_UPLOAD_PASSWORD = guest_upload_2024

操作统计配置(可选)

如需启用 R2 操作统计(A 类 / B 类操作次数),需添加以下变量:

变量名必需说明
CF_ACCOUNT_IDCloudflare 账户 ID
CF_API_TOKENAPI Token(需 Analytics 读取权限)
R2_BUCKET_NAME指定统计的存储桶名称(不填则统计所有)

获取账户 ID

  1. 登录 Cloudflare 控制台
  2. 右侧边栏可以看到 账户 ID
  3. 复制该 ID

创建 API Token

  1. 进入 API Tokens 页面
  2. 点击 创建令牌
  3. 选择 创建自定义令牌
  4. 配置权限:
    • 账户Account Analytics读取
  5. 账户资源选择你的账户
  6. 点击 继续以显示摘要创建令牌
  7. 复制生成的 Token

操作类型说明

类型包含操作计费
A 类操作PUT、POST、DELETE、ListObjects、上传、复制等较高
B 类操作GET、HEAD、下载、查询等较低

统计周期为最近 30 天,数据缓存 30 分钟。

CDN 回源配置(可选)

如果你想通过第三方 CDN(如 EdgeOne、又拍云等)回源访问文件,可以配置 FILE_BASE_URL

架构示意

用户浏览器
    ↓ 访问
CDN (例如 EdgeOne)
    ↓ 回源
R2 公共存储桶

配置步骤

  1. 配置 CDN 回源到 R2

    • 在 CDN 控制台创建站点(如 cdn.example.com
    • 配置回源地址为 R2 公共 URL(如 https://pub-xxx.r2.dev
  2. 设置环境变量

    FILE_BASE_URL = https://cdn.example.com
    
  3. 工作原理

    • 前端请求文件时会使用 FILE_BASE_URL(如 https://cdn.example.com/file.jpg
    • 用户浏览器直接请求 CDN
    • CDN 回源到 R2 获取文件

不配置时的默认行为

如果不配置 FILE_BASE_URL

  • 前端使用 /raw/file.jpg 相对路径
  • 请求通过 Cloudflare Pages Function 代理到 R2


使用指南

基本操作

上传文件

  • 点击右下角 上传按钮
  • 或直接 拖拽文件 到页面任意位置

创建文件夹

  • 点击上传按钮 → 选择 新建文件夹
  • 或点击工具栏的文件夹图标

文件操作

  • 单击文件:预览 / 下载
  • 右键 / 长按:打开操作菜单(重命名、下载、复制、移动、删除)

批量操作

  1. 点击工具栏的 选择图标 进入选择模式
  2. 点击文件卡片左上角的复选框选择文件
  3. 底部浮动栏显示已选数量和操作按钮
  4. 可进行批量下载、移动、删除

登录与权限

登录

  1. 点击顶部导航栏右侧的 登录按钮
  2. 在弹出的登录对话框中输入用户名和密码
  3. 登录成功后会显示用户头像和用户名

查看权限

  • 登录后点击用户头像,展开下拉菜单
  • 可以看到当前用户的角色(管理员 / 普通用户)
  • 显示可写入的目录列表
  • 管理员拥有所有目录权限

退出登录

  • 点击用户头像 → 点击 退出登录
  • 退出后将以访客身份浏览

主题切换

点击顶部导航栏右侧的 太阳 / 月亮图标 切换亮色 / 暗色主题。

视图切换

工具栏提供两种视图:

  • 网格视图:卡片式布局,适合浏览图片
  • 列表视图:紧凑列表,适合查看详细信息

高级搜索

支持两种搜索方式:

方式一:可视化搜索面板

点击搜索框右侧的 筛选图标 打开高级搜索面板,可以:

  • 选择文件类型(图片、视频、文档、压缩包、程序等)
  • 输入扩展名筛选(如 .pdf.jar
  • 设置文件大小范围

方式二:搜索语法

直接在搜索框输入查询语法:

语法说明示例
type:类型按文件类型筛选type:图片type:视频type:压缩
ext:扩展名.扩展名按扩展名筛选ext:pdf.jarext:mp4,mkv
size>大小大于指定大小size>=1GB
size<大小小于指定大小
size:范围大小范围
关键词文件名包含backup2024

支持的文件类型:

类型关键词
图片图片
视频video视频vid
文档document文档doctext
压缩包archive压缩zip
程序executable程序exe
其他other其他

支持的大小单位: BKBMBGBTB(不区分大小写)

组合查询示例:

文件分享

创建分享

  1. 右键点击文件 → 选择 分享
  2. 设置分享选项:
    • 有效期:1 小时 / 1 天 / 7 天 / 30 天 / 永久 / 自定义分钟
    • 密码保护:可选,设置访问密码
    • 下载限制:可选,限制最大下载次数
  3. 点击 创建分享链接
  4. 复制分享链接或 wget 命令

分享链接格式

类型格式
网页访问https://your-domain.com/s/{shareId}
直接下载https://your-domain.com/s/{shareId}/download
带密码下载https://your-domain.com/s/{shareId}/download?pwd=密码

wget 下载示例

# 无密码
wget --content-disposition "https://your-domain.com/s/abc123/download"

# 有密码
wget --content-disposition "https://your-domain.com/s/abc123/download?pwd=mypassword"

分享管理(管理员功能)

管理员可以查看和管理所有用户创建的分享链接。

打开分享管理

  1. 以管理员身份登录
  2. 点击顶部导航栏的用户头像
  3. 在下拉菜单中点击 分享管理

分享列表信息

字段说明
文件名分享的文件名称
文件大小文件大小
密码状态是否设置了访问密码
创建时间分享创建的时间
过期状态显示剩余时间或已过期
下载次数已下载次数 / 最大下载次数
创建者创建分享的用户

管理操作

  • 复制链接:快速复制分享链接
  • 删除分享:删除该分享(文件本身不会被删除)

过期状态说明

状态颜色说明
永久绿色永久有效的分享
X 天 / 小时后灰色 / 黄色即将过期
已过期红色分享已失效


本地开发

环境要求

  • Node.js 18+
  • pnpm(推荐)或 npm

开发步骤

# 克隆仓库
git clone https://github.com/你的用户名/Cloudflare-R2-oss.git
cd Cloudflare-R2-oss

# 安装依赖
pnpm install

# 启动开发服务器
pnpm dev

开发服务器启动后访问 http://localhost:8788

构建部署

# 构建
pnpm build

# 部署到 Cloudflare Pages
pnpm deploy


项目结构

Cloudflare-R2-oss/
├── assets/                 # 前端资源
│   ├── App.vue            # 主应用组件
│   ├── Header.vue         # 顶部导航栏(含用户菜单、搜索)
│   ├── AdvancedSearchPanel.vue # 高级搜索面板
│   ├── StatsCards.vue     # 统计卡片
│   ├── Breadcrumb.vue     # 面包屑导航
│   ├── Toolbar.vue        # 工具栏
│   ├── FileCard.vue       # 文件卡片
│   ├── BatchBar.vue       # 批量操作栏
│   ├── Dialog.vue         # 对话框基础组件
│   ├── LoginDialog.vue    # 登录对话框
│   ├── ShareDialog.vue    # 分享对话框
│   ├── ShareListDialog.vue # 分享列表对话框
│   ├── InputDialog.vue    # 输入对话框
│   ├── ConfirmDialog.vue  # 确认对话框
│   ├── Toast.vue          # 消息提示组件
│   ├── Menu.vue           # 菜单组件
│   ├── UploadPopup.vue    # 上传弹窗
│   ├── MimeIcon.vue       # 文件图标
│   ├── search.mjs         # 搜索解析引擎
│   ├── main.css           # 全局样式
│   ├── main.mjs           # 工具函数(上传、缩略图等)
│   ├── favicon.svg        # 网站图标
│   └── manifest.json      # PWA 配置
├── functions/              # Cloudflare Pages Functions
│   ├── api/
│   │   ├── auth.ts        # 认证 API
│   │   ├── config.ts      # 配置 API
│   │   ├── stats.ts       # 统计 API
│   │   ├── children/      # 文件列表 API
│   │   ├── share/         # 分享管理 API
│   │   └── write/         # 文件操作 API
│   ├── raw/               # 文件代理
│   └── s/                 # 分享页面
│       └── [id].ts        # 分享详情页
├── utils/                  # 工具函数
│   ├── auth.ts            # 权限验证
│   └── share.ts           # 分享工具
├── docs/                   # 文档资源
│   └── images/            # 截图图片
├── index.html             # 入口页面
├── wrangler.toml          # Wrangler 配置
├── package.json           # 项目配置
└── README.md              # 说明文档


常见问题

部署相关

Q: 上传失败怎么办?

检查以下几点:

  1. 是否已正确绑定 R2 存储桶,变量名必须是 BUCKET
  2. R2 存储桶是否已开启公开访问
  3. PUBURL 环境变量是否配置正确
Q: 为什么看不到操作统计?

操作统计需要配置以下环境变量:

  • CF_ACCOUNT_ID:Cloudflare 账户 ID
  • CF_API_TOKEN:API Token(需 Analytics 读取权限)

请参考 操作统计配置 章节。

Q: 如何自定义域名?

在 Pages 项目设置中添加自定义域名,Cloudflare 会自动配置 SSL。

Q: 忘记密码怎么办?

在 Pages 环境变量中查看、修改对应用户的密码,修改后重新部署即可。

文件操作相关

Q: 支持多大的文件?

单文件支持最大 5GB(使用分片上传)。

大文件上传建议使用稳定的网络环境,避免上传中断。

Q: R2 控制台显示 "正在进行的多部分上传" 无法删除怎么办?

这些是未完成的分段上传(Multipart Uploads),通常是因为大文件上传中断或失败导致的。它们不是真正的文件对象,所以无法通过常规方式删除。

解决方法:设置生命周期规则(推荐)

  1. 登录 Cloudflare 控制台
  2. 进入 R2 对象存储 → 选择你的存储桶
  3. 点击 设置(Settings) 标签
  4. 找到 对象生命周期规则(Object lifecycle rules)
  5. 添加规则:中止未完成的分段上传,设置为 1 天后自动删除
  6. 保存后等待规则生效,那些未完成的上传会被自动清理
Q: 重命名文件夹后,原文件夹没有被删除?

这通常是因为原文件夹中存在未完成的多部分上传

原因说明:

  • 重命名文件夹时,系统会复制所有文件到新路径,然后删除原文件
  • R2 的 list 操作只返回已完成的对象,不会返回 "正在进行的多部分上传"
  • 所以那些未完成的上传不会被迁移,残留在原路径下
  • 因为有这些残留的 "对象",R2 会继续显示原文件夹

解决方法: 参考上一个问题,设置生命周期规则清理未完成的分段上传,清理后原文件夹会自动消失。

Q: 为什么重命名文件夹这么慢?能不能直接改名?

这是对象存储(S3/R2)的固有限制,无法绕过。

技术原因:

  • R2(以及 AWS S3)中没有真正的 "文件夹" 概念
  • 文件夹只是通过对象 key 的前缀来模拟的
  • 例如 docs/file.pdf 就是一个完整的 key,不是 "docs 文件夹里的 file.pdf"
  • 要把它变成 文档/file.pdf,必须创建新对象、删除旧对象

R2/S3 API 限制:

  • 不支持重命名操作(rename)
  • 不支持移动操作(move)
  • 只能通过复制(copy)+ 删除(delete)实现

即使是 AWS S3 官方控制台,重命名文件夹也是同样的实现方式。Cloudflare R2 控制台目前甚至不提供文件夹重命名功能。

建议: 如果经常需要重命名文件夹,建议在创建时就想好名字,避免后续大量文件的复制操作。对于包含大量文件的文件夹,重命名的成本较高(耗时 + R2 操作次数计费)。


注意事项

安全建议

  1. 密码安全

    • 请使用强密码,避免使用简单密码如 123456
    • 定期更换密码
    • 不同用户使用不同密码
  2. 权限配置

    • 遵循最小权限原则,只给用户必要的目录权限
    • 敏感文件不要放在访客可访问的目录
    • 定期检查用户权限配置
  3. 分享链接

    • 敏感文件分享时建议设置密码和有效期
    • 定期清理过期的分享链接
    • 注意下载次数限制

费用说明

项目免费额度超出费用
R2 存储10 GB / 月$0.015/GB/ 月
R2 A 类操作100 万次 / 月$4.50 / 百万次
R2 B 类操作1000 万次 / 月$0.36 / 百万次
Pages 请求无限制免费
KV 存储1 GB$0.50/GB/ 月

对于个人使用,免费额度通常足够。大量文件操作(如批量重命名、移动)会消耗较多 A 类操作次数。

已知限制

  • 单文件最大支持 5GB
  • 文件夹重命名 / 移动需要复制所有文件(对象存储限制)
  • 暂不支持文件夹上传(浏览器限制)
  • 搜索仅支持当前目录,不支持全局搜索



致谢


如果这个项目对你有帮助,欢迎 Star ⭐


📌 转载信息
转载时间:
2026/1/2 16:16:55

功能特性

  • 多服务商支持 - 同时追踪 Claude、GPT、Gemini 等多个 AI 服务
  • 实时监控 - 一目了然查看配额消耗和剩余额度
  • 菜单栏集成 - 菜单栏快速访问
  • 插件系统 - 通过社区插件扩展功能
  • 插件市场 - 轻松发现和安装插件
  • 深色模式 - 原生深色主题 UI

可打包 window,Linux,没来急的测试

官网:AiBal - AI 监控助手
GitHub:GitHub - DDG0808/aibal: AiBal 是一款 macOS 菜单栏应用,为 AI 重度用户提供统一的多服务用量监控平台。
插件 GitHub:GitHub - DDG0808/aibal-plugins: aibal 插件库





📌 转载信息
原作者:
d914954480
转载时间:
2026/1/2 16:15:47

看佬友安装工具来切换配置,可能有人像我一样,不喜欢安装,这里有一种简易通过环境变量自由修改 cc 配置的方法:

1、终端执行:nano ~/.zshrc
2、写入配置并保存,以 glm 为例:

# Claude code (glm) glm() {
    export ANTHROPIC_BASE_URL=https://open.bigmodel.cn/api/anthropic
    export ANTHROPIC_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxx
    claude
}

3、终端执行:source ~/.zshrc
4、使用的时候只需要输入 glm 即可启动。可以在第 2 步里自定义更多的参数。


📌 转载信息
原作者:
ggvisPro
转载时间:
2026/1/2 16:13:59

项目背景:

起初是在整理表情包给角色扮演的机器人匹配合适语境的表情包麻烦,自己手动给图片打标较慢切难以管理。这两周空闲时间在 ai 加持下写了这个项目 (100% ai 生成哦,人工只做指挥)。

Note

理论上可以作为统一图床打标后给 ai 来筛选个性化符合语境的图片,不局限于表情包。

项目主要功能:

  • 基于视频模型和自定义提示词自动对图片进行打标 (打标推荐 gpt、gemini),然后向量化存储 (可选本地模型或者在线模型,在线的话轨迹流动 BAAI/bge-m3 免费的哦),提供多维度的筛选。
  • 支持 s3 兼容端点存储,并支持设置备份端点自动同步备份,对存储端点进行负载均衡。
  • 简单的多用户管理,方便多人合作上传
  • 图片区分公开和个人可见 (管理员可见所有) 详情可参考
  • 可前后端分离部署

项目地址:

** 项目体验地址:**https://img-tag.vercel.app demo/demo123
体验地址未配置视觉模型哦,需要完整功能的话可以自己部署试试看

资源占用

项目强依赖一个支持 vector 扩展的 postgresql,没有本地的话推荐在线的 https://neon.tech/

项目功能截图预览:

  1. 首页

  2. 仪表盘

  3. 我的图库 - 这里提供多维筛选 + 批量操作

  4. 图片探索 (公开)- 提供多维搜索和向量搜索


大图沉浸性浏览 + 部分键盘交互方便切换

  1. 上传图片 - 右下角浮标触发
  1. 存储端点管理

7. 标签管理,主分类标签提示词拿过去针对性提取相关关键字。

8. 任务队列

对于批量、耗时的异步操作,比如图片分析、批量删除、同步、批量分析等可以看到在任务队列看到相关参数和操作对象及结果

9. 系统设置

全局配置视觉模型相关内容、向量模型、以及用户管理和一些杂项配置


📌 转载信息
原作者:
wwzccccc
转载时间:
2026/1/2 16:13:49

详情:
● TLDs: .online / .site / .store
● Price: ~$0.20 (with Promo)
● Promo: FREEDOMAIN26

领取方式:
登入 https://www.namecheap.com
找你要的域名 (.online/.site/.store)
结算时候输入 Promo: FREEDOMAIN26
然后点击继续结算他会显示~$0.20 for 1 year

注意哈,主要登入后才能使用这个 promo 不然显示无效 promo code 或者过期。

搞公益站的大佬建议看一下本帖适合你们,或者需要备用域名的。


📌 转载信息
原作者:
dkly2004
转载时间:
2026/1/2 16:11:46