diff --git a/.edgeone/functions/index.js b/.edgeone/functions/index.js new file mode 100644 index 0000000..dba1419 --- /dev/null +++ b/.edgeone/functions/index.js @@ -0,0 +1,503 @@ + + let global = globalThis; + + class MessageChannel { + constructor() { + this.port1 = new MessagePort(); + this.port2 = new MessagePort(); + } + } + class MessagePort { + constructor() { + this.onmessage = null; + } + postMessage(data) { + if (this.onmessage) { + setTimeout(() => this.onmessage({ data }), 0); + } + } + } + global.MessageChannel = MessageChannel; + + async function handleRequest(context){ + let routeParams = {}; + let pagesFunctionResponse = null; + const request = context.request; + const urlInfo = new URL(request.url); + + if (urlInfo.pathname !== '/' && urlInfo.pathname.endsWith('/')) { + urlInfo.pathname = urlInfo.pathname.slice(0, -1); + } + + let matchedFunc = false; + + if('/api/clean-eo-cache' === urlInfo.pathname) { + matchedFunc = true; + (() => { + // functions/api/clean-eo-cache.js + async function qcloudV3Post(secretId, secretKey, service, bodyArray, headersArray) { + const HTTPRequestMethod = "POST"; + const CanonicalURI = "/"; + const CanonicalQueryString = ""; + const sortHeadersArray = Object.keys(headersArray).sort().reduce((obj, key) => { + obj[key] = headersArray[key]; + return obj; + }, {}); + let SignedHeaders = ""; + let CanonicalHeaders = ""; + for (const key in sortHeadersArray) { + SignedHeaders += key.toLowerCase() + ";"; + } + SignedHeaders = SignedHeaders.slice(0, -1); + for (const key in sortHeadersArray) { + CanonicalHeaders += `${key.toLowerCase()}:${sortHeadersArray[key].toLowerCase()} +`; + } + const HashedRequestPayload = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(JSON.stringify(bodyArray)) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const CanonicalRequest = `${HTTPRequestMethod} +${CanonicalURI} +${CanonicalQueryString} +${CanonicalHeaders} +${SignedHeaders} +${HashedRequestPayload}`; + const RequestTimestamp = Math.floor(Date.now() / 1e3); + const formattedDate = new Date(RequestTimestamp * 1e3).toISOString().split("T")[0]; + const Algorithm = "TC3-HMAC-SHA256"; + const CredentialScope = `${formattedDate}/${service}/tc3_request`; + const HashedCanonicalRequest = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(CanonicalRequest) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const StringToSign = `${Algorithm} +${RequestTimestamp} +${CredentialScope} +${HashedCanonicalRequest}`; + async function hmac(key, string) { + const cryptoKey = await crypto.subtle.importKey( + "raw", + typeof key === "string" ? new TextEncoder().encode(key) : key, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ); + const signature = await crypto.subtle.sign( + "HMAC", + cryptoKey, + new TextEncoder().encode(string) + ); + return new Uint8Array(signature); + } + const SecretDate = await hmac("TC3" + secretKey, formattedDate); + const SecretService = await hmac(SecretDate, service); + const SecretSigning = await hmac(SecretService, "tc3_request"); + const Signature = Array.from( + new Uint8Array( + await crypto.subtle.sign( + "HMAC", + await crypto.subtle.importKey( + "raw", + SecretSigning, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ), + new TextEncoder().encode(StringToSign) + ) + ) + ).map((b) => b.toString(16).padStart(2, "0")).join(""); + const Authorization = `${Algorithm} Credential=${secretId}/${CredentialScope}, SignedHeaders=${SignedHeaders}, Signature=${Signature}`; + headersArray["X-TC-Timestamp"] = RequestTimestamp.toString(); + headersArray["Authorization"] = Authorization; + return headersArray; + } + function onRequestOptions(context) { + return new Response(null, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type" + } + }); + } + async function onRequestPost(context) { + try { + const data = await context.request.json(); + const { secretId, secretKey, zoneId, type, targets } = data; + const service = "teo"; + const host = "teo.tencentcloudapi.com"; + const payload = { + ZoneId: zoneId, + Type: type, + Targets: targets + }; + const headersPending = { + "Host": host, + "Content-Type": "application/json", + "X-TC-Action": "CreatePurgeTask", + "X-TC-Version": "2022-09-01", + "X-TC-Region": "ap-guangzhou" + }; + const headers = await qcloudV3Post(secretId, secretKey, service, payload, headersPending); + const response = await fetch(`https://${host}`, { + method: "POST", + headers, + body: JSON.stringify(payload) + }); + const result = await response.json(); + return new Response(JSON.stringify(result), { + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + } + function onRequest(context) { + return new Response(JSON.stringify({ error: "Only POST method is allowed" }), { + status: 405, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + + pagesFunctionResponse = onRequest; + })(); + } + + if('/api/clean-eo-cache' === urlInfo.pathname && request.method === 'POST') { + matchedFunc = true; + (() => { + // functions/api/clean-eo-cache.js + async function qcloudV3Post(secretId, secretKey, service, bodyArray, headersArray) { + const HTTPRequestMethod = "POST"; + const CanonicalURI = "/"; + const CanonicalQueryString = ""; + const sortHeadersArray = Object.keys(headersArray).sort().reduce((obj, key) => { + obj[key] = headersArray[key]; + return obj; + }, {}); + let SignedHeaders = ""; + let CanonicalHeaders = ""; + for (const key in sortHeadersArray) { + SignedHeaders += key.toLowerCase() + ";"; + } + SignedHeaders = SignedHeaders.slice(0, -1); + for (const key in sortHeadersArray) { + CanonicalHeaders += `${key.toLowerCase()}:${sortHeadersArray[key].toLowerCase()} +`; + } + const HashedRequestPayload = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(JSON.stringify(bodyArray)) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const CanonicalRequest = `${HTTPRequestMethod} +${CanonicalURI} +${CanonicalQueryString} +${CanonicalHeaders} +${SignedHeaders} +${HashedRequestPayload}`; + const RequestTimestamp = Math.floor(Date.now() / 1e3); + const formattedDate = new Date(RequestTimestamp * 1e3).toISOString().split("T")[0]; + const Algorithm = "TC3-HMAC-SHA256"; + const CredentialScope = `${formattedDate}/${service}/tc3_request`; + const HashedCanonicalRequest = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(CanonicalRequest) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const StringToSign = `${Algorithm} +${RequestTimestamp} +${CredentialScope} +${HashedCanonicalRequest}`; + async function hmac(key, string) { + const cryptoKey = await crypto.subtle.importKey( + "raw", + typeof key === "string" ? new TextEncoder().encode(key) : key, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ); + const signature = await crypto.subtle.sign( + "HMAC", + cryptoKey, + new TextEncoder().encode(string) + ); + return new Uint8Array(signature); + } + const SecretDate = await hmac("TC3" + secretKey, formattedDate); + const SecretService = await hmac(SecretDate, service); + const SecretSigning = await hmac(SecretService, "tc3_request"); + const Signature = Array.from( + new Uint8Array( + await crypto.subtle.sign( + "HMAC", + await crypto.subtle.importKey( + "raw", + SecretSigning, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ), + new TextEncoder().encode(StringToSign) + ) + ) + ).map((b) => b.toString(16).padStart(2, "0")).join(""); + const Authorization = `${Algorithm} Credential=${secretId}/${CredentialScope}, SignedHeaders=${SignedHeaders}, Signature=${Signature}`; + headersArray["X-TC-Timestamp"] = RequestTimestamp.toString(); + headersArray["Authorization"] = Authorization; + return headersArray; + } + function onRequestOptions(context) { + return new Response(null, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type" + } + }); + } + async function onRequestPost(context) { + try { + const data = await context.request.json(); + const { secretId, secretKey, zoneId, type, targets } = data; + const service = "teo"; + const host = "teo.tencentcloudapi.com"; + const payload = { + ZoneId: zoneId, + Type: type, + Targets: targets + }; + const headersPending = { + "Host": host, + "Content-Type": "application/json", + "X-TC-Action": "CreatePurgeTask", + "X-TC-Version": "2022-09-01", + "X-TC-Region": "ap-guangzhou" + }; + const headers = await qcloudV3Post(secretId, secretKey, service, payload, headersPending); + const response = await fetch(`https://${host}`, { + method: "POST", + headers, + body: JSON.stringify(payload) + }); + const result = await response.json(); + return new Response(JSON.stringify(result), { + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + } + function onRequest(context) { + return new Response(JSON.stringify({ error: "Only POST method is allowed" }), { + status: 405, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + + pagesFunctionResponse = onRequestPost; + })(); + } + + if('/api/clean-eo-cache' === urlInfo.pathname && request.method === 'OPTIONS') { + matchedFunc = true; + (() => { + // functions/api/clean-eo-cache.js + async function qcloudV3Post(secretId, secretKey, service, bodyArray, headersArray) { + const HTTPRequestMethod = "POST"; + const CanonicalURI = "/"; + const CanonicalQueryString = ""; + const sortHeadersArray = Object.keys(headersArray).sort().reduce((obj, key) => { + obj[key] = headersArray[key]; + return obj; + }, {}); + let SignedHeaders = ""; + let CanonicalHeaders = ""; + for (const key in sortHeadersArray) { + SignedHeaders += key.toLowerCase() + ";"; + } + SignedHeaders = SignedHeaders.slice(0, -1); + for (const key in sortHeadersArray) { + CanonicalHeaders += `${key.toLowerCase()}:${sortHeadersArray[key].toLowerCase()} +`; + } + const HashedRequestPayload = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(JSON.stringify(bodyArray)) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const CanonicalRequest = `${HTTPRequestMethod} +${CanonicalURI} +${CanonicalQueryString} +${CanonicalHeaders} +${SignedHeaders} +${HashedRequestPayload}`; + const RequestTimestamp = Math.floor(Date.now() / 1e3); + const formattedDate = new Date(RequestTimestamp * 1e3).toISOString().split("T")[0]; + const Algorithm = "TC3-HMAC-SHA256"; + const CredentialScope = `${formattedDate}/${service}/tc3_request`; + const HashedCanonicalRequest = await crypto.subtle.digest( + "SHA-256", + new TextEncoder().encode(CanonicalRequest) + ).then((hash) => Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("")); + const StringToSign = `${Algorithm} +${RequestTimestamp} +${CredentialScope} +${HashedCanonicalRequest}`; + async function hmac(key, string) { + const cryptoKey = await crypto.subtle.importKey( + "raw", + typeof key === "string" ? new TextEncoder().encode(key) : key, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ); + const signature = await crypto.subtle.sign( + "HMAC", + cryptoKey, + new TextEncoder().encode(string) + ); + return new Uint8Array(signature); + } + const SecretDate = await hmac("TC3" + secretKey, formattedDate); + const SecretService = await hmac(SecretDate, service); + const SecretSigning = await hmac(SecretService, "tc3_request"); + const Signature = Array.from( + new Uint8Array( + await crypto.subtle.sign( + "HMAC", + await crypto.subtle.importKey( + "raw", + SecretSigning, + { name: "HMAC", hash: "SHA-256" }, + false, + ["sign"] + ), + new TextEncoder().encode(StringToSign) + ) + ) + ).map((b) => b.toString(16).padStart(2, "0")).join(""); + const Authorization = `${Algorithm} Credential=${secretId}/${CredentialScope}, SignedHeaders=${SignedHeaders}, Signature=${Signature}`; + headersArray["X-TC-Timestamp"] = RequestTimestamp.toString(); + headersArray["Authorization"] = Authorization; + return headersArray; + } + function onRequestOptions(context) { + return new Response(null, { + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type" + } + }); + } + async function onRequestPost(context) { + try { + const data = await context.request.json(); + const { secretId, secretKey, zoneId, type, targets } = data; + const service = "teo"; + const host = "teo.tencentcloudapi.com"; + const payload = { + ZoneId: zoneId, + Type: type, + Targets: targets + }; + const headersPending = { + "Host": host, + "Content-Type": "application/json", + "X-TC-Action": "CreatePurgeTask", + "X-TC-Version": "2022-09-01", + "X-TC-Region": "ap-guangzhou" + }; + const headers = await qcloudV3Post(secretId, secretKey, service, payload, headersPending); + const response = await fetch(`https://${host}`, { + method: "POST", + headers, + body: JSON.stringify(payload) + }); + const result = await response.json(); + return new Response(JSON.stringify(result), { + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + status: 500, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + } + function onRequest(context) { + return new Response(JSON.stringify({ error: "Only POST method is allowed" }), { + status: 405, + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*" + } + }); + } + + pagesFunctionResponse = onRequestOptions; + })(); + } + + + const params = {}; + if (routeParams.id) { + if (routeParams.mode === 1) { + const value = urlInfo.pathname.match(routeParams.left); + for (let i = 1; i < value.length; i++) { + params[routeParams.id[i - 1]] = value[i]; + } + } else { + const value = urlInfo.pathname.replace(routeParams.left, ''); + const splitedValue = value.split('/'); + if (splitedValue.length === 1) { + params[routeParams.id] = splitedValue[0]; + } else { + params[routeParams.id] = splitedValue; + } + } + + } + if(!matchedFunc){ + pagesFunctionResponse = function() { + return new Response(null, { + status: 404, + headers: { + "content-type": "text/html; charset=UTF-8", + "x-edgefunctions-test": "Welcome to use Pages Functions.", + }, + }); + } + } + return pagesFunctionResponse({request, params, env: {} }); + }addEventListener('fetch',event=>{return event.respondWith(handleRequest({request:event.request,params: {}, env: {} }))}); \ No newline at end of file diff --git a/.edgeone/meta.json b/.edgeone/meta.json new file mode 100644 index 0000000..e720d15 --- /dev/null +++ b/.edgeone/meta.json @@ -0,0 +1,29 @@ +{ + "conf": {}, + "routes": [ + { + "routePath": "/api/clean-eo-cache", + "mountPath": "/api", + "method": "", + "module": [ + "api/clean-eo-cache.js:onRequest" + ] + }, + { + "routePath": "/api/clean-eo-cache", + "mountPath": "/api", + "method": "POST", + "module": [ + "api/clean-eo-cache.js:onRequestPost" + ] + }, + { + "routePath": "/api/clean-eo-cache", + "mountPath": "/api", + "method": "OPTIONS", + "module": [ + "api/clean-eo-cache.js:onRequestOptions" + ] + } + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index e474f3b..5ef6a52 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,41 @@ -# Tencent Cloud EdgeOne -.env -.edgeone/* -!.edgeone/project.json -.tef_dist/* \ No newline at end of file +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/form.tsx b/app/form.tsx new file mode 100644 index 0000000..1be2216 --- /dev/null +++ b/app/form.tsx @@ -0,0 +1,684 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { useToast } from "@/components/ui/use-toast"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; +import { Save, Trash, CheckCircle2, XCircle, Pencil } from "lucide-react"; +import Link from "next/link"; + +interface SavedConfig { + id: string; + name: string; + secretId: string; + secretKey: string; + zoneId: string; +} + +interface EOApiResponse { + Response: { + RequestId: string; + JobId: string; + FailedList: string[]; + Error?: { + Message: string; + Code: string; + }; + }; +} + +export function CleanCacheForm() { + const [secretId, setSecretId] = useState(""); + const [secretKey, setSecretKey] = useState(""); + const [zoneId, setZoneId] = useState(""); + const [loading, setLoading] = useState(false); + const [dialogOpen, setDialogOpen] = useState(false); + const [result, setResult] = useState(null); + const [savedConfigs, setSavedConfigs] = useState([]); + const [saveDialogOpen, setSaveDialogOpen] = useState(false); + const [configName, setConfigName] = useState(""); + const [editingConfigId, setEditingConfigId] = useState(null); + const [urls, setUrls] = useState(""); + const [prefixes, setPrefixes] = useState(""); + const [hosts, setHosts] = useState(""); + const [tags, setTags] = useState(""); + const { toast } = useToast(); + + useEffect(() => { + // 加载保存的配置列表 + const configs = localStorage.getItem("savedConfigs"); + if (configs) { + setSavedConfigs(JSON.parse(configs)); + } + }, []); + + const saveConfig = () => { + if (!configName.trim()) { + toast({ + variant: "destructive", + title: "错误", + description: "请输入配置名称", + }); + return; + } + + if (!secretId || !secretKey || !zoneId) { + toast({ + variant: "destructive", + title: "错误", + description: "请填写完整的配置信息!", + }); + return; + } + + let updatedConfigs: SavedConfig[]; + + if (editingConfigId) { + // 编辑现有配置 + updatedConfigs = savedConfigs.map(config => { + if (config.id === editingConfigId) { + return { + ...config, + name: configName, + secretId, + secretKey, + zoneId + }; + } + return config; + }); + + toast({ + title: "成功", + description: "配置已更新", + }); + } else { + // 添加新配置 + const newConfig: SavedConfig = { + id: Date.now().toString(), + name: configName, + secretId, + secretKey, + zoneId, + }; + + updatedConfigs = [...savedConfigs, newConfig]; + + toast({ + title: "成功", + description: "配置已保存", + }); + } + + setSavedConfigs(updatedConfigs); + localStorage.setItem("savedConfigs", JSON.stringify(updatedConfigs)); + setSaveDialogOpen(false); + setConfigName(""); + setEditingConfigId(null); + }; + + const editConfig = (config: SavedConfig) => { + setConfigName(config.name); + setSecretId(config.secretId); + setSecretKey(config.secretKey); + setZoneId(config.zoneId); + setEditingConfigId(config.id); + setSaveDialogOpen(true); + }; + + const loadConfig = (config: SavedConfig) => { + setSecretId(config.secretId); + setSecretKey(config.secretKey); + setZoneId(config.zoneId); + + toast({ + title: "成功", + description: "配置已加载", + }); + }; + + const deleteConfig = (id: string) => { + const updatedConfigs = savedConfigs.filter(config => config.id !== id); + setSavedConfigs(updatedConfigs); + localStorage.setItem("savedConfigs", JSON.stringify(updatedConfigs)); + + toast({ + title: "成功", + description: "配置已删除", + }); + }; + + const purgeUrls = async () => { + if (!urls.trim()) { + toast({ + variant: "destructive", + title: "错误", + description: "请输入需要刷新的URL", + }); + return; + } + + await callApi("purge_url", urls.split("\n").map(t => t.trim()).filter(t => t)); + }; + + const purgePrefixes = async () => { + if (!prefixes.trim()) { + toast({ + variant: "destructive", + title: "错误", + description: "请输入需要刷新的目录", + }); + return; + } + + await callApi("purge_prefix", prefixes.split("\n").map(t => t.trim()).filter(t => t)); + }; + + const purgeHosts = async () => { + if (!hosts.trim()) { + toast({ + variant: "destructive", + title: "错误", + description: "请输入需要刷新的主机", + }); + return; + } + + await callApi("purge_host", hosts.split("\n").map(t => t.trim()).filter(t => t)); + }; + + const purgeTags = async () => { + if (!tags.trim()) { + toast({ + variant: "destructive", + title: "错误", + description: "请输入需要刷新的缓存标签", + }); + return; + } + + await callApi("purge_cache_tag", tags.split("\n").map(t => t.trim()).filter(t => t)); + }; + + const purgeAll = async () => { + await callApi("purge_all", []); + }; + + const callApi = async (type: string, targets: string[], method = "invalidate") => { + if (!secretId || !secretKey || !zoneId) { + toast({ + variant: "destructive", + title: "错误", + description: "请填写完整的配置信息!", + }); + return; + } + + setLoading(true); + try { + const response = await fetch("/api/eo-cleancache", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + secretId, + secretKey, + zoneId, + type, + targets, + method, + }), + }); + + const result = await response.json(); + setResult(result); + setDialogOpen(true); + + if (result.Response && !result.Response.Error) { + toast({ + title: "成功", + description: "操作成功!", + }); + } else { + toast({ + variant: "destructive", + title: "失败", + description: `操作失败:${ + result.Response?.Error?.Message || "未知错误" + }`, + }); + } + } catch (error) { + toast({ + variant: "destructive", + title: "错误", + description: `请求失败:${(error as Error).message}`, + }); + setResult({ + Response: { + RequestId: "", + JobId: "", + FailedList: [], + Error: { + Message: (error as Error).message, + Code: "RequestError" + } + } + }); + setDialogOpen(true); + } finally { + setLoading(false); + } + }; + + return ( + + + 腾讯云EdgeOne缓存刷新工具 + + 数据保存在浏览器本地,不会上传到任何服务器。通用国内站和国际站。 + + + + + +
+
+ 已保存的配置 + 点击配置名称可快速加载 +
+ +
+
+ + {savedConfigs.length === 0 ? ( +

暂无保存的配置

+ ) : ( +
+ {savedConfigs.map((config) => ( +
+ +
+ + +
+
+ ))} +
+ )} +
+
+ +
+
+ + setSecretId(e.target.value)} + placeholder="请输入 SecretId" + /> +
+ + 国内站: https://console.cloud.tencent.com/cam/capi + + + 国际站: https://console.intl.cloud.tencent.com/cam/capi + +
+
+ +
+ + setSecretKey(e.target.value)} + placeholder="请输入 SecretKey" + /> +
+ +
+ + setZoneId(e.target.value)} + placeholder="请输入 ZoneId" + /> +
+ + 国内站: https://console.cloud.tencent.com/edgeone/zones + + + 国际站: https://console.intl.cloud.tencent.com/edgeone/zones + +
+
+
+ + +
+ + + 刷新全部 + + + URL刷新 + + + 目录刷新 + + + Host刷新 + + + Cache Tag + + +
+ + + + + 刷新全部缓存 + + 将清理该域名下的所有缓存文件 + + + + + + + + + + + + 按URL刷新 + + 输入需要刷新的URL地址,每行一个 + + + +