Skip to content

Commit

Permalink
优化CDN缓存配置,提升性能;支持设置管理端删除、拉黑等操作绕过缓存,实时生效,见readme3.1.3.9节
Browse files Browse the repository at this point in the history
  • Loading branch information
MarSeventh committed Dec 11, 2024
1 parent d508694 commit 65dfc5f
Show file tree
Hide file tree
Showing 31 changed files with 102 additions and 27 deletions.
69 changes: 46 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

此外,上传**并没有严格限制文件类型**,理论上你可以上传**任何**文件,但是暂时不会针对图片和视频外的文件进行特殊优化和适配。

![CloudFlare](static/202410011443570.png)
![CloudFlare](static/readme/202410011443570.png)

# 2.Features

Expand All @@ -74,7 +74,7 @@
>
> Cloudflare R2渠道:上传大小不限,但超过免费额度会扣费,详见[Pricing | Cloudflare R2 docs](https://developers.cloudflare.com/r2/pricing/)
>
> ![](static/202411052346701.png)
> ![](static/readme/202411052346701.png)
- **上传方式多样**:支持多种上传方式(**拖拽点击、粘贴**)(Web/API)

Expand Down Expand Up @@ -153,19 +153,19 @@

1.[@BotFather](https://t.me/BotFather)发送`/newbot`,按照提示输入bot的备注、用户名等信息。成功创建后获得`TG_BOT_TOKEN`

![](static/202409071744569.png)
![](static/readme/202409071744569.png)

2. 创建一个新的频道(Channel),进入新建的频道,选择频道管理,将刚才创建的机器人设为频道管理员。

![](static/202409071758534.png)
![](static/readme/202409071758534.png)

![](static/202409071758796.png)
![](static/readme/202409071758796.png)

![](static/202410291531473.png)
![](static/readme/202410291531473.png)

3.[@VersaToolsBot](https://t.me/VersaToolsBot)**转发**一条第2步新建频道中的消息,获取`TG_CHAT_ID`(频道ID)

![](static/202409071751619.png)
![](static/readme/202409071751619.png)

</details>

Expand All @@ -176,15 +176,15 @@

1. 前往Cloudflare Dashboard,选择`R2 存储对象`

![](static/202411052318204.png)
![](static/readme/202411052318204.png)

2. 选择`创建存储桶`,名称随意,填完后点击`创建存储桶`即可完成创建

![](static/202411052319402.png)
![](static/readme/202411052319402.png)

3. 根据需求可选操作:如果**需要启用图像审查,需要开启存储桶的公网访问权限**,有两种开启方式,详见下图。无论你选择哪种方式,都需要记下完整的公网访问链接,格式为`https://xxxx.xxx`

![image-20241105232759131](static/202411052327191.png)
![image-20241105232759131](static/readme/202411052327191.png)

</details>

Expand Down Expand Up @@ -221,7 +221,7 @@

2. 打开 Cloudflare Dashboard,进入 Pages 管理页面,选择创建项目,选择`连接到 Git 提供程序`

![1](static/202407201047300.png)
![1](static/readme/202407201047300.png)

3. 按照页面提示输入项目名称,选择需要连接的 git 仓库,点击`部署站点`

Expand All @@ -236,21 +236,21 @@
将前面新建的存储桶绑定到项目,名称为`img_r2`

![](static/202411052323183.png)
![](static/readme/202411052323183.png)

如果后续要开启**图像审查**,需要设置`R2PublicUrl`环境变量,值为前面记下的**R2存储桶公网访问链接**

![](static/202411052330663.png)
![](static/readme/202411052330663.png)

</details>

3. **绑定KV数据库**

- 创建一个新的KV数据库

> ![](static/202408261035367.png)
> ![](static/readme/202408261035367.png)
>
> ![](static/202408261037971.png)
> ![](static/readme/202408261037971.png)
- 进入项目对应`设置`->`函数`->`KV 命名空间绑定`->`编辑绑定`->`变量名称`,填写`img_url`,KV命名空间选择刚才创建好的KV数据库

Expand Down Expand Up @@ -298,7 +298,7 @@
正常启动,控制台输出如下:

![202408191829163](static/202408191855625.png)
![202408191829163](static/readme/202408191855625.png)

</details>

Expand Down Expand Up @@ -394,6 +394,27 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
设置`AllowRandom`环境变量,值为`true`,以从图床中随机获取一张图片,详见[API文档](#4.2.2随机图API)。
#### 3.1.3.9管理端删除、拉黑等操作优化
正常情况下,因为CloudFlare CDN缓存的存在,在管理端进行删除、拉黑、加白名单等操作不会立即生效,需要等到缓存过期才能生效。
**为了让操作立即生效**,请添加`CF_ZONE_ID`、`CF_EMAIL`、`CF_API_KEY`环境变量,获取方式如下:
<details>
<summary>操作详情</summary>
`CF_ZONE_ID`:
![image-20241211123633692](static/readme/20241211123633692.png)
`CF_EMAIL`:即登录CloudFlare账号的邮箱
`CF_API_KEY`:
![image-20241211140019607](static/readme/202412111400766.png)
</details>
##### </details>
### 3.1.4其他操作指南
Expand All @@ -404,11 +425,11 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
1. **修改环境变量方式**:
![](static/202408261040233.png)
![](static/readme/202408261040233.png)
**修改环境变量后需要重新部署才能生效!**
![](static/202408261041507.png)
![](static/readme/202408261041507.png)
2. **程序更新方式**:
Expand All @@ -417,7 +438,7 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
如果有新的环境变量需要添加,请根据文档要求进行添加,然后重试部署。
![](static/202409161736365.png)
![](static/readme/202409161736365.png)
</details>
Expand All @@ -431,9 +452,9 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
## 4.1Web端使用方式
![](static/202412092301397.png)
![](static/readme/202412092301397.png)
![](static/202412092305405.png)
![](static/readme/202412092305405.png)
## 4.2API文档
Expand Down Expand Up @@ -537,6 +558,7 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
26. :white_check_mark:~~文件详情增加文件大小记录~~(2024.12.10已完成)
27. :hourglass_flowing_sand:支持管理员自定义全局默认链接前缀
28. :white_check_mark:~~开放更多文件格式~~(2024.12.9已完成)
29. :white_check_mark:~~进行删除、加入白名单、加入黑名单等操作时,自动清除CF CDN缓存,避免延迟生效~~(2024.12.11已完成)
</details>
Expand All @@ -551,6 +573,7 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
1. :hourglass_flowing_sand:增加新的图片审查渠道
1. :white_check_mark:~~R2渠道在管理端删除时,存储桶同步删除~~(2024.12.4已修复)
1. :white_check_mark:~~读取文件响应头增加允许跨域头`access-control-allow-origin: *`~~(2024.12.9已修复)
1. :white_check_mark:~~上传界面加入访问限制白名单~~(2024.12.11已修复)
</details>
Expand All @@ -568,11 +591,11 @@ Web端在登录页面输入你的**认证码**即可登录使用;API端需要
- PicGo插件设置中搜索`web-uploader`,安装可自定义前缀的版本,如图:
![](static/202408231141491.png)
![](static/readme/202408231141491.png)
- 打开`图床设置`->`自定义Web图床`->`Default`,然后按照下图方式配置,注意API地址和自定义图片URL前缀按照自己的域名进行修改。(**如果设置了`AUTH_CODE`,一定以`?authCode=your_authCode`的方式添加到API地址后面**):
![](static/202408261959174.png)
![](static/readme/202408261959174.png)
- 设置完成,确定即可使用PicGo上传到自建的图床。
Expand Down
13 changes: 13 additions & 0 deletions functions/api/manage/block/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export async function onRequest(context) {
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
// 组装 CDN URL
const url = new URL(request.url);
const cdnUrl = `https://${url.hostname}/file/${params.id}`;

// 解码params.id
params.id = decodeURIComponent(params.id);

Expand All @@ -18,6 +22,15 @@ export async function onRequest(context) {
value.metadata.ListType = "Block"
await env.img_url.put(params.id,"",{metadata: value.metadata});
const info = JSON.stringify(value.metadata);

// 清除CDN缓存
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-Auth-Email': `${env.CF_EMAIL}`, 'X-Auth-Key': `${env.CF_API_KEY}`},
body: `{"files":["${ cdnUrl }"]}`
};
await fetch(`https://api.cloudflare.com/client/v4/zones/${ env.CF_ZONE_ID }/purge_cache`, options);

return new Response(info);

}
12 changes: 12 additions & 0 deletions functions/api/manage/delete/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export async function onRequest(context) {
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
// 组装 CDN URL
const url = new URL(request.url);
const cdnUrl = `https://${url.hostname}/file/${params.id}`;

// 解码params.id
params.id = decodeURIComponent(params.id);

Expand All @@ -22,6 +26,14 @@ export async function onRequest(context) {

await env.img_url.delete(params.id);
const info = JSON.stringify(params.id);

// 清除CDN缓存
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-Auth-Email': `${env.CF_EMAIL}`, 'X-Auth-Key': `${env.CF_API_KEY}`},
body: `{"files":["${ cdnUrl }"]}`
};
await fetch(`https://api.cloudflare.com/client/v4/zones/${ env.CF_ZONE_ID }/purge_cache`, options);

return new Response(info);
} catch (e) {
Expand Down
13 changes: 13 additions & 0 deletions functions/api/manage/white/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ export async function onRequest(context) {
next, // used for middleware or to fetch assets
data, // arbitrary space for passing data between middlewares
} = context;
// 组装 CDN URL
const url = new URL(request.url);
const cdnUrl = `https://${url.hostname}/file/${params.id}`;

// 解码params.id
params.id = decodeURIComponent(params.id);

Expand All @@ -18,6 +22,15 @@ export async function onRequest(context) {
value.metadata.ListType = "White"
await env.img_url.put(params.id,"",{metadata: value.metadata});
const info = JSON.stringify(value.metadata);

// 清除CDN缓存
const options = {
method: 'POST',
headers: {'Content-Type': 'application/json', 'X-Auth-Email': `${env.CF_EMAIL}`, 'X-Auth-Key': `${env.CF_API_KEY}`},
body: `{"files":["${ cdnUrl }"]}`
};
await fetch(`https://api.cloudflare.com/client/v4/zones/${ env.CF_ZONE_ID }/purge_cache`, options);

return new Response(info);

}
22 changes: 18 additions & 4 deletions functions/file/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ export async function onRequest(context) { // Contents of context object
if (fileType) {
headers.set('Content-Type', fileType);
}
// 根据Referer设置CDN缓存策略,如果是从/或/dashboard等访问,则仅允许浏览器缓存;否则设置为public,缓存时间为1年
if (Referer && Referer.includes(url.origin)) {
headers.set('Cache-Control', 'private, max-age=86400');
} else {
headers.set('Cache-Control', 'public, max-age=31536000');
}

// 返回图片
const newRes = new Response(object.body, {
status: 200,
headers,
Expand Down Expand Up @@ -129,6 +136,13 @@ export async function onRequest(context) { // Contents of context object
if (fileType) {
headers.set('Content-Type', fileType);
}
// 根据Referer设置CDN缓存策略,如果是从/或/dashboard等访问,则仅允许浏览器缓存;否则设置为public,缓存时间为1年
if (Referer && Referer.includes(url.origin)) {
headers.set('Cache-Control', 'private, max-age=86400');
} else {
headers.set('Cache-Control', 'public, max-age=31536000');
}

const newRes = new Response(response.body, {
status: response.status,
statusText: response.statusText,
Expand All @@ -147,8 +161,8 @@ export async function onRequest(context) { // Contents of context object
async function returnWithCheck(request, env, url, imgRecord) {
const response = new Response('good', { status: 200 });

// Referer header equal to the dashboard page
if (request.headers.get('Referer') == url.origin + "/dashboard") {
// Referer header equal to the dashboard page or upload page
if (request.headers.get('Referer') && request.headers.get('Referer').includes(url.origin)) {
//show the image
return response;
}
Expand All @@ -165,14 +179,14 @@ async function returnWithCheck(request, env, url, imgRecord) {
if (typeof request.headers.get('Referer') == "undefined" || request.headers.get('Referer') == null || request.headers.get('Referer') == "") {
return Response.redirect(url.origin + "/blockimg", 302)
} else {
return new Response('Error: Image Blocked', { status: 404 });
return Response.redirect(url.origin + "/static/BlockImg.png", 302);
}

} else if (record.metadata.Label == "adult") {
if (typeof request.headers.get('Referer') == "undefined" || request.headers.get('Referer') == null || request.headers.get('Referer') == "") {
return Response.redirect(url.origin + "/blockimg", 302)
} else {
return new Response('Error: Image Blocked', { status: 404 });
return Response.redirect(url.origin + "/static/BlockImg.png", 302);
}
}
//check if the env variables WhiteList_Mode are set
Expand Down
Binary file added static/BlockImg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Binary file added static/readme/20241211123633692.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/readme/202412111400766.png

0 comments on commit 65dfc5f

Please sign in to comment.