mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-23 16:38:34 +08:00
支持动态UUID
This commit is contained in:
11
README.md
11
README.md
@@ -136,7 +136,10 @@ Telegram交流群:[@CMLiussss](https://t.me/CMLiussss),**感谢[Alice Networ
|
|||||||
| 变量名 | 示例 | 必填 | 备注 | YT |
|
| 变量名 | 示例 | 必填 | 备注 | YT |
|
||||||
|--------|---------|-|-----|-----|
|
|--------|---------|-|-----|-----|
|
||||||
| UUID | `90cd4a77-141a-43c9-991b-08263cfe9c10` |✅| Powershell -NoExit -Command "[guid]::NewGuid()"| [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=72s) |
|
| UUID | `90cd4a77-141a-43c9-991b-08263cfe9c10` |✅| Powershell -NoExit -Command "[guid]::NewGuid()"| [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=72s) |
|
||||||
| PROXYIP | `proxyip.fxxk.dedyn.io:443` |❌| 备选作为访问CFCDN站点的代理节点(支持多ProxyIP, ProxyIP之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=166s) |
|
| KEY | `token` |❌| 动态UUID秘钥,使用变量`KEY`的时候,将不再启用变量`UUID`| |
|
||||||
|
| TIME | `7` |❌| 动态UUID有效时间(单位:天)| |
|
||||||
|
| UPTIME | `3` |❌| 动态UUID有效时间(默认:北京时间`3`点更新) | |
|
||||||
|
| PROXYIP | `proxyip.fxxk.dedyn.io:443` |❌| 备选作为访问CFCDN站点的代理节点(支持自定义ProxyIP端口, 支持多ProxyIP, ProxyIP之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=166s) |
|
||||||
| SOCKS5 | `user:password@127.0.0.1:1080` |❌| 优先作为访问CFCDN站点的SOCKS5代理(支持多socks5, socks5之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=826s) |
|
| SOCKS5 | `user:password@127.0.0.1:1080` |❌| 优先作为访问CFCDN站点的SOCKS5代理(支持多socks5, socks5之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=826s) |
|
||||||
| GO2SOCKS5 | `blog.cmliussss.com`,`*.ip111.cn`,`*google.com` |❌| 设置`SOCKS5`变量之后,可设置强制使用socks5访问名单(`*`可作为通配符,`换行`作多元素间隔) ||
|
| GO2SOCKS5 | `blog.cmliussss.com`,`*.ip111.cn`,`*google.com` |❌| 设置`SOCKS5`变量之后,可设置强制使用socks5访问名单(`*`可作为通配符,`换行`作多元素间隔) ||
|
||||||
| ADD | `icook.tw:2053#官方优选域名` |❌| 本地优选TLS域名/优选IP(支持多元素之间`,`或`换行`作间隔) ||
|
| ADD | `icook.tw:2053#官方优选域名` |❌| 本地优选TLS域名/优选IP(支持多元素之间`,`或`换行`作间隔) ||
|
||||||
@@ -158,6 +161,12 @@ Telegram交流群:[@CMLiussss](https://t.me/CMLiussss),**感谢[Alice Networ
|
|||||||
| CFKEY | `c6a944b5c956b6c18c2352880952bced8b85e` |❌| CF账户Global API Key(与`CFEMAIL`都填上后, 订阅信息将显示请求使用量, 小白别用) | |
|
| CFKEY | `c6a944b5c956b6c18c2352880952bced8b85e` |❌| CF账户Global API Key(与`CFEMAIL`都填上后, 订阅信息将显示请求使用量, 小白别用) | |
|
||||||
| CFPORTS | `2053`,`2096`,`8443` |❌| CF账户标准端口列表 | |
|
| CFPORTS | `2053`,`2096`,`8443` |❌| CF账户标准端口列表 | |
|
||||||
|
|
||||||
|
**注意: 填入`KEY`后将不再启用`UUID`!请二选一使用!!!**
|
||||||
|
1. 填入`KEY`后,永久订阅地址为`https://[YOUR-URL]/[YOUR-KEY]`;
|
||||||
|
2. 填入`KEY`后,临时订阅地址为`https://[YOUR-URL]/[YOUR-UUID]`;
|
||||||
|
3. **动态UUID**的订阅使用时间为**1**个`TIME`有效时间周期;
|
||||||
|
4. **动态UUID**的节点使用时间为**2**个`TIME`有效时间周期(也就是动态UUID失效的了,节点也可继续使用一个周期,只是无法继续更新订阅);
|
||||||
|
|
||||||
**注意: 填入`SOCKS5`后将不再启用`PROXYIP`!请二选一使用!!!**
|
**注意: 填入`SOCKS5`后将不再启用`PROXYIP`!请二选一使用!!!**
|
||||||
|
|
||||||
**注意: 填入`SUB`后将不再启用`ADD*`类变量生成的订阅内容!请二选一使用!!!**
|
**注意: 填入`SUB`后将不再启用`ADD*`类变量生成的订阅内容!请二选一使用!!!**
|
||||||
|
|||||||
130
_worker.js
130
_worker.js
@@ -77,6 +77,9 @@ let proxyhosts = [];//本地代理域名池
|
|||||||
let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL
|
let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL
|
||||||
let RproxyIP = 'false';
|
let RproxyIP = 'false';
|
||||||
let httpsPorts = ["2053","2083","2087","2096","8443"];
|
let httpsPorts = ["2053","2083","2087","2096","8443"];
|
||||||
|
let effectiveTime = 7;//有效时间 单位:天
|
||||||
|
let updateTime = 3;//更新时间
|
||||||
|
let userIDLow;
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
* @param {import("@cloudflare/workers-types").Request} request
|
* @param {import("@cloudflare/workers-types").Request} request
|
||||||
@@ -97,7 +100,14 @@ export default {
|
|||||||
fakeUserID = fakeUserIDMD5.slice(0, 8) + "-" + fakeUserIDMD5.slice(8, 12) + "-" + fakeUserIDMD5.slice(12, 16) + "-" + fakeUserIDMD5.slice(16, 20) + "-" + fakeUserIDMD5.slice(20);
|
fakeUserID = fakeUserIDMD5.slice(0, 8) + "-" + fakeUserIDMD5.slice(8, 12) + "-" + fakeUserIDMD5.slice(12, 16) + "-" + fakeUserIDMD5.slice(16, 20) + "-" + fakeUserIDMD5.slice(20);
|
||||||
fakeHostName = fakeUserIDMD5.slice(6, 9) + "." + fakeUserIDMD5.slice(13, 19);
|
fakeHostName = fakeUserIDMD5.slice(6, 9) + "." + fakeUserIDMD5.slice(13, 19);
|
||||||
//console.log(`虚假UUID: ${fakeUserID}`); // 打印fakeID
|
//console.log(`虚假UUID: ${fakeUserID}`); // 打印fakeID
|
||||||
|
if (env.KEY) {
|
||||||
|
const userIDs = await generateDynamicUUID(env.KEY);
|
||||||
|
userID = userIDs[0];
|
||||||
|
userIDLow = userIDs[1];
|
||||||
|
//console.log(`启用动态UUID\n秘钥KEY: ${env.KEY}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}`);
|
||||||
|
effectiveTime = env.TIME || effectiveTime;
|
||||||
|
updateTime = env.UPTIME || updateTime;
|
||||||
|
}
|
||||||
proxyIP = env.PROXYIP || proxyIP;
|
proxyIP = env.PROXYIP || proxyIP;
|
||||||
proxyIPs = await ADD(proxyIP);
|
proxyIPs = await ADD(proxyIP);
|
||||||
proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
|
proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
|
||||||
@@ -146,9 +156,8 @@ export default {
|
|||||||
FileName = env.SUBNAME || FileName;
|
FileName = env.SUBNAME || FileName;
|
||||||
if (url.searchParams.has('notls')) noTLS = 'true';
|
if (url.searchParams.has('notls')) noTLS = 'true';
|
||||||
if (!upgradeHeader || upgradeHeader !== 'websocket') {
|
if (!upgradeHeader || upgradeHeader !== 'websocket') {
|
||||||
// const url = new URL(request.url);
|
const 路径 = url.pathname.toLowerCase();
|
||||||
switch (url.pathname.toLowerCase()) {
|
if (路径 == '/') {
|
||||||
case '/':
|
|
||||||
if (env.URL302) return Response.redirect(env.URL302, 302);
|
if (env.URL302) return Response.redirect(env.URL302, 302);
|
||||||
else if (env.URL) return await proxyURL(env.URL, url);
|
else if (env.URL) return await proxyURL(env.URL, url);
|
||||||
else return new Response(JSON.stringify(request.cf, null, 4), {
|
else return new Response(JSON.stringify(request.cf, null, 4), {
|
||||||
@@ -157,12 +166,12 @@ export default {
|
|||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
case `/${fakeUserID}`:
|
} else if (路径 == `/${fakeUserID}`) {
|
||||||
const fakeConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url);
|
const fakeConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url, env);
|
||||||
return new Response(`${fakeConfig}`, { status: 200 });
|
return new Response(`${fakeConfig}`, { status: 200 });
|
||||||
case `/${userID}`: {
|
} else if (路径 == `/${env.KEY}` || 路径 == `/${userID}`) {
|
||||||
await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}</tg-spoiler>\n域名: ${url.hostname}\n<tg-spoiler>入口: ${url.pathname + url.search}</tg-spoiler>`);
|
await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}</tg-spoiler>\n域名: ${url.hostname}\n<tg-spoiler>入口: ${url.pathname + url.search}</tg-spoiler>`);
|
||||||
const vlessConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, UA, RproxyIP, url);
|
const vlessConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, UA, RproxyIP, url, env);
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
//const timestamp = Math.floor(now / 1000);
|
//const timestamp = Math.floor(now / 1000);
|
||||||
const today = new Date(now);
|
const today = new Date(now);
|
||||||
@@ -208,8 +217,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
default:
|
|
||||||
if (env.URL302) return Response.redirect(env.URL302, 302);
|
if (env.URL302) return Response.redirect(env.URL302, 302);
|
||||||
else if (env.URL) return await proxyURL(env.URL, url);
|
else if (env.URL) return await proxyURL(env.URL, url);
|
||||||
else return new Response('不用怀疑!你UUID就是错的!!!', { status: 404 });
|
else return new Response('不用怀疑!你UUID就是错的!!!', { status: 404 });
|
||||||
@@ -243,6 +251,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
enableSocks = false;
|
enableSocks = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await vlessOverWSHandler(request);
|
return await vlessOverWSHandler(request);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -419,7 +428,7 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR
|
|||||||
} else {
|
} else {
|
||||||
// 否则,尝试使用预设的代理 IP(如果有)或原始地址重试连接
|
// 否则,尝试使用预设的代理 IP(如果有)或原始地址重试连接
|
||||||
if (!proxyIP || proxyIP == '') {
|
if (!proxyIP || proxyIP == '') {
|
||||||
proxyIP = atob('cHJveHlpcC5meHhrLmRlZHluLmlv');
|
proxyIP = atob('cHJveHlpcC50cDEuY21saXVzc3NzLmNvbQ==');
|
||||||
} else if (proxyIP.includes(']:')) {
|
} else if (proxyIP.includes(']:')) {
|
||||||
portRemote = proxyIP.split(']:')[1] || portRemote;
|
portRemote = proxyIP.split(']:')[1] || portRemote;
|
||||||
proxyIP = proxyIP.split(']:')[0] || proxyIP;
|
proxyIP = proxyIP.split(']:')[0] || proxyIP;
|
||||||
@@ -427,6 +436,7 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR
|
|||||||
portRemote = proxyIP.split(':')[1] || portRemote;
|
portRemote = proxyIP.split(':')[1] || portRemote;
|
||||||
proxyIP = proxyIP.split(':')[0] || proxyIP;
|
proxyIP = proxyIP.split(':')[0] || proxyIP;
|
||||||
}
|
}
|
||||||
|
if (proxyIP.includes('.tp')) portRemote = proxyIP.split('.tp')[1].split('.')[0] || portRemote;
|
||||||
tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote);
|
tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote);
|
||||||
}
|
}
|
||||||
// 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接)
|
// 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接)
|
||||||
@@ -560,9 +570,15 @@ function processVlessHeader(vlessBuffer, userID) {
|
|||||||
let isUDP = false;
|
let isUDP = false;
|
||||||
|
|
||||||
// 验证用户 ID(接下来的 16 个字节)
|
// 验证用户 ID(接下来的 16 个字节)
|
||||||
if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) {
|
function isUserIDValid(userID, userIDLow, buffer) {
|
||||||
isValidUser = true;
|
const userIDArray = new Uint8Array(buffer.slice(1, 17));
|
||||||
|
const userIDString = stringify(userIDArray);
|
||||||
|
return userIDString === userID || userIDString === userIDLow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用函数验证
|
||||||
|
isValidUser = isUserIDValid(userID, userIDLow, vlessBuffer);
|
||||||
|
|
||||||
// 如果用户 ID 无效,返回错误
|
// 如果用户 ID 无效,返回错误
|
||||||
if (!isValidUser) {
|
if (!isValidUser) {
|
||||||
return {
|
return {
|
||||||
@@ -1251,17 +1267,18 @@ function checkSUB(host) {
|
|||||||
if ((!sub || sub == '') && (addresses.length + addressesapi.length + addressesnotls.length + addressesnotlsapi.length + addressescsv.length) == 0){
|
if ((!sub || sub == '') && (addresses.length + addressesapi.length + addressesnotls.length + addressesnotlsapi.length + addressescsv.length) == 0){
|
||||||
addresses = [
|
addresses = [
|
||||||
'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点',
|
'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点',
|
||||||
|
'127.0.0.1:1234#CFnat',
|
||||||
'visa.cn:443',
|
'visa.cn:443',
|
||||||
'www.visa.com:8443',
|
'singapore.com:8443',
|
||||||
'cis.visa.com:2053',
|
'japan.com:2053',
|
||||||
'africa.visa.com:2083',
|
'brazil.com:2083',
|
||||||
'www.visa.com.sg:2087',
|
'russia.com:2087',
|
||||||
'www.visaeurope.at:2096',
|
'www.gov.ua:2096',
|
||||||
'www.visa.com.mt:8443',
|
'www.gco.gov.qa:8443',
|
||||||
'qa.visamiddleeast.com',
|
'www.gov.se',
|
||||||
'time.is',
|
'time.is',
|
||||||
'www.wto.org:8443',
|
'www.wto.org:8443',
|
||||||
'chatgpt.com:2087',
|
'fbi.gov:2087',
|
||||||
'icook.hk',
|
'icook.hk',
|
||||||
//'104.17.0.0#IPv4',
|
//'104.17.0.0#IPv4',
|
||||||
'[2606:4700::]#IPv6'
|
'[2606:4700::]#IPv6'
|
||||||
@@ -1269,9 +1286,9 @@ function checkSUB(host) {
|
|||||||
if (host.includes(".workers.dev")) addressesnotls = [
|
if (host.includes(".workers.dev")) addressesnotls = [
|
||||||
'usa.visa.com:2095',
|
'usa.visa.com:2095',
|
||||||
'myanmar.visa.com:8080',
|
'myanmar.visa.com:8080',
|
||||||
'www.visa.com.tw:8880',
|
'dynadot.com:8880',
|
||||||
'www.visaeurope.ch:2052',
|
'www.visaeurope.ch:2052',
|
||||||
'www.visa.com.br:2082',
|
'shopify.com:2082',
|
||||||
'www.visasoutheasteurope.com:2086'
|
'www.visasoutheasteurope.com:2086'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1329,7 +1346,8 @@ let subParams = ['sub','base64','b64','clash','singbox','sb'];
|
|||||||
* @param {string} UA
|
* @param {string} UA
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
|
async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url, env) {
|
||||||
|
const uuid = (_url.pathname == `/${env.KEY}`) ? env.KEY : userID;
|
||||||
checkSUB(hostName);
|
checkSUB(hostName);
|
||||||
const userAgent = UA.toLowerCase();
|
const userAgent = UA.toLowerCase();
|
||||||
const Config = 配置信息(userID , hostName);
|
const Config = 配置信息(userID , hostName);
|
||||||
@@ -1373,7 +1391,7 @@ async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
|
|||||||
else socks5List += `\n ${go2Socks5s.join('\n ')}\n`;
|
else socks5List += `\n ${go2Socks5s.join('\n ')}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let 订阅器 = '';
|
let 订阅器 = '\n';
|
||||||
if (!sub || sub == '') {
|
if (!sub || sub == '') {
|
||||||
if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`;
|
if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`;
|
||||||
else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`;
|
else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`;
|
||||||
@@ -1392,36 +1410,36 @@ async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
|
|||||||
订阅器 += `\nSUB(优选订阅生成器): ${sub}`;
|
订阅器 += `\nSUB(优选订阅生成器): ${sub}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (env.KEY && _url.pathname !== `/${env.KEY}`) 订阅器 = '';
|
||||||
|
else 订阅器 += `\nSUBAPI(订阅转换后端): ${subProtocol}://${subconverter}\nSUBCONFIG(订阅转换配置文件): ${subconfig}`;
|
||||||
|
const 动态UUID = (uuid != userID) ? `TOKEN: ${uuid}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}\nTIME(动态UUID有效时间): ${effectiveTime} 天\nUPTIME(动态UUID更新时间): ${updateTime} 时(北京时间)\n\n` : "";
|
||||||
return `
|
return `
|
||||||
################################################################
|
################################################################
|
||||||
Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式
|
Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
快速自适应订阅地址:
|
快速自适应订阅地址:
|
||||||
https://${proxyhost}${hostName}/${userID}
|
https://${proxyhost}${hostName}/${uuid}
|
||||||
https://${proxyhost}${hostName}/${userID}?sub
|
https://${proxyhost}${hostName}/${uuid}?sub
|
||||||
|
|
||||||
Base64订阅地址:
|
Base64订阅地址:
|
||||||
https://${proxyhost}${hostName}/${userID}?b64
|
https://${proxyhost}${hostName}/${uuid}?b64
|
||||||
https://${proxyhost}${hostName}/${userID}?base64
|
https://${proxyhost}${hostName}/${uuid}?base64
|
||||||
|
|
||||||
clash订阅地址:
|
clash订阅地址:
|
||||||
https://${proxyhost}${hostName}/${userID}?clash
|
https://${proxyhost}${hostName}/${uuid}?clash
|
||||||
|
|
||||||
singbox订阅地址:
|
singbox订阅地址:
|
||||||
https://${proxyhost}${hostName}/${userID}?sb
|
https://${proxyhost}${hostName}/${uuid}?sb
|
||||||
https://${proxyhost}${hostName}/${userID}?singbox
|
https://${proxyhost}${hostName}/${uuid}?singbox
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
################################################################
|
################################################################
|
||||||
${FileName} 配置信息
|
${FileName} 配置信息
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
HOST: ${hostName}
|
${动态UUID}HOST: ${hostName}
|
||||||
UUID: ${userID}
|
UUID: ${userID}
|
||||||
FKID: ${fakeUserID}
|
FKID: ${fakeUserID}
|
||||||
UA: ${UA}
|
UA: ${UA}
|
||||||
|
|
||||||
${订阅器}
|
${订阅器}
|
||||||
SUBAPI(订阅转换后端): ${subProtocol}://${subconverter}
|
|
||||||
SUBCONFIG(订阅转换配置文件): ${subconfig}
|
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
################################################################
|
################################################################
|
||||||
v2ray
|
v2ray
|
||||||
@@ -1904,3 +1922,43 @@ function isValidIPv4(address) {
|
|||||||
const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||||
return ipv4Regex.test(address);
|
return ipv4Regex.test(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateDynamicUUID(key) {
|
||||||
|
// 获取当前时间是当年的第几周(以北京时间凌晨3点为界)
|
||||||
|
function getWeekOfYear() {
|
||||||
|
const now = new Date();
|
||||||
|
const timezoneOffset = 8; // 北京时间相对于UTC的时区偏移+8小时
|
||||||
|
const adjustedNow = new Date(now.getTime() + timezoneOffset * 60 * 60 * 1000);
|
||||||
|
const start = new Date(2007, 6, 7, 3, 0, 0); // 固定起始日期为2007年7月7日的凌晨3点
|
||||||
|
|
||||||
|
// 计算当前时间与今年1月1日凌晨3点的差距
|
||||||
|
const diff = adjustedNow - start;
|
||||||
|
|
||||||
|
// 一周的毫秒数(7天)
|
||||||
|
const oneWeek = 1000 * 60 * 60 * 24 * effectiveTime;
|
||||||
|
|
||||||
|
// 返回当前是第几周,向上取整
|
||||||
|
return [Math.ceil(diff / oneWeek), Math.ceil((diff / oneWeek)- 1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwdTimes = getWeekOfYear(); // 获取当前周数并存储
|
||||||
|
const passwdTime = passwdTimes[0];
|
||||||
|
const passwdTimeLow = passwdTimes[1];
|
||||||
|
|
||||||
|
// 生成 UUID 的辅助函数
|
||||||
|
function generateUUID(baseString) {
|
||||||
|
const hashBuffer = new TextEncoder().encode(baseString);
|
||||||
|
return crypto.subtle.digest('SHA-256', hashBuffer).then((hash) => {
|
||||||
|
const hashArray = Array.from(new Uint8Array(hash));
|
||||||
|
const hexHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
||||||
|
let uuid = hexHash.substr(0, 8) + '-' + hexHash.substr(8, 4) + '-4' + hexHash.substr(13, 3) + '-' + (parseInt(hexHash.substr(16, 2), 16) & 0x3f | 0x80).toString(16) + hexHash.substr(18, 2) + '-' + hexHash.substr(20, 12);
|
||||||
|
return uuid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成两个 UUID
|
||||||
|
const currentUUIDPromise = generateUUID(key + passwdTime);
|
||||||
|
const previousUUIDPromise = generateUUID(key + passwdTimeLow);
|
||||||
|
|
||||||
|
return Promise.all([currentUUIDPromise, previousUUIDPromise]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user