在整理旧照片的时候，从一刻相册下载下来的照片时间信息不对，发现原照片没有EXIF日期信息，导致即便下载原始文件，也不会保留文件原始创建日期问题分析一刻相册中的图片，右侧显示时间为 2020年05月25日，实际下载时间却丢失而变为 当前下载时间网盘图片实际下载图片可以发现这类文件的文件名 IMG20200525123827.jpg是明显包含时间戳的：2020-05-25 12:38，那就可以用脚本解决。问题是微信导出的 mmexport 开头的图片并没有肉眼可见的日期而是 13 位 Unix 毫秒时间戳脚本说明逻辑说明优先级：文件名中明确的日期/时间格式>文件名中 13 位 Unix 毫秒时间戳>文件名末尾的导出时间戳情况处理逻辑exif有时间,文件名无时间移入保留 文件夹，不做修改文件名有时间修改EXIF 信息后移入 修改 文件夹都无时间直接移入无时间 文件夹文件名支持类型首先明确目前已经支持的一些文件名格式示例解析结果Screenshot_YYYY-MM-DD-HH-MM-SSScreenshot_2019-04-04-16-07-36-53.jpg2019-04-04 16:07:36IMG_YYYYMMDD_HHMMSSIMG_20180526_115312_1579248363749_80.jpg2018-05-26 11:53:12任意位置连续 14 位YYYYMMDDHHMMSSIMG20180807145631.jpg、faceu_0_20190218141843.jpg2019-02-18 14:18:43mmexport<13位>mmexport1533697685086.jpg2018-08-08 11:08:05wx_camera_<13位>wx_camera_1532072568536.jpg2018-07-20 15:42:48microMsg.<13位>microMsg.1530196473712.jpg2018-06-28 22:34:33hdImg_...<13位>hdImg_...1552658489533.jpg2019-03-15 22:01:29qq_pic_merged_<13位>qq_pic_merged_1556725293245.jpg2019-05-01 23:41:33scan_filter_<13位>scan_filter_1552792846706.jpg2019-03-17 11:20:46mmqrcode<13位>mmqrcode1553238340684.png2019-03-22 15:05:40注意：文件名末尾的 _<13位>_<索引>（如 _1579248363749_80）是图片从 App 导出/备份的时间（约 2020-01-17），不是真实拍摄时间。只有当文件名中没有其它可识别时间时，才会用它兜底。支持的扩展名jpg、jpeg、png、heic、webp、gif注意：.gif 按会直接删除，大部分为表情包，不需要可以修改源码。使用说明环境安装确保Python版本>3.10安装依赖python -m pip install pillow piexif脚本使用新建文件夹，所有图片至于文件夹内的顶层目录。2.复制python脚本文件进入后，使用python *.py运行。先跑 --dry-run 预览，确认分类符合预期后再正式执行。执行会把图片移动（shutil.move）到 保留/修改/无时间 文件夹，原文件夹将不再保留这些图片。process_images.py 保留在目录中，可随时重新运行以处理新放入的图片。脚本下载此处内容需要评论回复后方可阅读
基于 PHP 的 Github 镜像加速项目（包含后台管理）
Ghproxy PHPPHP GitHub 镜像代理 · 单文件部署 · 自带 MySQL 统计后台在 GitHub 文件链接前添加镜像地址即可加速下载，302 由服务端自动跟随，流式转发大文件。前台界面后台管理界面包含请求统计、仓库封禁（按仓库封禁）、短链接管理、站点信息自定义。示例站： https://gh1.lhl.one示例站网络环境： Cloudflare + 美国Cogent ( 到 Github 10ms )
关于 AB Store Web原本 AB Store 是通过同步 Github Repo 中的 Plugin.php 来实现获取最新的插件。但是作为维护总是修改json文件有点麻烦，于是搭建了一个专门用于管理插件仓库的网站： AB Store Web，AB Store Web 开放 API，方便开发者集成。AB Store Web API> Base URL：`https://ab-store.lhl.one` > 所有接口返回 JSON：`{ ok: boolean, ... }`。鉴权方式：HttpOnly Cookie 会话（`abs_session`）或分享口令（`?token=`）。 > 人机验证：部分接口需在请求体携带 `turnstile`（前端 widget token，字段名对应 `cf-turnstile-response`）。状态码约定HTTP含义200成功400参数错误401未登录403无权访问 / 人机验证失败404资源不存在409冲突（如同目录重复提交）429频率超限插件状态枚举status含义pending审核中（新提交 / 更新审核）approved已通过（上架）rejected已拒绝archived已下架私密插件（isPrivate: true）不进入公共列表，详情页仅上传者/持完整链接访问 (插件ID+Token)。获取插件列表GET /api/plugins无需鉴权。返回所有已通过且非私密的插件。响应{ "ok": true, "plugins": [ { "id": "a1b2c3d4", "name": "AdminBeautify", "dir": "AdminBeautifyStore", "author": "LHL", "version": "1.0.21", "github": "https://github.com/...", "homepage": "https://...", "tags": ["editor", "md3"], "desc": "简介", "minVer": "1.2.0", "maxVer": "1.2.1", "uploader": "lhl", "downloads": 128, "createdAt": 1755300000000 } ] }获取单个插件详情GET /api/plugin/<id> GET /api/plugin/<id>?token=<shareToken> # 待审核 / 私密插件approved 且非私密：公开访问pending / 私密 / 其他状态：需正确 token 或登录（上传者/管理员）下载插件GET /api/download/<id> GET /api/download/<id>?token=<shareToken>302 重定向到最终下载地址，下载计数 +1。鉴权同详情接口。注意限流登录：30 次/IP/小时分享 / 注册 / 重置：20 次/IP/小时验证码：同邮箱 1 分钟 1 次
插件信息 AB Store Web 插件主页：Paid for Typecho Paid for Typecho什么是Paid?一个文章付费与订阅插件，支持单篇付费和订阅计划，内置多种支付通道和可定制主题，通过自定义字段灵活控制付费属性。文章/页面自定义字段字段说明示例paid_enable是否启用付费阅读，1 开启，0 关闭1paid_price文章价格9.90paid_methods可用支付驱动，英文逗号分隔；留空使用全部已配置渠道alipay_face,wechat,epaypaid_theme主题 ID（对应themes 下文件夹名）；留空使用插件默认主题defaultpaid_desc购买提示文案；留空使用插件默认提示文案购买后可永久查看全文。paid_plan订阅计划标识（plan_key），留空则为单篇付费monthlypaid_theme_options主题高级设置，格式字段:值，一行一个accent:#00d1ff亦可直接在“文章定价与计划”面板中设置支付通道在插件设置的“支付通道定义”中，每行一个通道，格式固定为：支付通道名称|支付驱动:配置1:配置2:配置3...支付通道名称：前台展示名，例如“支付宝扫码”。支付驱动：如 alipay_face、wechat、epay。配置项用英文冒号分隔；值里有冒号时写成 \:，例如 https\://pay.example.com。同一个驱动只保留一条配置。内置驱动配置支付宝当面付|alipay_face:https\://gateway.example.com/pay:你的AppId:通知验签Key 微信支付|wechat:https\://gateway.example.com/pay:你的商户号:通知验签Key # 易支付最后一个为支付通道，支持alipay, wxpay, qqpay等 易支付|epay:https\://pay.example.com:商户PID:商户Key:alipay支持的驱动会实时更新短代码支持（文章部分付费）本功能仅在文章已经开启付费的情况下生效，即 paid_enable为 1可用 [Paid]与 [Paid /]将需要付费的片段进行部分付费，如下图所示：这里的示例使用配置为：前面的内容 [Paid] 11111 [Paid /] [Paid] 22222 [Paid /] 后面的内容主题与设置所有主题均支持通过 主题高级设置（全局） 或文章自定义字段 paid_theme_options 自定义 CSS 变量，格式为一行一个 字段:值。通用规则字段名以 -- 开头时直接作为 CSS 变量名使用。字段名不以 -- 开头时，自动加前缀 --tp-。例如 primary:#000 → --tp-primary:#000。全局设置与文章级 paid_theme_options 冲突时，文章级优先。值中的 ; { } 会被自动过滤。1. default9 个变量，支持亮色 / 暗色双模式。字段名（简写）CSS 变量亮色默认值说明primary--tp-primary#2376e5主色（按钮、链接、选中态）bg--tp-bg#ffffff卡片背景色fg--tp-fg#152238正文文字色muted--tp-muted#6b7890次要/描述文字色border--tp-border#dbe4f1边框色input-bg--tp-input-bg#fbfcff输入框背景色chip-bg--tp-chip-bg#f4f7fc支付方式选项卡背景shadow--tp-shadowrgba(30, 55, 95, 0.12)卡片阴影颜色（透明度由插件控制）radius--tp-radius18px卡片/按钮/输入框圆角2. default213 个变量，支持亮色 / 暗色双模式。字段名（简写）CSS 变量亮色默认值说明primary--tp-primary#1abc9c主色bg--tp-bg#fff卡片背景色fg--tp-fg#333正文文字色muted--tp-muted#777次要/描述文字色border--tp-border#ececec边框色input-bg--tp-input-bg#fff输入框背景色chip-bg--tp-chip-bg#fafafa支付方式选项卡背景shadow--tp-shadowrgba(0,0,0,.06)卡片阴影颜色radius--tp-radius0.3125rem通用圆角chip-hover--tp-chip-hover#f0f0f0选项卡悬停背景radius-btn--tp-radius-btn100rem按钮圆角（胶囊形）font--tp-font'Open Sans', Helvetica, 'Microsoft Yahei', sans-serif字体栈3. default313 个变量，支持亮色 / 暗色双模式。字段名（简写）CSS 变量亮色默认值说明primary--tp-primary#5b6e8a主色（暖灰蓝）bg--tp-bg#fff卡片背景色fg--tp-fg#3a3a3a正文文字色muted--tp-muted#9a9a9a次要/描述文字色border--tp-border#eae6e0边框色input-bg--tp-input-bg#fafaf9输入框背景色chip-bg--tp-chip-bg#f6f4f1支付方式选项卡背景shadow--tp-shadowrgba(60,50,40,.045)卡片阴影颜色radius--tp-radius1.5rem通用圆角chip-hover--tp-chip-hover#efebe7选项卡悬停背景radius-sm--tp-radius-sm0.875rem小元素圆角font--tp-font系统字体栈字体栈4. default414 个变量，支持亮色 / 暗色双模式。字段名（简写）CSS 变量亮色默认值说明primary--tp-primary#5a8f7b主色（苔绿）primary-light--tp-primary-lightrgba(90,143,123,.08)主色极淡背景（悬停底纹）primary-soft--tp-primary-softrgba(90,143,123,.15)主色柔和背景（选中光晕）bg--tp-bg#fff卡片右侧主栏背景fg--tp-fg#3d3d3d正文文字色muted--tp-muted#8c8c8c次要/描述文字色border--tp-border#e8e3db边框色input-bg--tp-input-bg#fafaf7输入框背景色chip-bg--tp-chip-bg#f6f4ef支付方式选项卡背景left-bg--tp-left-bg#fafaf7左侧预览栏背景色shadow--tp-shadowrgba(50,40,30,.05)卡片阴影颜色radius--tp-radius1.25rem通用圆角radius-sm--tp-radius-sm0.75rem小元素圆角font--tp-font系统字体栈字体栈5. default5变量最多的主题，15 个变量，支持亮色 / 暗色双模式。字段名（简写）CSS 变量亮色默认值说明primary--tp-primary#d4836a主色（暖珊瑚）primary-soft--tp-primary-softrgba(212,131,106,.12)主色柔和背景primary-glow--tp-primary-glowrgba(212,131,106,.2)主色发光阴影bg--tp-bg#fff卡片背景色fg--tp-fg#3a3533正文文字色muted--tp-muted#9e9692次要/描述文字色border--tp-border#ece7e3边框色input-bg--tp-input-bg#faf9f8输入框背景色chip-bg--tp-chip-bg#f7f4f2支付方式选项卡背景chip-hover--tp-chip-hover#f0ebe7选项卡悬停背景shadow--tp-shadowrgba(60,40,30,.06)卡片阴影颜色radius--tp-radius1rem通用圆角radius-lg--tp-radius-lg1.25rem大元素圆角font--tp-font系统字体栈字体栈form-width--tp-form-width420px⭐ 表单区域最大宽度全局设置（插件配置页 → 主题高级设置（全局））primary:#0b6bcb radius:12px shadow:rgba(0,0,0,.08)文章级设置（自定义字段 paid_theme_options）primary:#e74c3c radius:8px --tp-form-width:500px亮暗色适配插件设置中的两个字段：设置项可选值说明卡片亮暗模式auto（默认）/ dark / lightdark/light 会强制卡片使用对应配色；auto 由下方“亮暗自动适配方式”决定亮暗自动适配方式system（默认）或多行 dark:xxx / light:xxx仅在paid_theme_mode 为 auto 时生效paid_theme_mode_switch 两种写法：跟随系统（默认）：填 system，前台通过 prefers-color-scheme 自动适配操作系统的亮暗设置，适用于大多数没有专门暗色开关、只跟随系统的主题。跟随主题的亮暗切换 class：如果主题是通过给 或 添加/移除某个 class 来切换亮暗（例如点击主题自带的“月亮”按钮），请按“一行一个”填写：dark:dark-mode light:light-mode即 dark: 后面写暗色模式对应的 class 名，light: 后面写亮色模式对应的 class 名。插件前端会监听 body/html 的 class 变化，实时切换卡片配色，无需刷新页面。另外如果只填写一行，如：dark:dark则主题默认为亮色，当且仅当检测到body或者html的classname出现dark的时候才会切换暗色注：当主题使用data-theme="light"形式而非className控制亮暗主题时，直接填system即可，插件会自动适配
本文章仅针对开源项目的分析。由于网上V2board的API文档实在是太少了，而且基本上没有提到有管理端的，所以我特地整理了这个文档。1. 文档说明提取范围：v2board-master 源码中的路由、控制器、请求校验（FormRequest）路由前缀：/api/v1后台前缀：/api/v1/{secure_path}{secure_path} 来自 config('v2board.secure_path', config('v2board.frontend_admin_path', hash('crc32b', config('app.key'))))统一 API 中间件：ForceJson（强制 JSON）+ Language（可用 content-language 请求头切换语言）2. 鉴权与返回约定2.1 鉴权方式用户端鉴权（middleware: user）读取 auth_data（body/query）或 Authorization 请求头通过 AuthService::decryptAuthData() 解 JWT管理端鉴权（middleware: admin）同用户端，但要求 is_admin = 1客户端订阅鉴权（middleware: client）必须传 token（query/body），并在 v2_user.token 命中员工端鉴权（middleware: staff）同用户端，但要求 is_staff = 12.2 通用成功返回大多数接口：{ "data": "..." }部分接口会额外返回：分页：{ data: [...], total: 123 }支付下单：{ type: number, data: any }2.3 通用失败返回源码大量使用 abort(status, message)。常见：认证失败：403参数/业务失败：500不存在或关闭功能：404生产环境常见响应体示例：{ "message": "未登录或登陆已过期" }或{ "message": "Order does not exist" }3. 用户端 API下述“用户端”包含：访客/登录注册（guest + passport）登录后用户能力（user）订阅客户端输出（client）3.1 访客与登录注册（Guest/Passport）3.1.1 GET /api/v1/guest/comm/config鉴权：无控制器：App\Http\Controllers\Guest\CommController@config请求参数：无返回字段（data）：tos_urlis_email_verifyis_invite_forceemail_whitelist_suffixis_recaptcharecaptcha_site_keyapp_descriptionapp_urllogo成功示例：{ "data": { "tos_url": "https://example.com/tos", "is_email_verify": 1, "is_invite_force": 0, "email_whitelist_suffix": ["gmail.com", "qq.com"], "is_recaptcha": 0, "recaptcha_site_key": null, "app_description": "V2Board is best", "app_url": "https://panel.example.com", "logo": "https://panel.example.com/logo.png" } }失败示例：通常无业务失败，配置异常时返回 500 + message3.1.2 GET /api/v1/guest/plan/fetch鉴权：无控制器：Guest\PlanController@fetch请求参数：无返回字段：data 为可展示套餐数组成功示例：{ "data": [ { "id": 1, "name": "Pro", "group_id": 1, "month_price": 1999, "show": 1 } ] }失败示例：500 + message3.1.3 POST /api/v1/passport/auth/register鉴权：无控制器：Passport\AuthController@register请求参数（body）：email required|email:strictpassword required|min:8invite_code nullable（开启强制邀请时必填）email_code nullable（开启邮箱验证码时必填）recaptcha_data nullable（开启 recaptcha 时使用）返回字段：data.token（用户订阅 token）data.is_admindata.auth_data（后续用户鉴权 JWT）成功示例：{ "data": { "token": "user_token_xxx", "is_admin": 0, "auth_data": "jwt_xxx" } }失败示例：{ "message": "Email already exists" }3.1.4 POST /api/v1/passport/auth/login鉴权：无控制器：Passport\AuthController@login请求参数（body）：email required|email:strictpassword required|min:8返回字段：同注册接口（token/is_admin/auth_data）成功示例：{ "data": { "token": "user_token_xxx", "is_admin": 1, "auth_data": "jwt_xxx" } }失败示例：{ "message": "Incorrect email or password" }3.1.5 GET /api/v1/passport/auth/token2Login鉴权：无控制器：Passport\AuthController@token2Login请求参数（query）：token nullable（存在时 302 跳转到前端登录 URL）verify nullable（存在时兑换临时 token，直接返回登录态）redirect nullable返回字段：verify 分支返回 data.token/is_admin/auth_data成功示例（verify 分支）：{ "data": { "token": "user_token_xxx", "is_admin": 0, "auth_data": "jwt_xxx" } }失败示例：{ "message": "Token error" }3.1.6 POST /api/v1/passport/auth/forget鉴权：无控制器：Passport\AuthController@forget请求参数（body）：email required|email:strictpassword required|min:8email_code required返回字段：data: true成功示例：{ "data": true }失败示例：{ "message": "Incorrect email verification code" }3.1.7 POST /api/v1/passport/auth/getQuickLoginUrl鉴权：需要 auth_data 或 Authorization控制器：Passport\AuthController@getQuickLoginUrl请求参数（body）：redirect nullableauth_data nullable（也可放请求头）返回字段：data 为一次性登录链接成功示例：{ "data": "https://panel.example.com/#/login?verify=...&redirect=dashboard" }失败示例：403 + message: 未登录或登陆已过期3.1.8 POST /api/v1/passport/auth/loginWithMailLink鉴权：无控制器：Passport\AuthController@loginWithMailLink请求参数（body）：email required|email:strictredirect nullable返回字段：data 为邮件登录链接（或 true）成功示例：{ "data": "https://panel.example.com/#/login?verify=..." }失败示例：{ "message": "Sending frequently, please try again later" }3.1.9 POST /api/v1/passport/comm/sendEmailVerify鉴权：无控制器：Passport\CommController@sendEmailVerify请求参数（body）：email required|email:strictrecaptcha_data nullable返回字段：data: true成功示例：{ "data": true }失败示例：{ "message": "Email verification code has been sent, please request again later" }3.1.10 POST /api/v1/passport/comm/pv鉴权：无控制器：Passport\CommController@pv请求参数（body）：invite_code nullable返回字段：data: true成功示例：{ "data": true }失败示例：500 + message3.2 登录后用户 API（User）统一鉴权：auth_data（body/query）或 Authorization（header）3.2.1 用户资料与账户GET /api/v1/user/info控制器：User\UserController@info参数：无返回字段（data）：emailtransfer_enablelast_login_atcreated_atbannedremind_expireremind_trafficexpired_atbalancecommission_balanceplan_iddiscountcommission_ratetelegram_iduuidavatar_url（控制器拼接）成功示例：{ "data": { "email": "u@example.com", "transfer_enable": 107374182400, "expired_at": 1780000000, "balance": 1200, "commission_balance": 500, "uuid": "uuid_xxx", "avatar_url": "https://cdn.v2ex.com/gravatar/..." } }失败：{ "message": "The user does not exist" }GET /api/v1/user/checkLogin控制器：User\UserController@checkLogin参数：无返回字段：data.is_login、data.is_admin（管理员才有）成功示例：{ "data": { "is_login": true, "is_admin": true } }失败：403 未登录POST /api/v1/user/changePassword控制器：User\UserController@changePassword参数（body）：old_password requirednew_password required|min:8返回：data: true成功示例：{ "data": true }失败：{ "message": "The old password is wrong" }POST /api/v1/user/update控制器：User\UserController@update参数（body）：remind_expire in:0,1remind_traffic in:0,1返回：data: true成功示例：{ "data": true }失败：{ "message": "Save failed" }GET /api/v1/user/getStat控制器：User\UserController@getStat参数：无返回：data 数组（顺序）[待支付订单数, 未关闭工单数, 邀请用户数]成功示例：{ "data": [1, 0, 12] }失败：403 未登录POST /api/v1/user/transfer控制器：User\UserController@transfer参数（body）：transfer_amount required|integer|min:1返回：data: true成功示例：{ "data": true }失败：{ "message": "Insufficient commission balance" }GET /api/v1/user/resetSecurity控制器：User\UserController@resetSecurity参数：无返回：data 新订阅地址成功示例：{ "data": "https://sub.example.com/api/v1/client/subscribe?token=new_token" }失败：{ "message": "Reset failed" }POST /api/v1/user/getQuickLoginUrl控制器：User\UserController@getQuickLoginUrl参数：redirect nullable返回：data 一次性登录 URL成功示例：{ "data": "https://panel.example.com/#/login?verify=..." }失败：500/403 + messageGET /api/v1/user/getActiveSession控制器：User\UserController@getActiveSession参数：无返回：data 对象（session\_id => 元信息）元信息：ip, login_at, ua成功示例：{ "data": { "session_guid_1": { "ip": "1.2.3.4", "login_at": 1762000000, "ua": "Mozilla/5.0" } } }失败：{ "message": "The user does not exist" }POST /api/v1/user/removeActiveSession控制器：User\UserController@removeActiveSession参数（body）：session_id required返回：data: true/false成功示例：{ "data": true }失败：{ "message": "The user does not exist" }3.2.2 订阅、节点与流量GET /api/v1/user/getSubscribe控制器：User\UserController@getSubscribe参数：无返回字段（data）：用户字段：plan_id, token, expired_at, u, d, transfer_enable, email, uuidplan（套餐对象，可空）subscribe_urlreset_day成功示例：{ "data": { "plan_id": 1, "token": "user_token_xxx", "u": 123, "d": 456, "transfer_enable": 107374182400, "subscribe_url": "https://sub.example.com/api/v1/client/subscribe?token=...", "reset_day": 12, "plan": { "id": 1, "name": "Pro" } } }失败：{ "message": "Subscription plan does not exist" }GET /api/v1/user/server/fetch控制器：User\ServerController@fetch参数：无返回：data 节点数组（由 ServerService::getAvailableServers 生成）额外：返回 ETag，若命中 If-None-Match 会 304成功示例：{ "data": [ { "type": "vmess", "name": "HK-01", "host": "hk.example.com", "port": 443, "rate": 1 } ] }失败：通常为 304 或 500GET /api/v1/user/stat/getTrafficLog控制器：User\StatController@getTrafficLog参数：无返回：data[]，每条含u, d, record_at, user_id, server_rate成功示例：{ "data": [ { "u": 1024, "d": 2048, "record_at": 1761955200, "user_id": 10, "server_rate": 1 } ] }失败：403 未登录GET /api/v1/user/plan/fetch控制器：User\PlanController@fetch参数（query）：id nullable（传则查单套餐）返回：传 id：data 单套餐不传：data 可售套餐数组（已按容量扣减）成功示例：{ "data": [ { "id": 1, "name": "Pro", "capacity_limit": 132 } ] }失败：{ "message": "Subscription plan does not exist" }3.2.3 订单POST /api/v1/user/order/save控制器：User\OrderController@save参数（body）：plan_id requiredperiod required|in:month_price,quarter_price,half_year_price,year_price,two_year_price,three_year_price,onetime_price,reset_pricecoupon_code nullable返回：data = trade_no成功示例：{ "data": "202607091234567890" }失败示例：{ "message": "You have an unpaid or pending order, please try again later or cancel it" }POST /api/v1/user/order/checkout控制器：User\OrderController@checkout参数（body）：trade_no requiredmethod required（支付方式 ID）token nullable（Stripe 等）返回字段：type：支付输出类型（由支付网关决定）data：支付数据（URL、表单、SDK 参数等）成功示例：{ "type": 1, "data": { "redirect": "https://pay.example.com/..." } }免费订单成功示例：{ "type": -1, "data": true }失败示例：{ "message": "Payment method is not available" }GET /api/v1/user/order/check控制器：User\OrderController@check参数（query）：trade_no required返回：data = 订单状态（0/1/2/3）成功示例：{ "data": 0 }失败：{ "message": "Order does not exist" }GET /api/v1/user/order/detail控制器：User\OrderController@detail参数（query）：trade_no required返回字段（data）：订单主字段（trade\_no、status、plan\_id、period、total\_amount...）plantry_out_plan_idsurplus_orders（若存在）成功示例：{ "data": { "trade_no": "202607091234567890", "status": 0, "plan_id": 1, "period": "month_price", "total_amount": 1999, "plan": { "id": 1, "name": "Pro" }, "try_out_plan_id": 0 } }失败：{ "message": "Order does not exist or has been paid" }GET /api/v1/user/order/fetch控制器：User\OrderController@fetch参数（query）：status nullable返回：data[] 订单列表（隐藏 id,user_id，并追加 plan）成功示例：{ "data": [{ "trade_no": "...", "status": 0, "plan": { "id": 1 } }] }失败：403 未登录GET /api/v1/user/order/getPaymentMethod控制器：User\OrderController@getPaymentMethod参数：无返回：data[]，每项含id, name, payment, icon, handling_fee_fixed, handling_fee_percent成功示例：{ "data": [ { "id": 1, "name": "Stripe", "payment": "StripeCredit", "icon": "stripe", "handling_fee_fixed": 0, "handling_fee_percent": 2.5 } ] }失败：500 + messagePOST /api/v1/user/order/cancel控制器：User\OrderController@cancel参数（body）：trade_no required返回：data: true成功示例：{ "data": true }失败：{ "message": "You can only cancel pending orders" }3.2.4 工单GET /api/v1/user/ticket/fetch控制器：User\TicketController@fetch参数（query）：id nullable（传时返回单工单 + 消息列表）返回：不传 id：data[] 工单列表传 id：data 工单对象 + message[]（含 is_me）成功示例：{ "data": { "id": 1001, "subject": "无法连接", "status": 0, "message": [ { "user_id": 10, "message": "请帮忙排查", "is_me": true } ] } }失败：{ "message": "Ticket does not exist" }POST /api/v1/user/ticket/save控制器：User\TicketController@save参数（body）：subject requiredlevel required|in:0,1,2message required返回：data: true成功示例：{ "data": true }失败：{ "message": "There are other unresolved tickets" }POST /api/v1/user/ticket/reply控制器：User\TicketController@reply参数（body）：id requiredmessage required返回：data: true成功示例：{ "data": true }失败：{ "message": "The ticket is closed and cannot be replied" }POST /api/v1/user/ticket/close控制器：User\TicketController@close参数（body）：id required返回：data: true成功示例：{ "data": true }失败：{ "message": "Close failed" }POST /api/v1/user/ticket/withdraw控制器：User\TicketController@withdraw参数（body）：withdraw_method requiredwithdraw_account required返回：data: true成功示例：{ "data": true }失败：{ "message": "Unsupported withdrawal method" }3.2.5 邀请与公告GET /api/v1/user/invite/save控制器：User\InviteController@save参数：无返回：data（布尔）成功示例：{ "data": true }失败：{ "message": "The maximum number of creations has been reached" }GET /api/v1/user/invite/fetch控制器：User\InviteController@fetch参数：无返回字段：data.codes[]data.stat[]：注册用户数有效佣金确认中佣金佣金比例可用佣金成功示例：{ "data": { "codes": [{ "code": "ABCD1234", "status": 0 }], "stat": [12, 3500, 200, 10, 1200] } }失败：500 + messageGET /api/v1/user/invite/details控制器：User\InviteController@details参数（query）：current nullable 默认 1page_size nullable 最小有效值 10返回：data[] + total成功示例：{ "data": [{ "trade_no": "...", "get_amount": 100 }], "total": 20 }失败：500 + messageGET /api/v1/user/notice/fetch控制器：User\NoticeController@fetch参数（query）：current nullable 默认 1返回：data[] + total成功示例：{ "data": [{ "id": 1, "title": "维护通知" }], "total": 5 }失败：500 + message3.2.6 其它用户接口POST /api/v1/user/coupon/check控制器：User\CouponController@check参数（body）：code requiredplan_id nullable返回：data 为 coupon 对象成功示例：{ "data": { "id": 1, "code": "SALE50", "type": 2, "value": 50 } }失败：{ "message": "Coupon cannot be empty" }GET /api/v1/user/telegram/getBotInfo控制器：User\TelegramController@getBotInfo参数：无返回：data.username成功示例：{ "data": { "username": "my_bot" } }失败：500 + messageGET /api/v1/user/comm/config控制器：User\CommController@config参数：无返回字段：is_telegramtelegram_discuss_linkstripe_pkwithdraw_methodswithdraw_closecurrencycurrency_symbolcommission_distribution_enablecommission_distribution_l1/l2/l3成功示例：{ "data": { "currency": "CNY", "currency_symbol": "¥" } }失败：500 + messagePOST /api/v1/user/comm/getStripePublicKey控制器：User\CommController@getStripePublicKey参数（body）：id required（支付方式 ID）返回：data（Stripe publishable key）成功示例：{ "data": "pk_live_xxx" }失败：{ "message": "payment is not found" }GET /api/v1/user/knowledge/fetch控制器：User\KnowledgeController@fetch参数（query）：id nullablelanguage nullablekeyword nullable返回：传 id：单文章对象不传：按分类分组对象成功示例：{ "data": { "新手": [ { "id": 1, "category": "新手", "title": "如何订阅" } ] } }失败：{ "message": "Article does not exist" }GET /api/v1/user/knowledge/getCategory控制器：User\KnowledgeController@getCategory参数：无返回：与 fetch 的分类数据一致（按分类分组）成功示例：{ "data": { "新手": [{ "id": 1, "title": "..." }] } }失败：500 + message3.3 客户端订阅 API（Client）3.3.1 GET /api/v1/client/subscribe鉴权：token（query/body）控制器：Client\ClientController@subscribe参数（query）：token requiredflag nullable（用于识别客户端协议）返回：纯文本（非 JSON），按客户端协议输出订阅成功示例：vmess://... ss://... trojan://...失败示例：403 token is null / token is error3.3.2 GET /api/v1/client/app/getConfig鉴权：token控制器：Client\AppController@getConfig参数（query）：token required返回：YAML（Clash 配置文本）成功示例：proxies: - name: HK-01 type: vmess失败：403 token 错误3.3.3 GET /api/v1/client/app/getVersion鉴权：token控制器：Client\AppController@getVersion参数：无（会读取 User-Agent）返回字段：常规：windows_version/windows_download_url/macos_version/macos_download_url/android_version/android_download_url特定 UA：仅返回单平台 version/download_url成功示例：{ "data": { "windows_version": "1.2.3", "windows_download_url": "https://example.com/win.exe", "macos_version": "1.2.3", "macos_download_url": "https://example.com/mac.dmg", "android_version": "1.2.3", "android_download_url": "https://example.com/app.apk" } }失败：403 token 错误4. 管理端 API（Admin）统一前缀：/api/v1/{secure_path}统一鉴权：admin 中间件（auth_data 或 Authorization，且 is_admin=1）注：以下按功能分组，均给出控制器溯源。部分无 FormRequest 的接口参数来自控制器内 validate() 或 input() 判空逻辑。[付费阅读] 管理端API购买可永久查看。5. 访客回调与其它路由补充5.1 支付回调GET|POST /api/v1/guest/payment/notify/{method}/{uuid}控制器：Guest\PaymentController@notify鉴权：无（依赖支付回调签名与 uuid）参数：网关自定义成功输出：success 或网关 custom_result失败：500 fail/verify error/handle error5.2 Telegram webhookPOST /api/v1/guest/telegram/webhook控制器：Guest\TelegramController@webhook参数：access_token（query，必须等于 md5(telegram_bot_token)）body 为 Telegram update成功：无固定 JSON（命令处理后可能无输出）失败：401（token 不匹配）5.3 服务端对接入口（节点上报/拉取）ANY /api/v1/server/{class}/{action}路由：ServerRoute行为：动态实例化 App\Http\Controllers\Server\{Class}Controller@{action}说明：该入口面向节点程序与后端通讯，不属于前台用户/后台管理 UI API6. 字段与状态码补充6.1 常见金额与流量单位金额：多数为“分”（int），前端显示需 /100流量：多数为字节（bytes），部分界面按 GB 换算6.2 常见状态含义（按源码习惯）订单 status：0 待支付1/2/3 视业务流程定义（已支付/取消/已完成等）工单 status：0 未关闭1 已关闭6.3 登录态结构（AuthService::generateAuthData）{ "token": "用户订阅token", "is_admin": 0, "auth_data": "JWT" }7. 主要源码位置路由：app/Providers/RouteServiceProvider.phpapp/Http/Routes/*.php控制器：app/Http/Controllers/Passport/*app/Http/Controllers/Guest/*app/Http/Controllers/User/*app/Http/Controllers/Client/*app/Http/Controllers/Admin/*app/Http/Controllers/Admin/Server/*请求校验：app/Http/Requests/Passport/*app/Http/Requests/User/*app/Http/Requests/Admin/*鉴权：app/Http/Middleware/User.phpapp/Http/Middleware/Admin.phpapp/Http/Middleware/Client.phpapp/Services/AuthService.php点击访问原文查看完整内容
插件信息 Typecho 文章 SEO 元信息增强插件 · 支持 AI 自动生成 Description / KeywordsAB Store Web 插件主页: TypechoMeta 支持一键生成 Desc 和 关键词支持自定义AI提示词与AI配置支持博客根目录
TypechoBio 个人主页插件 插件信息 支持指定域名，复用Typecho的评论系统，并指定评论文章ID。AB Store Web 插件主页: TypechoBio 安装方式方式一：AB-Store 一键安装（推荐）安装 AdminBeautify 插件后，进入后台 AB-Store 应用商店，搜索 TypechoBio 即可一键安装并获取后续更新。您也可到 TypechoBio - AB Store Web 上获取口令安装。方式二：手动安装下载最新 Release 压缩包 / 从 AB Store Web 上下载解压为 TypechoBio 文件夹上传至 Typecho 的 usr/plugins/ 目录登录后台 → 控制台 → 插件管理 → 启用 TypechoBio方式三：Git 克隆cd /path/to/typecho/usr/plugins/ git clone https://github.com/lhl77/TypechoBio.git TypechoBio可安装主题ImsyyDuckfolioZyyoDmegoWexuoIAMITypechoBio 主题适配文档本文面向 TypechoBio 主题开发者，目标是把“一个现成前端页面或主题源码”稳定适配到 TypechoBio。文档内容基于当前插件实现整理，和实际代码行为对齐，适用于以下场景：新增一个本地主题到 usr/plugins/TypechoBio/templates/<theme>/将外部 HTML、静态站点、Vite 构建产物、主题源码迁移到 TypechoBio为主题接入插件统一的配置系统、评论区、独立域名运行时上下文为主题打包成远程主题市场可安装的 zip 包1. 运行模型TypechoBio 不是 Typecho 前台主题，而是“独立域名命中后接管输出”的插件入口。当前请求命中插件后，执行链路大致如下：插件根据 bio_hosts 判断当前 Host 是否需要被 TypechoBio 接管。若命中，则先处理主题专属 API，再处理主题静态资源请求。非资源请求会进入 usr/plugins/TypechoBio/templates/index.php。templates/index.php 读取当前启用主题、主题配置、用户状态、评论数据等上下文。最终 require usr/plugins/TypechoBio/templates/<theme>/home.php 输出完整页面。这意味着：home.php 需要自己输出完整 HTML 页面，不要假设 Typecho 默认前台模板会包裹你。主题运行时可直接访问数据库、登录态、插件配置，但应优先使用插件已整理好的 $typechoBioContext。资源、评论、配置都应按 TypechoBio 的约定来接，而不是直接照搬原主题的 CMS 假设。2. 主题最小目录结构一个主题至少要满足下面的目录结构，才能被 TypechoBio 识别：usr/plugins/TypechoBio/ └── templates/ └── my-theme/ ├── home.php ├── theme.json ├── assets/ │ ├── app.css │ └── app.js ├── images/ └── ...识别规则很严格：templates/ /home.php 必须存在templates/ /theme.json 必须存在两者缺一不可，否则 discoverThemes() 不会把这个目录识别为可用主题建议：主题目录名只使用字母、数字、下划线、连字符目录名与 theme.json 中的主题标识保持一致把当前主题的静态资源全部放在本主题目录下，不要散落到插件根目录3. home.php 的职责home.php 是主题真正的入口文件，建议把它当成“页面壳层模板”。它至少应负责：输出完整 HTML 文档结构读取 $typechoBioContext根据 themeConfig 渲染页面内容正确引用当前主题静态资源按需接入评论区、代理接口、额外共享片段一个最小可运行的例子如下：<?php if (!defined('__TYPECHO_ROOT_DIR__')) { exit; } $ctx = isset($typechoBioContext) && is_array($typechoBioContext) ? $typechoBioContext : array(); $themeConfig = isset($ctx['themeConfig']) && is_array($ctx['themeConfig']) ? $ctx['themeConfig'] : array(); $siteTitle = isset($ctx['siteTitle']) ? (string) $ctx['siteTitle'] : 'TypechoBio'; $themeAssetBase = isset($ctx['themeAssetBase']) ? (string) $ctx['themeAssetBase'] : ''; $pluginRoot = dirname(dirname(__DIR__)); ?> <?php echo htmlspecialchars($siteTitle, ENT_QUOTES, 'UTF-8'); ?> <link rel="stylesheet" href="<?php echo htmlspecialchars($themeAssetBase . 'assets/app.css', ENT_QUOTES, 'UTF-8'); ?>"> <?php echo htmlspecialchars($siteTitle, ENT_QUOTES, 'UTF-8'); ?> <?php $bioCommentOptions = array( 'enabled' => true, 'container_class' => 'my-theme-comments', ); require $pluginRoot . '/views/comment-section.php'; require $pluginRoot . '/views/comment-script.php'; ?> <script src="<?php echo htmlspecialchars($themeAssetBase . 'assets/app.js', ENT_QUOTES, 'UTF-8'); ?>"> 4. 运行时上下文 $typechoBioContexttemplates/index.php 在载入 home.php 前，会把通用运行时信息整理到 $typechoBioContext 中。主题应优先从这里取值，而不是自己重新拼一遍。当前常用字段如下。4.1 基础站点信息host：当前命中的域名siteTitle：站点标题siteUrl：站点地址adminUrl：后台地址siteIcon：插件设置中的站点图标4.2 用户信息userLogged：当前是否已登录userName：登录用户名userGroup：用户组4.3 主题与插件信息themeKey：当前主题目录名themeConfig：当前主题解析后的配置数组themeAssetBase：当前主题静态资源基址pluginAssetBase：插件根资源基址pluginNamepluginVersionpluginLink4.4 评论与接口信息profileApi：登录态查询接口commentApi：评论提交接口commentTarget：评论目标文章信息commentList：已审核评论列表commentResultcommentMessage4.5 其他常用数据posts：最近文章列表，当前实现默认取最近 10 篇已发布文章headPreMeta：后台配置的自定义 head 片段（meta 前置）headCustomCss：后台配置的自定义 CSSheadCustomJs：后台配置的自定义 JSloadStart：请求开始时间4.6 读取示例<?php $ctx = $typechoBioContext; $config = isset($ctx['themeConfig']) ? $ctx['themeConfig'] : array(); $title = isset($ctx['siteTitle']) ? $ctx['siteTitle'] : ''; $logged = !empty($ctx['userLogged']); $commentApi = isset($ctx['commentApi']) ? $ctx['commentApi'] : '';5. theme.json 规范theme.json 是 TypechoBio 识别主题元数据和配置表单的唯一标准文件。一个推荐的完整示例如下：{ "name": "My Theme", "version": "1.0.0", "description": "一个适配到 TypechoBio 的主题", "fields": [ { "key": "hero_title", "label": "首屏标题", "type": "text", "default": "Hello TypechoBio", "description": "显示在首屏的大标题" }, { "key": "hero_desc", "label": "首屏描述", "type": "textarea", "default": "这是主题说明", "description": "支持多行文本" }, { "key": "theme_mode", "label": "配色模式", "type": "select", "default": "system", "description": "主题初始配色模式", "options": { "light": "浅色", "dark": "深色", "system": "跟随系统" } }, { "key": "show_comments", "label": "启用评论区", "type": "checkbox", "default": "1", "description": "关闭后不渲染评论区" } ] }5.1 顶层字段name：主题显示名称version：主题版本号，主题市场比较更新时会用到description：主题简介fields：配置字段数组5.2 fields 数组支持的字段每个字段对象支持：key：字段键名，必填，只允许字母、数字、下划线label：后台显示名称type：字段类型default：默认值description：后台说明文字options：仅 select 类型使用当前插件支持的 type 只有以下五种：texttextareanumberselectcheckbox注意事项：key 不能包含空格、连字符、点号、斜杠不支持对象嵌套配置结构，复杂数据请序列化成字符串或 JSON 字符串checkbox 最终会被规范化为字符串 1 或 0select 的 options 必须是对象，键是实际值，值是后台显示文本5.3 默认值行为TypechoBio 会根据 theme.json 自动做以下同步：新增字段时，自动把默认值补进配置 JSON删除字段时，自动从配置 JSON 和后台表单中移除新安装主题时，当前页会把该主题默认配置直接同步到主题配置 JSON因此，default 字段应认真填写，不要省略，否则该字段首次接入时会是空字符串。6. 配置系统是怎么存的TypechoBio 当前同时维护两层配置表示，但开发时应把 JSON 当成最终结构：统一 JSON：bio_theme_configs_json后台表单字段：bio_theme_cfg__{theme}__{key}说明：配置页展示的是常规表单项表单提交时会自动同步回 bio_theme_configs_json插件内部也会在主题增删字段时自动做规范化同步主题开发者应遵守：在 home.php 中优先读取 $typechoBioContext['themeConfig']不要自己拼接 bio_theme_cfg__... 去数据库取值复杂数组、列表、对象数据建议直接存在 text 或 textarea 字段里，再在主题内自己 json_decode例如：<?php $config = isset($typechoBioContext['themeConfig']) ? $typechoBioContext['themeConfig'] : array(); $socialLinks = array(); if (!empty($config['social_links'])) { $decoded = json_decode((string) $config['social_links'], true); if (is_array($decoded)) { $socialLinks = $decoded; } }7. 静态资源接入规范TypechoBio 会拦截当前Typecho前台主题的资源请求，并直接从当前启用TypechoBio主题目录回源。7.1 推荐写法最稳妥的写法是始终使用 $typechoBioContext['themeAssetBase'] 来拼资源路径：<link rel="stylesheet" href="<?php echo htmlspecialchars($typechoBioContext['themeAssetBase'] . 'assets/app.css', ENT_QUOTES, 'UTF-8'); ?>"> <script src="<?php echo htmlspecialchars($typechoBioContext['themeAssetBase'] . 'assets/app.js', ENT_QUOTES, 'UTF-8'); ?>"> <img src="<?php echo htmlspecialchars($typechoBioContext['themeAssetBase'] . 'images/avatar.png', ENT_QUOTES, 'UTF-8'); ?>" alt="avatar">7.2 资源放置建议建议把资源全部收敛到主题目录内部，例如：templates/my-theme/ ├── home.php ├── theme.json ├── assets/ ├── css/ ├── js/ ├── images/ ├── img/ └── font/7.3 根路径资源的兼容插件当前会对以下请求做主题目录回源：当前主题目录下的普通请求路径/assets/.../favicon.ico/apple-touch-icon.png/robots.txt这意味着某些原主题若大量引用根路径资源，例如 /assets/app.css，在当前启用主题下通常也能被命中。但仍建议在适配时主动改成基于 themeAssetBase 的显式路径，原因是：路径更清晰不依赖隐式兼容逻辑方便后续迁移、调试和主题切换7.4 大小写问题Linux 文件系统区分大小写，资源路径必须和真实文件名完全一致。不要把：Pacifico-Regular.ttf 写成 pacifico-regular.ttfImages/avatar.png 写成 images/avatar.pngCSS 中的相对路径也一样需要严格匹配真实目录大小写。7.5 MIME 类型插件已经对常见资源做了固定 MIME 映射，包括：cssjs / mjsjson / mapsvgicopng / jpg / webp / avifwoff / woff2 / ttf / otf / eot所以主题构建产物可以直接放进主题目录，不必额外再配静态服务器。8. 评论区接入TypechoBio 已提供统一的评论区渲染片段，主题适配时优先复用，不建议每个主题自己重复造一套评论后端。8.1 接入前提后台插件配置中必须填写：评论文章 ID，即 bio_comment_cid未填写时，共享评论组件会输出“尚未配置评论文章 ID”的提示。8.2 最小接入方式在主题 home.php 中加入：<?php $pluginRoot = dirname(dirname(__DIR__)); $config = isset($typechoBioContext['themeConfig']) ? $typechoBioContext['themeConfig'] : array(); $showComments = !isset($config['show_comments']) || in_array((string) $config['show_comments'], array('1', 'true', 'on', 'yes'), true); $bioCommentOptions = array( 'enabled' => $showComments, 'container_tag' => 'section', 'container_class' => 'my-theme-comment-section', 'id' => 'bio-comments', 'title' => '留言板', 'title_tag' => 'h3', 'reload_on_success' => true, 'reload_delay' => 500, ); require $pluginRoot . '/views/comment-section.php'; require $pluginRoot . '/views/comment-script.php';8.3 可用选项$bioCommentOptions 当前支持：enabledcontainer_tagcontainer_classidtitletitle_tagreload_on_successreload_delaytarget_missing_textempty_list_text8.4 共享类名共享评论模板会输出以下类名，主题只需给这些类写样式：.bio-comment-heading.bio-comment-status.bio-comment-form.bio-comment-form-row.bio-comment-field.bio-comment-actions.bio-comment-submit.bio-comment-tip.bio-comment-list.bio-comment-item.bio-comment-meta.bio-comment-author.bio-comment-time.bio-comment-reply-label.bio-comment-replyto.bio-comment-text.bio-comment-children.bio-comment-empty8.5 主题侧建议评论样式建议遵守当前项目已有方向：突出用户名弱化发布时间强调正文内容嵌套回复层级要可读，不要只靠细线缩进9. 远程主题仓库与 zip 包要求如果你的主题要进入 TypechoBio 后台的“获取主题”市场，需要同时准备：可安装的 zip 包远程 themes.json 目录条目9.1 zip 包要求插件安装时会：下载 zip 包解压到临时目录在解压结果中查找合法主题目录合法主题目录必须同时包含 home.php 和 theme.json成功后复制到 usr/plugins/TypechoBio/templates/ /因此，zip 包内建议是这样的结构：my-theme.zip └── my-theme/ ├── home.php ├── theme.json ├── assets/ └── ...不建议把一堆文件直接散在 zip 根目录。9.2 themes.json 条目建议目录结构建议为：{ "schema_version": 1, "themes": [ { "dir": "my-theme", "name": "My Theme", "description": "主题简介", "version": "1.0.0", "cover": "https://example.com/cover.jpg", "zip_url": "https://example.com/my-theme.zip", "github_url": "https://github.com/example/my-theme", "demo_url": "https://demo.example.com" } ] }字段说明：dir：本地安装目录名name：显示名称description：介绍version：远程版本号cover：封面图zip_url：zip 下载链接github_url：源码仓库或介绍页demo_url：在线演示建议保证：dir 与 zip 内主题目录名一致version 与 theme.json 中的 version 一致cover、zip_url 使用稳定可访问地址10. 从现成前端项目迁移时的建议流程如果你是从一个已有 HTML / Vue / React / 静态主题迁移到 TypechoBio，建议按下面顺序做：第一步：先跑起来页面壳层把最终可部署 HTML 结构放进 home.php把 CSS / JS / 字体 / 图片放进主题目录先确保在 TypechoBio 独立域名下能正常打开页面第二步：再接插件上下文把站点标题替换成 $typechoBioContext['siteTitle']把社交、导航、介绍等可变内容改为 themeConfig把原硬编码资源路径改成 themeAssetBase第三步：再接后台可配置字段在 theme.json 里声明字段进入插件配置页确认字段能显示切换到该主题时确认配置项能显示修改字段并保存，确认 home.php 中能读到新值第四步：最后再接评论和 API如需评论区，先配置 show_comments 再引入共享评论片段如需远程接口，优先做同源代理，不要让浏览器直连不稳定第三方11. 常见坑与排查11.1 主题不显示在后台“启用主题”里优先检查：templates/<theme>/home.php 是否存在templates/<theme>/theme.json 是否存在theme.json 是否是合法 JSON11.2 主题显示了，但配置项不出现优先检查：theme.json 顶层是否有 fieldsfields 是否是数组每个字段是否都带合法 keykey 是否只包含字母、数字、下划线11.3 默认值没有生效优先检查：字段是否写了 defaultcheckbox 默认值是否写成 1 或 0复杂 JSON 字符串默认值是否本身就是合法 JSON11.4 资源 404优先检查：路径是否基于 themeAssetBase文件名大小写是否和真实文件一致CSS 内相对路径是否正确资源是否真的放在当前启用主题目录里11.5 评论区不显示优先检查：插件设置里是否填写了 评论文章 ID主题配置里是否把 show_comments 关掉了home.php 是否真的 require 了 views/comment-section.php 和 views/comment-script.php11.6 主题依赖第三方 API，页面打开就报错优先检查：是否仍在浏览器直连跨域 API上游是否有 TLS / 证书 / 频控问题是否应该改为插件本地代理代理失败时是否提供了同形 fallback 数据12. 推荐的适配清单一个新主题适配完成前，至少逐项确认以下内容：主题目录下同时存在 home.php 和 theme.json页面在独立域名下能正常打开静态资源全部命中后台能识别并显示主题后台能切换到该主题主题配置项能显示默认值能正确进入 themeConfig修改配置后前台能读取到新值评论区可按主题配置开关若有第三方 API，则已改为同源代理或有降级方案zip 包安装后，当前页无需手动刷新也能看到配置项与默认值13. 建议的目录模板可直接参考下面这个目录组织：templates/my-theme/ ├── home.php ├── theme.json ├── assets/ │ ├── app.css │ ├── app.js │ └── vendor/ ├── images/ ├── font/ ├── partials/ │ ├── hero.php │ └── footer.php └── README.md其中：home.php 负责装配页面partials/ 放主题内部拆分片段assets/ 放构建产物README.md 可写主题自己特有的说明，但插件总规范应以本文为准
本编辑器 AB Vditor 基于插件AdminBeautify，Vditor基于原版3.11.2版本支持 所见即所得、实时预览、分屏编辑 这三个Vditor的模式，并可快速切换支持 短代码 显示，如图为 Mirages 主题自带的一些工具照顾到兼容性，故使用 单独的复制界面，从而避免某些情况下兼容性的问题支持 全屏编辑，让您更加专注于创作～Github 地址: AB Admin您也可以访问 AB-Admin - AB Store Web 以获取和下载插件。如何使用？先安装 AB Admin: https://blog.lhl.one/artical/977.html#%E5%AE%89%E8%A3%85%E6%96%B9%E6%B3%95在控制台-插件-AB-Admin中的编辑器设置启用AB Vditor即可。如图。
AB Avatar 插件信息 Admin Beautify 专用头像管理插件，支持更换Gravatar源(本地代理)，支持自定义头像您也可以访问 AB Avatar - AB Store Web 以获取和下载插件。 功能特色功能说明Gravatar 多源切换支持官方 / loli / Cravatar / 自定义域名 / 本地代理代理地址编码本地代理使用 token 编码参数，避免暴露明文 Gravatar 格式本地头像专属路由本地存储头像通过签名 token 路由访问，不直接暴露文件路径自定义头像上传个人设置页与用户编辑页支持上传、裁剪、压缩、恢复PicUp 集成可选 PicUp 存储后端，支持上传策略（Profile）选择管理面板插件设置页内置“自定义头像管理”，支持恢复为 Gravatar安全防护支持代理防盗链、每 IP 限流、代理缓存天数配置截图编辑头像可上传自定义头像上传自行裁剪安装方式一：AB-Store 一键安装（推荐）安装 AdminBeautify 插件后，进入后台 AB-Store 应用商店，搜索 AB Avatar 即可一键安装并获取后续更新。您也可以访问 AB Avatar - AB Store Web 获取口令安装。方式二：下载压缩包前往 GitHub 仓库 / AB Store Web 下载源码或发布包解压后将目录重命名为 AdminBeautifyAvatar上传到 Typecho 的 usr/plugins/ 目录在后台进入 控制台 -> 插件，启用 AdminBeautifyAvatar方式三：Git 克隆cd /your-site/usr/plugins/ git clone https://github.com/lhl77/Typecho-Plugin-AdminBeautifyAvatar.git AdminBeautifyAvatar目录示例：your-site/ └── usr/ └── plugins/ └── AdminBeautifyAvatar/依赖说明必需：AdminBeautify可选：PicUp（作为头像存储后端）当 AdminBeautify 未启用时，插件设置页仅显示依赖提示，不显示其它配置项。主要配置项前台 Gravatar 源：官方 / loli / Cravatar / 自定义 / 本地代理本地代理上游源、缓存天数、限流、防盗链允许用户上传自定义头像自定义头像大小限制（MB）头像存储后端：本地 / PicUpPicUp 上传策略（Profile）前台自动替换 Gravatar 地址路由说明路由作用/ab-avatar/gravatar/{token}头像代理路由（token 编码参数）/ab-avatar/local/{token}本地头像专属访问路由（签名 token）/ab-avatar/upload当前登录用户上传自定义头像/ab-avatar/restore当前登录用户恢复为 Gravatar/ab-avatar/manage管理员恢复指定用户头像 / 代上传使用建议先启用 AdminBeautify，再启用本插件默认推荐“前台 Gravatar 源 = 本地代理”若站点开启 CDN / 缓存，建议保持 proxy_hotlink_protection 与 proxy_rate_limit_per_min 为开启状态若使用 PicUp，请先确认 PicUp 插件已正确配置并可正常上传常见问题1) 访问代理头像返回 invalid token说明当前链接不是插件生成的编码代理地址，或 token 已损坏。请确保头像 URL 来自插件自动生成逻辑。2) 用户头像上传失败请检查：PHP gd 扩展是否可用上传文件格式是否为 JPG/PNG/GIF/WEBP上传文件大小是否超过插件设置阈值3) 设置页更新后样式或脚本不生效插件静态资源带版本参数（基于文件修改时间），如仍未更新，建议清理站点缓存和 OPcache。
这可能是史上颜值最高的 Typecho OAuth 第三方登录插件目录AB OAuthAdminBeautify 专用 OAuth 登录插件，支持QQ / 微信 / GitHub / Google / MSN / 新浪 / 豆瓣 / 百度 / OIDC等。精简显示完整显示OAuth 配置说明Github进入Github Developer Settings，左侧选择OAuth App，然后点击New OAuth App接着复制你的回调地址然后按照图片填入相关信息接着点击 generate client secret生成密钥将ClientID和生成的Client Secret分别填入AB-OAuth插件后台中即可Google进入Google API 控制台，点击进入OAuth 权限请求界面进入客户端，然后创建客户端填入相关客户端信息，选择Web 应用，并粘贴你的回调地址添加完成后弹出窗口将客户端ID和生成的客户端密钥分别填入AB-OAuth插件后台中即可Microsoft (MSN)进入Azure管理页面，点击进入Entry ID管理页面然后根据使用情况进行配置，粘贴您的回调地址，得到客户端ID进入证书与密码，添加新的客户端密码，其值为您的客户端密码检查API权限，是否有User.Read其他的正在编辑中