百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

利用Cloudflare Worker部署免费的短链接平台

wptr33 2025-07-01 23:41 6 浏览

介绍:

020短链,利用Cloudflare Worker实现的简单免费的短链接平台

部署方法:

第一步:创建命名空间

进入Workers & Pages栏目下的KV项目


创建一个命名空间,点击Create a namespace


记住创建的这个空间名称,一会要用


第二步:创建Worker

进入Workers & Pages栏目下的Overview项目

点击Create application

点击Create Worker

点击deploy

第三步:配置Worker

点击Configure Worker

进入Settings栏目下的Variables项目


绑定KV Namespace

其中Variable name填写LINKS, KV namespace填写你刚刚创建的命名空间名称


在Triggers中修改域名


例如这样:

点击页面上方的Quick edit

填入以下内容

// 项目名,决定html从哪个项目获取,
const github_repo = typeof(GITHUB_REPO)!="undefined" ? GITHUB_REPO
    : 'AoEiuV020/Url-Shorten-Worker'
// 项目版本,cdn会有缓存,所以有更新时需要指定版本,
const github_version = typeof(GITHUB_VERSION)!="undefined" ? GITHUB_VERSION
    : '@main'
// 密码,密码正确情况无视白名单和超时设置,且支持自定义短链接,
const password = typeof(PASSWORD)!="undefined" ? PASSWORD
    : 'AoEiuV020 yes'
// 短链超时,单位毫秒,支持整数乘法,0表示不设置超时,
const shorten_timeout = typeof(SHORTEN_TIMEOUT)!="undefined" ? SHORTEN_TIMEOUT.split("*").reduce((a,b)=>parseInt(a)*parseInt(b),1)
    : (1000 * 60 * 10)
// 默认短链key的长度,遇到重复时会自动延长,
const default_len = typeof(DEFAULT_LEN)!="undefined" ? parseInt(DEFAULT_LEN)
    : 6
// 为true开启演示,否则无密码且非白名单请求不受理,是则允许访客试用,超时后失效,
const demo_mode = typeof(DEMO_MODE)!="undefined" ? DEMO_MODE === 'true'
    : true
// 为true自动删除超时的演示短链接记录,否则仅是标记过期,以便在后台查询历史记录,
const remove_completely = typeof(REMOVE_COMPLETELY)!="undefined" ? REMOVE_COMPLETELY === 'true'
    : true
// 白名单中的域名无视超时,json数组格式,写顶级域名就可以,自动通过顶级域名和所有二级域名,
const white_list = JSON.parse(typeof(WHITE_LIST)!="undefined" ? WHITE_LIST
    : `[
"aoeiuv020.com",
"aoeiuv020.cn",
"aoeiuv020.cc",
"020.name"
    ]`)
// 演示模式开启时网页上展示这段禁止滥用提示,并不需要明确表示什么时候失效,
const demo_notice = typeof(DEMO_NOTICE)!="undefined" ? DEMO_NOTICE
    : `注意:为防止示例服务被人滥用,故所有由demo网站生成的链接随时可能失效,如需长期使用请自行搭建。`
//console.log(`${github_repo}, ${github_version}, ${password}, ${shorten_timeout}, ${demo_mode}, ${white_list}, ${demo_notice}`)
const html404 = `<!DOCTYPE html>
<body>
  <h1>404 Not Found.</h1>
  <p>The url you visit is not found.</p>
</body>`


async function randomString(len) {
    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 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 checkWhite(host){
    return white_list.some((h) => host == h || host.endsWith('.'+h))
} 
async function md5(message) {
  const msgUint8 = new TextEncoder().encode(message) // encode as (utf-8) Uint8Array
  const hashBuffer = await crypto.subtle.digest('MD5', msgUint8) // hash the message
  const hashArray = Array.from(new Uint8Array(hashBuffer)) // convert buffer to byte array
  const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('') // convert bytes to hex string

  return hashHex
}
async function checkHash(url, hash) {
    if (!hash) {
        return false
    }
    return (await md5(url+password)) == hash
}
async function save_url(url, key, admin, len) {
  len = len || default_len;
    // 密码正确且指定了key的情况直接覆盖旧值,
    const override = admin && key
    if (!override) {
        // 密码不正确情况无视指定key,
        key = await randomString(len)
    }
    const is_exists = await load_url(key)
    console.log("key exists " + key + " " + is_exists)
    if (override || !is_exists) {
        var mode = 3
        if (admin) {
            mode = 0
        }
        let value = `${mode};${Date.now()};${url}`
        if (remove_completely && mode != 0 && !await checkWhite(new URL(url).host)) {
          // 利用expirationTtl实现过期记录自动删除,低于60秒会报错,
          let ttl = Math.max(60, shorten_timeout / 1000)
          console.log("key auto remove: " + key + ", " + ttl + "s")
          return await LINKS.put(key, value, {expirationTtl: ttl}),key
        } else {
          return await LINKS.put(key, value),key
        }
    } else {
        return await save_url(url, key, admin, len + 1)
    }
}
async function load_url(key) {
    const value = await LINKS.get(key)
    if (!value) {
        return null
    }
    const list = value.split(';')
    console.log("value split " + list)
    var url
    if (list.length == 1) {
        // 老数据暂且正常跳转,
        url = list[0]
    } else {
        url = list[2]
        const mode = parseInt(list[0])
        const create_time = parseInt(list[1])
        if (mode != 0 && shorten_timeout > 0
            && Date.now() - create_time > shorten_timeout) {
            const host = new URL(url).host
            if (await checkWhite(host)) {
                console.log('white list')
            } else {
                // 超时和找不到做同样的处理,
                console.log("shorten timeout")
                return null
            }
        }
    }
    return url
}
async function handleRequest(request) {
  console.log(request)
  if (request.method === "POST") {
    let req=await request.json()
    console.log("url " + req["url"])
    let admin = await checkHash(req["url"], req["hash"])
    console.log("admin " + admin)
    if(!await checkURL(req["url"]) || (!admin && !demo_mode && !await checkWhite(new URL(req["url"]).host))){
    // 非演示模式下,非白名单地址当成地址不合法处理,
    return new Response(`{"status":500,"key":": Error: Url illegal."}`, {
      headers: {
      "content-type": "text/html;charset=UTF-8",
      "Access-Control-Allow-Origin":"*",
      "Access-Control-Allow-Methods": "POST",
      },
    })}
    let stat,random_key=await save_url(req["url"], req["key"], admin)
    console.log("stat " + stat)
    if (typeof(stat) == "undefined"){
      return new Response(`{"status":200,"key":"/`+random_key+`"}`, {
      headers: {
      "content-type": "text/html;charset=UTF-8",
      "Access-Control-Allow-Origin":"*",
      "Access-Control-Allow-Methods": "POST",
      },
    })
    }else{
      return new Response(`{"status":200,"key":": Error:Reach the KV write limitation."}`, {
      headers: {
      "content-type": "text/html;charset=UTF-8",
      "Access-Control-Allow-Origin":"*",
      "Access-Control-Allow-Methods": "POST",
      },
    })}
  }else if(request.method === "OPTIONS"){  
      return new Response(``, {
      headers: {
      "content-type": "text/html;charset=UTF-8",
      "Access-Control-Allow-Origin":"*",
      "Access-Control-Allow-Methods": "POST",
      },
    })

  }

  const requestURL = new URL(request.url)
  const path = requestURL.pathname.split("/")[1]
  console.log(path)
  if(!path){

    const html= await fetch(`https://cdn.jsdelivr.net/gh/${github_repo}${github_version}/index.html`)
    const text = (await html.text())
        .replaceAll("###GITHUB_REPO###", github_repo)
        .replaceAll("###GITHUB_VERSION###", github_version)
        .replaceAll("###DEMO_NOTICE###", demo_notice)
    
    return new Response(text, {
    headers: {
      "content-type": "text/html;charset=UTF-8",
    },
  })
  }
  const url = await load_url(path)
  if (!url) {
    // 找不到或者超时直接404,
    console.log('not found')
    return new Response(html404, {
      headers: {
        "content-type": "text/html;charset=UTF-8",
      },
      status: 404
    })
  }
  return Response.redirect(url, 302)
}



addEventListener("fetch", async event => {
  event.respondWith(handleRequest(event.request))
})

可以修改如下环境变量,Key均为对应大写:

  1. 调整超时设置

演示模式生成的短链接超时无法访问,
白名单或者密码正确情况超时设置无效,
修改脚本开头的变量shorten_timeout, 单位毫秒,0表示不设置超时,

  1. 调整白名单

白名单中的域名设置短链接无视超时,
修改脚本开头的变量white_list, 是个json数组,写顶级域名就可以,自动通过顶级域名和所有二级域名,

  1. 关闭演示模式

只有演示模式开启才允许访客无密码添加非白名单地址,超时短链接会失效,
修改脚本开头的变量demo_mode,为true开启演示,为false无密码且非白名单请求不受理,

  1. 自动删除演示记录

针对演示模式开启情况下的超时失效的短链接记录是否自动删除,
修改脚本开头的变量remove_completely,为true自动删除超时的演示短链接记录,否则仅是标记过期,以便在后台查询历史记录,

  1. 修改密码

网页有个隐藏输入框可以输入密码,
密码正确情况无视白名单和超时设置,且支持自定义短链接,
修改脚本开头的变量password,这个私密信息比较建议直接在环境变量里配置,

  1. 修改短链长度

短链长度就是随机生成的key也就是短链接的path部分的长度,
长度不够时容易出现重复,遇到重复时会自动延长,
修改脚本开头的变量default_len,

相关地址:

GitHub地址:
https://github.com/AoEiuV020/Url-Shorten-Worker

Demo:https://020.name

相关推荐

python生成脚本与部署的方案(python生成脚本与部署的方案区别)

上周接到一个需求任务,去帮助抢舱位小队优化流程和提升他们的效率。公司的订舱需求越来越大,需求的舱位产品越来越多,而且每次只给我们几十分钟的准备时间,导致每次匆匆忙忙,人手不足,抢不到舱位则影响公司业务...

什么是Python中的生成器推导式?(生成器推导式的结果是一个)

编程派微信号:codingpy本文作者为NedBatchelder,是一名资深Python工程师,目前就职于在线教育网站Edx。文中蓝色下划线部分可“阅读原文”后点击。Python中有一种紧凑的语法...

Python技巧1:使用Python生成验证码

使用Python生成验证码

别再用手敲了,这个工具可以自动生成python爬虫代码

我们在写爬虫代码时,常常需要各种分析调试,而且每次直接用代码调试都很麻烦所以今天给大家分享一个工具,不仅能方便模拟发送各种http请求,还能轻松调试,最重要的是,可以将调试最终结果自动转换成爬虫代码,...

在 Python 中构建生成式 AI 处理器

为什么不为ApacheNiFi2.0.0创建一个Python处理器?在本教程中,了解这样做的挑战是容易还是困难。当我开始做这件事时,那是一个下雪天。我看到了IBMWatsonXPyt...

一文掌握Python生成器和迭代器之间的区别

迭代器(Iterators)迭代器是遵循迭代器协议的对象,这意味着它们实现了__iter__()和__next__()方法。__iter__()返回迭代器对象本身,__next__()返回容器中的下一...

为你的python程序上锁:软件序列号生成器

序列号很多同学可能开发了非常多的程序了,并且进行了...

5分钟掌握Python(八)之生成器(生成器 python)

1)说明:在Python中,这种一边循环一边计算的机制,称为生成器:generator。在Python中,使用了yield的函数被称为生成器(generator)。跟普通函数不同的是,生成...

python中迭代器和生成器傻傻分不清,别急,这就告诉你区别

杂谈...

使用python生成添加管理员账户的exe

0x01前言在渗透测试中,针对Windows服务器获取webshell后一般会考虑新建管理员账号(当然某些情况下可以直接读密码)登录rdp方便渗透。目前来说,常见的使用netuser(包括激活gu...

人人都能看懂的「迭代器、生成器」入门指南

来源:早起Python作者:刘早起...

用检索增强生成让大模型更强大,这里有个手把手的Python实现

选自towardsdatascience...

Markdown + 文档管理 + 静态网页生成,集大成的 Markdown 应用:MWeb

上周给大家推荐了Typora,作为一款纯粹的Markdown应用来说,它的各种功能和细节可以说已经相当极致,然而,Ulysses用户表示:我们想要的不仅仅是Markdown。是的,Markdo...

python yield -- 生成器(python 生成器send)

概念:yield和return的区别:一个是返回值,一个是迭代器,多次返回python中,yield关键字用于从一个函数中返回一个值,并且能够在之后从同一个位置继续执行。这使得yield成为...

Python生成器(Python生成器对象)

一、Python生成器介绍1.什么是生成器在Python中,使用了...