Docker
服务搭建
Docker-笔记类
Docker-Wiki.js
Docker-Trilium(个人云笔记)
Docker-MrDoc(觅思文档)
Docker-MinDoc(文档管理系统)
Docker-Wiz(为知笔记)
Docker-Wizard(开源文档管理系统)
Docker-siyuan(思源笔记)
Docker-Athensresearch(雅典)
Docker-Logseq(日志序列)
Docker-Joplin Server(需搭配APP)
MarkDown-进阶
Docker-AFFiNE(白板文档)
Docker-Obsidian(黑曜石)
Html-文章保护
Docker-Memos(备忘录)
Docker-Etherpad(实时协作在线编辑器)
Docker-AppFlowy
Docker-DokuWiki(维基)
Docker-小书匠
Docker-MD(微信 Markdown编辑器)
Docker-Halo(个人博客)
闲置宽带利用
甜糖
网心云
点心云
Docker-JS TOOL(京豆)
Docker-Gitea(代码托管平台)
Git使用小技巧
Git-命令推送
Git-常用命令
Docker-CentOS
Docker-iperf3(网络测速)
Docker-QingLong(青龙面板)
🚫青龙-NolanJDCloud
青龙-Ninja
青龙-京豆
青龙-互助码
青龙-WxPusher(通知)
青龙-其他脚本
青龙-bilibili
青龙-短视频金币
青龙-常见问题
青龙-迁移
青龙-快速部署
Docker-EMQX(MQTT服务器)
EMQX-高级操作
Docker-Home Assistant
Docker-RustDesk(远程桌面)
Docker-邮箱系统
Docker-SRS(直播服务器)
Docker-Bitwarden(密码服务器)
Bitwarden-TOTP(双因素认证2FA)
Docker-书签/导航
Docker-OneNav
Docker-Flare
Docker-WebStack
Docker-GEEKAPE
Docker-nav(发现导航)
Docker-Linkding(网络书签)
Docker-Wallabag(网络书签)
Docker-Benotes(书签)
Docker-LyLme Spage(六零导航页)
Docker-Homarr(浏览器仪表板)
Docker-Sun-Panel
Docker-Nginx
Docker-nginxWebUI(反向代理)
Docker-Nginx Proxy Manager
Docker-Nginx Config(配置生成器)
Docker-Nginx UI
Nginx-配置详细说明
PHP扩展
Nginx-安全
Nginx-Shell管理脚本
Docker-Redis
Docker-Jupyter(Python在线环境)
JupyterHub(支持课程管理)
Jupyter(单用户)
Jupyter-OpenCV
JupyterHub-自构建
Docker-LiveTorrent(磁力链在线解析)
Docker-OpenVPN
OpenVPN-配置注释
OpenVPN-基础部署
OpenVPN-一键脚本部署
OpenVPN-访问内部设备
OpenVPN-ovpn-admin(WebUI)
Docker-Switch联机
Docker-网站统计
Docker-Umami
Docker-Matomo
Docker-IPsecVPN
Docker-Zerotier(虚拟组网)
ZeroTier-Planet(星球)
Zerotier-Moons(月亮)
Zerotier-伪根服务器(修改UI)
ZeroTier-局域组网
Docker-Linux
Docker-Screeps 服务器
Docker-Harbor(Docker 镜像服务器)
Harbor-镜像迁移
docker-mirror(镜像转存工具)
Docker-Reader(网文阅读器)
Docker-聊天类
Docker-Rocket.chat(IM聊天系统)
Docker-Fiora(二次元聊天室)
Docker-Mattermost(开发者聊天室)
Docker-唐僧叨叨
Docker-Mastodon
Docker-VoceChat
Docker-blogChat(在线聊天室)
Docker-NASTools(影音库工具)
Docker-Lottery(抽奖程序)
Docker-VSCode Web版
Docker-临时分享类
Docker-PasteMe(自销毁小纸条)
Docker-PasteBin(临时分享)
Docker-Pingvin Share(文件分享)
Docker-Snapdrop(局域网传输)
Docker-FileCodeBox(文件快递柜)
Docker-microbin(临时分享)
Docker-PairDrop(局域网传输)
Docker-FastSend(文件快传)
Docker-internal-chat(局域网文字/文件)
Docker-Enclosed(私密安全笔记)
Docker-任务管理类
Docker-TAIGA(看板)
Docker-Focalboard(看板)
Docker-DooTask(轻量级任务管理工具)
Docker-Vikunja(待办事项)
Docker-Tasks.md(任务管理板)
Docker-Send(私密文件分享)
Docker-ScreeGo(在线多人屏幕分享)
Docker-ChatGPT
ChatGPT(伪)-部署
ChatGPT-未测试项目
ChatGPT-ChatGPT Web
🚫ChatGPT-ChatGPT Vercel
🚫ChatGPT-PandoraNext(潘多拉)
🚫ChatGPT-PandoraNext-Helper(Token管理)
Docker-kkFileView(文件在线预览)
Docker-问卷考试类
Docker-学之思(开源考试系统)
Docker-文字识别类(OCR)
Docker-OCR(Python)
Docker-Paperless-ngx(无纸化)
Docker-短链类
Docker-Yourls(短链)
Worker短链(CloudFlare)
Docker-GreaterWMS(仓管系统)
Docker-内网穿透
Docker-Frp
Docker-NPS
Frp-客户端配置工具
Docker-Frp-Panel
Docker-OpenP2P
Docker-网盘
Docker-AList(云盘直链)
🚫Docker-FileRun(个人网盘)
Docker-腾飞WebOS
Docker-Cloudreve(支持WebDAV子账户)
Docker-Seafile
Docker-ZFile
Docker-Dufs(轻量文件服务器)
Docker-FileGator
Docker-SSH/远程桌面
Docker-Guacamole(远程桌面)
Docker-Nexterm(服务器管理软件)
Docker-Sshwifty(SSH&Telnet客户端)
Docker-Next Terminal(Web终端)
Docker-CF_Tunnels(隧道)
Docker-RouYi_WMS
Docker-图形化管理工具
Docker-Portainer(图形化管理工具)
Docker-SimpleDocker(Docker控制面板)
Docker-Easypanel(易面板)
Docker-DockerUI(国内图形化)
Docker-1Panel
Docker-宝塔
Docker-lazydocker(Shell UI)
Docker-⭐DPanel(可视化管理面板)
Docker-⭐Dockge(docker-compose管理器)
Docker-Dokploy(PaaS)
Docker-UpSnap(局域网唤醒)
Docker-Watchtower(自动更新)
Docker-RunnerGo(全栈测试)
Docker-Web工具箱
Docker-Stirling-PDF(PDF工具)
Docker-IT Tools(IT工具箱)
Docker-Reference(速查神器)
Docker-Photopea(在线PS)
Docker-miniPaint(在线PS)
Docker-Morphos(文件转换)
Docker-证件照片排版
Docker-网易云解锁
Docker-nondanee_UnblockNeteaseMusic
Docker-UnblockNeteaseMusic
Docker-LX Music_Sync(数据同步)
Docker-在线白板类
Docker-Wbo(协作白板)
Docker-Excalidraw+(在线白板)
Docker-tldraw
Docker-Cook(今天吃什么)
Docker-Android
Docker-QD(自动签到)
QD-使用说明
Docker-消息推送(通知)
Docker-Wecom酱
Docker-Gotify(消息通知)
Docker-WeChatBot(微信机器人)
Docker-lumen项目部署
Docker-简历构建器
Docker-Open Resume
Docker-Reactive Resume
Docker-照片备份
Docker-Immich
Docker-vlmcsd(KMS)
Docker-Papermerge(数字档案文件管理系统)
Docker-SearXNG(搜索引擎)
Docker-Snipe-IT(资产管理)
Docker-Moments(极简朋友圈)
Docker-neko(浏览器)
Docker-AsPoem(诗词网站)
Docker-CMS(内容管理系统)
Docker-Ikaros(个人内容管家)
Docker-JPress
Docker-OneKVM(类向日葵控控)
Docker-OS(系统)
Docker-DSM(群晖)
Docker-OSX(MacOS)
Docker-Puter(云端系统)
Docker-GodoOS(内网办公操作系统)
Docker-Nextcloud
Docker-KodCloud(可道云)
Docker-MacOS
Docker-Windows
Docker-Ferry(工单系统)
Docker-HivisionIDPhotos(AI证件照)
Docker-IPTVnator(IPTV播放器)
Docker-SQL工具
Docker-SQLynx
Docker-STUN/TURN(Coturn)
Docker-gocron(定时任务管理系统)
Docker-paopao-ce(微社区)
Docker-JSON Hero(JSON浏览器)
Docker-n8n(工作流自动化)
Docker-Aria2
Docker-NTP(时间服务器)
Docker-EasyTier(异地组网)
Linux-EasyTier
EasyTier-配置文件
Docker-Domain Admin(域管理员)
Docker-WPS
卷的使用
备份与还原
各种开源项目(未测试)
限制资源
开放端口调整
更换 Docker 源
镜像转存DockerHub
DockerHub 代理加速(Cloudflare Worker)
Docker 代理加速(Cloudflare Worker)
代理加速
⭐CF-Workers-docker(DockerHub 加速)
docker run 转 docker-compose
Docker 解释
Dockerfile(自建镜像)
使用第三方构建
多阶段构建(缩小体积)
Docker的备份服务器
Kasm(Web服务)
Docker-备份与还原
Docker-volume(卷)
Docker-network(网络)
Linux 安装 Docker
Windows 安装 Docker
Win7 安装 Docker
游戏服务器搭建
Docker-Valheim(英灵神殿)
Docker-jsnes(在线NES)
Docker-7DaysToDie(七日杀)
7DaysToDie-配置文件
Docker-Conan Exiles(流放者柯南)
Docker-noname(无名杀)
Docker-Epic游戏商城每周免费游戏
Docker-Terraria(泰拉瑞亚)
泰拉瑞亚-服务器
Terrari-存档同步
Docker-Mindustry(像素工厂)
Docker-Habitica(习惯养成RPG)
Docker-RetroArch(在线模拟器)
Docker-vue-idle-game(轮回勇士传说)
Docker-Phira(Phigros 二创版)
Docker-Pterodactyl(翼龙面板⭐)
Pterodactyl-自建预设(游戏部署脚本)
Docker-GamePanelX-V3(游戏面板)
Docker-MCSManager(游戏面板⭐)
Docker-Easy-WI(游戏面板)
Docker-常见问题处理
Podman(Docker替代品)
Docker-Linux Command(Linux命令搜索引擎)
Docker-修改存储位置
Dockerc(编译为独立二进制文件)
slim(镜像瘦身)
Docker-API(远程连接)
Docker-cloudbak(云朵备份)
本文档使用 MrDoc 发布
-
+
首页
Worker短链(CloudFlare)
Url-Shorten-Worker 使用 [CloudFlare](https://dash.cloudflare.com/) 的 Worker 实现无服务器部署短链服务器 **相关链接:** - xyTom 原版(main):https://github.com/xyTom/Url-Shorten-Worker - crazypeace 版:https://github.com/crazypeace/Url-Shorten-Worker 、[Url-Shorten-Worker 教程](https://zelikk.blogspot.com/search/label/Url-Shorten-Worker) - DDD网络科技 版:[GitHub](https://github.com/wandduse/workers_kv_Shortenurl)、[博客教程](https://www.wanuse.com/2024/01/cloudflare-workerskv.html)、[BiLi教程](https://www.bilibili.com/video/BV1Ye411n77u) ## [crazypeace 版](https://github.com/crazypeace/Url-Shorten-Worker) 需要验证密码才能访问创建短链主页,验证通过的才可创建,直接访问调整到指定页面(隐藏该短链服务器),创建的短链可以通过短链主页删除,并且支持作为短链/图床/网络记事本/网络日记本 1. `Workers 和 Pages——KV` 创建一个命名空间(名称随意) 2. 刚创建的 KV 命名空间添加密钥 `password`,值就是 path密码 3. `Workers 和 Pages` 创建一个 Worker 项目 4. `Worker 项目——设置——变量——KV 命名空间绑定` 变量名称 `LINKS`, KV 命名空间 `选择刚刚创建的命名空间` 5. 复制 [项目代码](https://github.com/crazypeace/Url-Shorten-Worker/raw/main/worker.js) 覆盖原Worker代码 6. 保存并部署 7. 绑定自己域名 `Worker 项目——触发器——自定义域——添加自定义域`,添加你需要设置的域名 进入后台(增删短链):`https://<Worker项目绑定的域名>/<path密码(KV中设置的password)>` 如我自用的:https://mini.918178.xyz/ai773203918 ### Workers_crazypeace 如果需要自定义页面,可以把[项目](https://github.com/crazypeace/Url-Shorten-Worker)拉取到自己仓库进行修改,然后替换Worker中`https://<你的GitHub>.github.io` ```js const config = { no_ref: "off", //控制 HTTP referrer 标头,如果要创建一个隐藏 HTTP Referer 标头的匿名链接,请设置为 "on" theme: "",//主页主题,默认主题使用空值。要使用 urlcool 主题,请填写 "theme/urlcool" cors: "on",//允许 API 请求的跨域资源共享 unique_link: false,//如果为 true,则相同的长 url 将被缩短为相同的短 url custom_link: true,//允许用户自定义短 URL overwrite_kv: false, // 允许用户覆盖现有密钥 snapchat_mode: false,//访问后,链接将被破坏 visit_count: false,//计算访问次数 load_kv: false,//从 Cloudflare KV 加载全部内容(禁用后只能管理本地添加的内容,全部内容通过KV命名空间进行管理) system_type: "shorturl", // 短链:shorturl,图床:imghost,网络记事本:pastebin,网络日记本:journal } // 键入protect_keylist无法从 UI 和 API 读取、添加、删除 const protect_keylist = [ "password", ] let index_html = "https://raw.githubusercontent.com/ai773203918/Url-Shorten-Worker/main/index.html" //使用我汉化的(仅汉化) //let index_html = "https://crazypeace.github.io/Url-Shorten-Worker/" + config.theme + "/index.html" // 调用的页面文件(crazypeace的,也可替换为自己的) let no_ref_html = "https://crazypeace.github.io/Url-Shorten-Worker/no-ref.html" // 调用的页面文件(默认为crazypeace的,也可替换为自己的) // 404提示 const html404 = `<!DOCTYPE html> <html> <body> <h1>404 未找到.</h1> <p>未找到您访问的网址.</p> </body> </html>` let response_header = { "Content-type": "text/html;charset=UTF-8;application/json", } if (config.cors == "on") { response_header = { "Content-type": "text/html;charset=UTF-8;application/json", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST", "Access-Control-Allow-Headers": "Content-Type", } } function base64ToBlob(base64String) { var parts = base64String.split(';base64,'); var contentType = parts[0].split(':')[1]; var raw = atob(parts[1]); var rawLength = raw.length; var uInt8Array = new Uint8Array(rawLength); for (var i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], { type: contentType }); } async function randomString(len) { len = len || 6; let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /*去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 *** Easily confused characters removed */ let maxPos = chars.length; let result = ''; for (i = 0; i < len; i++) { result += chars.charAt(Math.floor(Math.random() * maxPos)); } return result; } async function sha512(url) { url = new TextEncoder().encode(url) const url_digest = await crypto.subtle.digest( { name: "SHA-512", }, url, // The data you want to hash as an ArrayBuffer ) const hashArray = Array.from(new Uint8Array(url_digest)); // 将缓冲区转换为字节数组 const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); //console.log(hashHex) return hashHex } async function checkURL(URL) { let str = URL; let Expression = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/; let objExp = new RegExp(Expression); if (objExp.test(str) == true) { if (str[0] == 'h') return true; else return false; } else { return false; } } async function save_url(URL) { let random_key = await randomString() let is_exist = await LINKS.get(random_key) // console.log(is_exist) if (is_exist == null) { return await LINKS.put(random_key, URL), random_key } else { save_url(URL) } } async function is_url_exist(url_sha512) { let is_exist = await LINKS.get(url_sha512) // console.log(is_exist) if (is_exist == null) { return false } else { return is_exist } } async function handleRequest(request) { // console.log(request) // 查KV中的password对应的值 Query "password" in KV const password_value = await LINKS.get("password"); /************************/ // 以下是API接口的处理 if (request.method === "POST") { let req = await request.json() // console.log(req) let req_cmd = req["cmd"] let req_url = req["url"] let req_key = req["key"] let req_password = req["password"] /* console.log(req_cmd) console.log(req_url) console.log(req_key) console.log(req_password) */ if (req_password != password_value) { return new Response(`{"status":500,"key": "", "error":"Error: Invalid password."}`, { headers: response_header, }) } if (req_cmd == "add") { if ((config.system_type == "shorturl") && !await checkURL(req_url)) { return new Response(`{"status":500, "url": "` + req_url + `", "error":"Error: Url illegal."}`, { headers: response_header, }) } let stat, random_key if (config.custom_link && (req_key != "")) { // Refuse 'password" as Custom shortURL if (protect_keylist.includes(req_key)) { return new Response(`{"status":500,"key": "` + req_key + `", "error":"Error: Key in protect_keylist."}`, { headers: response_header, }) } let is_exist = await is_url_exist(req_key) if ((!config.overwrite_kv) && (is_exist)) { return new Response(`{"status":500,"key": "` + req_key + `", "error":"Error: Specific key existed."}`, { headers: response_header, }) } else { random_key = req_key stat, await LINKS.put(req_key, req_url) } } else if (config.unique_link) { let url_sha512 = await sha512(req_url) let url_key = await is_url_exist(url_sha512) if (url_key) { random_key = url_key } else { stat, random_key = await save_url(req_url) if (typeof (stat) == "undefined") { await LINKS.put(url_sha512, random_key) // console.log() } } } else { stat, random_key = await save_url(req_url) } // console.log(stat) if (typeof (stat) == "undefined") { return new Response(`{"status":200, "key":"` + random_key + `", "error": ""}`, { headers: response_header, }) } else { return new Response(`{"status":500, "key": "", "error":"Error: Reach the KV write limitation."}`, { headers: response_header, }) } } else if (req_cmd == "del") { // Refuse to delete 'password' entry if (protect_keylist.includes(req_key)) { return new Response(`{"status":500, "key": "` + req_key + `", "error":"Error: Key in protect_keylist."}`, { headers: response_header, }) } await LINKS.delete(req_key) // 计数功能打开的话, 要把计数的那条KV也删掉 Remove the visit times record if (config.visit_count) { await LINKS.delete(req_key + "-count") } return new Response(`{"status":200, "key": "` + req_key + `", "error": ""}`, { headers: response_header, }) } else if (req_cmd == "qry") { // 拒绝查询 '密码' if (protect_keylist.includes(req_key)) { return new Response(`{"status":500,"key": "` + req_key + `", "error":"Error: Key in protect_keylist."}`, { headers: response_header, }) } let value = await LINKS.get(req_key) if (value != null) { let jsonObjectRetrun = JSON.parse(`{"status":200, "error":"", "key":"", "url":""}`); jsonObjectRetrun.key = req_key; jsonObjectRetrun.url = value; return new Response(JSON.stringify(jsonObjectRetrun), { headers: response_header, }) } else { return new Response(`{"status":500, "key": "` + req_key + `", "error":"Error: Key not exist."}`, { headers: response_header, }) } } else if (req_cmd == "qryall") { if ( !config.load_kv) { return new Response(`{"status":500, "error":"Error: Config.load_kv false."}`, { headers: response_header, }) } let keyList = await LINKS.list() if (keyList != null) { // 初始化返回数据结构 Init the return struct let jsonObjectRetrun = JSON.parse(`{"status":200, "error":"", "kvlist": []}`); for (var i = 0; i < keyList.keys.length; i++) { let item = keyList.keys[i]; // Hide 'password' from the query all result if (protect_keylist.includes(item.name)) { continue; } // Hide '-count' from the query all result if (item.name.endsWith("-count")) { continue; } let url = await LINKS.get(item.name); let newElement = { "key": item.name, "value": url }; // 填充要返回的列表 Fill the return list jsonObjectRetrun.kvlist.push(newElement); } return new Response(JSON.stringify(jsonObjectRetrun) , { headers: response_header, }) } else { return new Response(`{"status":500, "error":"Error: Load keyList failed."}`, { headers: response_header, }) } } } else if (request.method === "OPTIONS") { return new Response(``, { headers: response_header, }) } /************************/ // 以下是浏览器直接访问worker页面的处理 const requestURL = new URL(request.url) const path = requestURL.pathname.split("/")[1] const params = requestURL.search; // console.log(path) // 如果path为空, 即直接访问本worker // If visit this worker directly (no path) if (!path) { return Response.redirect("https://nas.918178.xyz:10086", 302) // 如果没有path子路径则跳转到指定页面 // 也可直接提示404 /* new Response(html404, { headers: { "content-type": "text/html;charset=UTF-8", }, status: 404 }) */ } // 如果path符合password 显示操作页面index.html if (path == password_value) { let index = await fetch(index_html) index = await index.text() index = index.replace(/__PASSWORD__/gm, password_value) // 操作页面文字修改 // index = index.replace(/短链系统变身/gm, "") return new Response(index, { headers: { "content-type": "text/html;charset=UTF-8", }, }) } // 在KV中查询 短链接 对应的原链接 // Query the value(long url) in KV by key(short url) let value = await LINKS.get(path); // console.log(value) // 如果path是'password', 让查询结果为空, 不然直接就把password查出来了 if (protect_keylist.includes(path)) { value = "" } if (value) { // 计数功能 if (config.visit_count) { // 获取并增加访问计数 let count = await LINKS.get(path + "-count"); if (count === null) { await LINKS.put(path + "-count", "1"); // 初始化为1,因为这是首次访问 } else { count = parseInt(count) + 1; await LINKS.put(path + "-count", count.toString()); } } // 如果阅后即焚模式 if (config.snapchat_mode) { // 删除KV中的记录 // Remove record before jump to long url await LINKS.delete(path) } // 作为一个短链系统, value就是long URL, 需要跳转 if (config.system_type == "shorturl") { // 带上参数部分, 拼装要跳转的最终网址 // URL to jump finally let location; if (params) { location = value + params } else { location = value } if (config.no_ref == "on") { let no_ref = await fetch(no_ref_html) no_ref = await no_ref.text() no_ref = no_ref.replace(/{__FINAL_LINK__}/gm, location) return new Response(no_ref, { headers: { "content-type": "text/html;charset=UTF-8", }, }) } else { return Response.redirect(location, 302) } } else if (config.system_type == "imghost") { // 如果是图床 var blob = base64ToBlob(value) return new Response(blob, { headers: {'Content-Type': 'text/plain;charset=UTF-8'}, }) } else { // 如果只是一个单纯的key-value系统, 简单的显示value就行了 return new Response(value, { headers: {'Content-Type': 'text/plain;charset=UTF-8'}, }) } } else { // 其它 config.system_type 类型 // If request not in KV, return 404 return new Response(html404, { headers: { "content-type": "text/html;charset=UTF-8", }, status: 404 }) } } addEventListener("fetch", async event => { event.respondWith(handleRequest(event.request)) }) ``` ## [xyTom 原版](https://github.com/xyTom/Url-Shorten-Worker) 最基础的功能,访问主页为创建短链主页,任何人都可创建,创建的短链会存储在KV下(删除也是要去到KV里删除) 1. `Workers 和 Pages——KV` 创建一个命名空间(名称随意) 2. `Workers 和 Pages` 创建一个 Worker 项目 3. `Worker 项目——设置——变量——KV 命名空间绑定` 变量名称 `LINKS`, KV 命名空间 `选择刚刚创建的命名空间` 4. 复制 [项目代码](https://raw.githubusercontent.com/xyTom/Url-Shorten-Worker/main/index.js) 覆盖原Worker代码 5. 保存并部署 6. 绑定自己域名 `Worker 项目——触发器——自定义域——添加自定义域`,添加你需要设置的域名 进入主页:`https://<Worker项目绑定的域名>` ### Worker_xyTom ```js const config = { no_ref: "off", //控制 HTTP 引用头,如果要创建匿名链接以隐藏 HTTP 引用头,请设置为 "on" theme:"",//主页主题,默认主题请使用空值。要使用 urlcool 主题,请填写 "theme/urlcool" cors: "on",//允许为应用程序接口请求提供跨源资源共享. unique_link:false,//如果为 "true",相同的长网址将被缩短为相同的短网址 custom_link:true,//允许用户自定义短网址 safe_browsing_api_key: "" //输入 Google Safe Browsing API Key,在重定向前启用网址安全检查 } const html404 = `<!DOCTYPE html> <body> <h1>404 未找到.</h1> <p>未找到您访问的网址.</p> </body>` let response_header={ "content-type": "text/html;charset=UTF-8", } if (config.cors=="on"){ response_header={ "content-type": "text/html;charset=UTF-8", "Access-Control-Allow-Origin":"*", "Access-Control-Allow-Methods": "POST", } } async function randomString(len) { len = len || 6; let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/ let maxPos = $chars.length; let result = ''; for (i = 0; i < len; i++) { result += $chars.charAt(Math.floor(Math.random() * maxPos)); } return result; } async function sha512(url){ url = new TextEncoder().encode(url) const url_digest = await crypto.subtle.digest( { name: "SHA-512", }, url, // The data you want to hash as an ArrayBuffer ) const hashArray = Array.from(new Uint8Array(url_digest)); // convert buffer to byte array const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); //console.log(hashHex) return hashHex } async function checkURL(URL){ let str=URL; let Expression=/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/; let objExp=new RegExp(Expression); if(objExp.test(str)==true){ if (str[0] == 'h') return true; else return false; }else{ return false; } } async function save_url(URL){ let random_key=await randomString() let is_exist=await LINKS.get(random_key) console.log(is_exist) if (is_exist == null) return await LINKS.put(random_key, URL),random_key else save_url(URL) } async function is_url_exist(url_sha512){ let is_exist = await LINKS.get(url_sha512) console.log(is_exist) if (is_exist == null) { return false }else{ return is_exist } } async function is_url_safe(url){ let raw = JSON.stringify({"client":{"clientId":"Url-Shorten-Worker","clientVersion":"1.0.7"},"threatInfo":{"threatTypes":["MALWARE","SOCIAL_ENGINEERING","POTENTIALLY_HARMFUL_APPLICATION","UNWANTED_SOFTWARE"],"platformTypes":["ANY_PLATFORM"],"threatEntryTypes":["URL"],"threatEntries":[{"url":url}]}}); let requestOptions = { method: 'POST', body: raw, redirect: 'follow' }; result = await fetch("https://safebrowsing.googleapis.com/v4/threatMatches:find?key="+config.safe_browsing_api_key, requestOptions) result = await result.json() console.log(result) if (Object.keys(result).length === 0){ return true }else{ return false } } async function handleRequest(request) { console.log(request) if (request.method === "POST") { let req=await request.json() console.log(req["url"]) if(!await checkURL(req["url"])){ return new Response(`{"status":500,"key":": Error: Url illegal."}`, { headers: response_header, })} let stat,random_key if (config.unique_link){ let url_sha512 = await sha512(req["url"]) let url_key = await is_url_exist(url_sha512) if(url_key){ random_key = url_key }else{ stat,random_key=await save_url(req["url"]) if (typeof(stat) == "undefined"){ console.log(await LINKS.put(url_sha512,random_key)) } } }else{ stat,random_key=await save_url(req["url"]) } console.log(stat) if (typeof(stat) == "undefined"){ return new Response(`{"status":200,"key":"/`+random_key+`"}`, { headers: response_header, }) }else{ return new Response(`{"status":200,"key":": Error:Reach the KV write limitation."}`, { headers: response_header, })} }else if(request.method === "OPTIONS"){ return new Response(``, { headers: response_header, }) } const requestURL = new URL(request.url) const path = requestURL.pathname.split("/")[1] const params = requestURL.search; console.log(path) if(!path){ const html= await fetch("https://xytom.github.io/Url-Shorten-Worker/"+config.theme+"/index.html") return new Response(await html.text(), { headers: { "content-type": "text/html;charset=UTF-8", }, }) } const value = await LINKS.get(path); let location ; if(params) { location = value + params } else { location = value } console.log(value) if (location) { if (config.safe_browsing_api_key){ if(!(await is_url_safe(location))){ let warning_page = await fetch("https://xytom.github.io/Url-Shorten-Worker/safe-browsing.html") warning_page =await warning_page.text() warning_page = warning_page.replace(/{Replace}/gm, location) return new Response(warning_page, { headers: { "content-type": "text/html;charset=UTF-8", }, }) } } if (config.no_ref=="on"){ let no_ref= await fetch("https://xytom.github.io/Url-Shorten-Worker/no-ref.html") no_ref=await no_ref.text() no_ref=no_ref.replace(/{Replace}/gm, location) return new Response(no_ref, { headers: { "content-type": "text/html;charset=UTF-8", }, }) }else{ return Response.redirect(location, 302) } } // If request not in kv, return 404 return new Response(html404, { headers: { "content-type": "text/html;charset=UTF-8", }, status: 404 }) } addEventListener("fetch", async event => { event.respondWith(handleRequest(event.request)) }) ```
造物者W
2024年2月27日 15:11
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码