myblog-1d4.pages.dev
Open in
urlscan Pro
172.66.47.5
Public Scan
URL:
https://myblog-1d4.pages.dev/posts/2024-022/
Submission Tags: @ecarlesi possiblethreat phishing Search All
Submission: On August 29 via api from IT — Scanned from IT
Submission Tags: @ecarlesi possiblethreat phishing Search All
Submission: On August 29 via api from IT — Scanned from IT
Form analysis
1 forms found in the DOMjavascript:void(0);
<form class="pagefind-ui__form svelte-e9gkc3" role="search" aria-label="站内搜索" action="javascript:void(0);"><input class="pagefind-ui__search-input svelte-e9gkc3" type="text" placeholder="搜索" autocapitalize="none" enterkeyhint="search"> <button
class="pagefind-ui__search-clear svelte-e9gkc3 pagefind-ui__suppressed">清除</button>
<div class="pagefind-ui__drawer svelte-e9gkc3 pagefind-ui__hidden"> </div>
</form>
Text Content
skip to content 槿呈Goidea Home Posts About Close 清除 Dark Theme TABLE OF CONTENTS * #配置 R2 Uploader Worker * #部署前端界面 * #在前端界面进行设置 * #Reference CLOUDFLARE R2 的完美搭档:R2 UPLOADER 使用指南 2024年8月8日 / 34 min read tech Cloudflare R2 作为一款出色的对象存储服务,以其慷慨的免费额度和低廉的价格(前提是合理设置数据访问频率,不然就和我一样被账单吓到哭泣)赢得了广泛青睐。然而,其简陋的图形界面却给日常管理带来了诸多不便,如无法直接移动文件、缺乏缩略图预览等功能。为了解决这些痛点,GitHub 上应运而生了 R2 Uploader 项目,它不仅弥补了 R2 的不足,还带来了一系列实用功能: 1. 大文件上传:突破 R2 dashboard 300MB 的上传限制,R2 Uploader 理论上可支持高达 100GB 的单文件上传。 2. 图像压缩:针对将 R2 用作 CDN 的用户,内置图像压缩功能显著提升了图片加载速度。 3. 多数据桶快速切换:支持为不同数据桶设置多个 Worker,并实现它们之间的便捷切换。 4. 跨设备安全同步:通过 GitHub 登录,实现配置的安全同步。所有数据在传输到数据库前都经过本地 AES 加密处理。 在实际使用过程中,我发现 R2 Uploader 还能与 WebP Cloud 完美配合,进一步增强了其实用性。 配置 R2 UPLOADER WORKER 1. 前往 Cloudflare 控制面板。 2. 左侧面板上有一个名为 “Workers 和 Pages” 的部分,点击它,进入到「概述」页面,再点击「创建」按钮。 3. 然后点击「创建 Worker」按钮。 4. 在名称部分,可以输入喜欢的名称,也可以忽略,直接点击「部署」按钮。 5. 之后点击「编辑代码」按钮,删除掉代码编辑器中的内容。 6. 在代码编辑器内,粘贴以下代码(源码可以在这里找到,也可以据此自行构建代码),然后点击「部署」按钮。 点击查看代码 var te = async (e, t = Object.create(null)) => { let { all: s = !1, dot: r = !1 } = t, o = (e instanceof S ? e.raw.headers : e.headers).get("Content-Type"); return (o !== null && o.startsWith("multipart/form-data")) || (o !== null && o.startsWith("application/x-www-form-urlencoded")) ? Pe(e, { all: s, dot: r }) : {}; }; async function Pe(e, t) { let s = await e.formData(); return s ? Ae(s, t) : {}; } function Ae(e, t) { let s = Object.create(null); return ( e.forEach((r, n) => { t.all || n.endsWith("[]") ? He(s, n, r) : (s[n] = r); }), t.dot && Object.entries(s).forEach(([r, n]) => { r.includes(".") && (Te(s, r, n), delete s[r]); }), s ); } var He = (e, t, s) => { e[t] !== void 0 ? Array.isArray(e[t]) ? e[t].push(s) : (e[t] = [e[t], s]) : (e[t] = s); }, Te = (e, t, s) => { let r = e, n = t.split("."); n.forEach((o, c) => { c === n.length - 1 ? (r[o] = s) : ((!r[o] || typeof r[o] != "object" || Array.isArray(r[o]) || r[o] instanceof File) && (r[o] = Object.create(null)), (r = r[o])); }); }; var D = e => { let t = e.split("/"); return t[0] === "" && t.shift(), t; }, re = e => { let { groups: t, path: s } = je(e), r = D(s); return Se(r, t); }, je = e => { let t = []; return ( (e = e.replace(/\{[^}]+\}/g, (s, r) => { let n = `@${r}`; return t.push([n, s]), n; })), { groups: t, path: e } ); }, Se = (e, t) => { for (let s = t.length - 1; s >= 0; s--) { let [r] = t[s]; for (let n = e.length - 1; n >= 0; n--) if (e[n].includes(r)) { e[n] = e[n].replace(r, t[s][1]); break; } } return e; }, _ = {}, $ = e => { if (e === "*") return "*"; let t = e.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); return t ? (_[e] || (t[2] ? (_[e] = [e, t[1], new RegExp("^" + t[2] + "$")]) : (_[e] = [e, t[1], !0])), _[e]) : null; }, _e = e => { try { return decodeURI(e); } catch { return e.replace(/(?:%[0-9A-Fa-f]{2})+/g, t => { try { return decodeURI(t); } catch { return t; } }); } }, K = e => { let t = e.url, s = t.indexOf("/", 8), r = s; for (; r < t.length; r++) { let n = t.charCodeAt(r); if (n === 37) { let o = t.indexOf("?", r), c = t.slice(s, o === -1 ? void 0 : o); return _e(c.includes("%25") ? c.replace(/%25/g, "%2525") : c); } else if (n === 63) break; } return t.slice(s, r); }; var se = e => { let t = K(e); return t.length > 1 && t[t.length - 1] === "/" ? t.slice(0, -1) : t; }, b = (...e) => { let t = "", s = !1; for (let r of e) t[t.length - 1] === "/" && ((t = t.slice(0, -1)), (s = !0)), r[0] !== "/" && (r = `/${r}`), r === "/" && s ? (t = `${t}/`) : r !== "/" && (t = `${t}${r}`), r === "/" && t === "" && (t = "/"); return t; }, k = e => { if (!e.match(/\:.+\?$/)) return null; let t = e.split("/"), s = [], r = ""; return ( t.forEach(n => { if (n !== "" && !/\:/.test(n)) r += "/" + n; else if (/\:/.test(n)) if (/\?/.test(n)) { s.length === 0 && r === "" ? s.push("/") : s.push(r); let o = n.replace("?", ""); (r += "/" + o), s.push(r); } else r += "/" + n; }), s.filter((n, o, c) => c.indexOf(n) === o) ); }, L = e => /[%+]/.test(e) ? (e.indexOf("+") !== -1 && (e = e.replace(/\+/g, " ")), /%/.test(e) ? M(e) : e) : e, ne = (e, t, s) => { let r; if (!s && t && !/[%+]/.test(t)) { let c = e.indexOf(`?${t}`, 8); for (c === -1 && (c = e.indexOf(`&${t}`, 8)); c !== -1; ) { let a = e.charCodeAt(c + t.length + 1); if (a === 61) { let i = c + t.length + 2, l = e.indexOf("&", i); return L(e.slice(i, l === -1 ? void 0 : l)); } else if (a == 38 || isNaN(a)) return ""; c = e.indexOf(`&${t}`, c + 1); } if (((r = /[%+]/.test(e)), !r)) return; } let n = {}; r ??= /[%+]/.test(e); let o = e.indexOf("?", 8); for (; o !== -1; ) { let c = e.indexOf("&", o + 1), a = e.indexOf("=", o); a > c && c !== -1 && (a = -1); let i = e.slice(o + 1, a === -1 ? (c === -1 ? void 0 : c) : a); if ((r && (i = L(i)), (o = c), i === "")) continue; let l; a === -1 ? (l = "") : ((l = e.slice(a + 1, c === -1 ? void 0 : c)), r && (l = L(l))), s ? ((n[i] && Array.isArray(n[i])) || (n[i] = []), n[i].push(l)) : (n[i] ??= l); } return t ? n[t] : n; }, oe = ne, ie = (e, t) => ne(e, t, !0), M = decodeURIComponent; var S = class { raw; #r; #s; routeIndex = 0; path; bodyCache = {}; constructor(e, t = "/", s = [[]]) { (this.raw = e), (this.path = t), (this.#s = s), (this.#r = {}); } param(e) { return e ? this.getDecodedParam(e) : this.getAllDecodedParams(); } getDecodedParam(e) { let t = this.#s[0][this.routeIndex][1][e], s = this.getParamValue(t); return s ? (/\%/.test(s) ? M(s) : s) : void 0; } getAllDecodedParams() { let e = {}, t = Object.keys(this.#s[0][this.routeIndex][1]); for (let s of t) { let r = this.getParamValue(this.#s[0][this.routeIndex][1][s]); r && typeof r == "string" && (e[s] = /\%/.test(r) ? M(r) : r); } return e; } getParamValue(e) { return this.#s[1] ? this.#s[1][e] : e; } query(e) { return oe(this.url, e); } queries(e) { return ie(this.url, e); } header(e) { if (e) return this.raw.headers.get(e.toLowerCase()) ?? void 0; let t = {}; return ( this.raw.headers.forEach((s, r) => { t[r] = s; }), t ); } async parseBody(e) { return (this.bodyCache.parsedBody ??= await te(this, e)); } cachedBody = e => { let { bodyCache: t, raw: s } = this, r = t[e]; if (r) return r; let n = Object.keys(t)[0]; return n ? t[n].then( o => (n === "json" && (o = JSON.stringify(o)), new Response(o)[e]()) ) : (t[e] = s[e]()); }; json() { return this.cachedBody("json"); } text() { return this.cachedBody("text"); } arrayBuffer() { return this.cachedBody("arrayBuffer"); } blob() { return this.cachedBody("blob"); } formData() { return this.cachedBody("formData"); } addValidatedData(e, t) { this.#r[e] = t; } valid(e) { return this.#r[e]; } get url() { return this.raw.url; } get method() { return this.raw.method; } get matchedRoutes() { return this.#s[0].map(([[, e]]) => e); } get routePath() { return this.#s[0].map(([[, e]]) => e)[this.routeIndex].path; } }; var ae = { Stringify: 1, BeforeStream: 2, Stream: 3 }, ke = (e, t) => { let s = new String(e); return (s.isEscaped = !0), (s.callbacks = t), s; }; var F = async (e, t, s, r, n) => { let o = e.callbacks; if (!o?.length) return Promise.resolve(e); n ? (n[0] += e) : (n = [e]); let c = Promise.all(o.map(a => a({ phase: t, buffer: n, context: r }))).then( a => Promise.all(a.filter(Boolean).map(i => F(i, t, !1, r, n))).then( () => n[0] ) ); return s ? ke(await c, o) : c; }; var Me = "text/plain; charset=UTF-8", V = (e, t = {}) => (Object.entries(t).forEach(([s, r]) => e.set(s, r)), e), v = class { #r; #s; env = {}; #a; finalized = !1; error; #c = 200; #o; #e; #t; #n; #i = !0; #u; #l; #h; #d; #f; constructor(e, t) { (this.#r = e), t && ((this.#o = t.executionCtx), (this.env = t.env), (this.#h = t.notFoundHandler), (this.#f = t.path), (this.#d = t.matchResult)); } get req() { return (this.#s ??= new S(this.#r, this.#f, this.#d)), this.#s; } get event() { if (this.#o && "respondWith" in this.#o) return this.#o; throw Error("This context has no FetchEvent"); } get executionCtx() { if (this.#o) return this.#o; throw Error("This context has no ExecutionContext"); } get res() { return ( (this.#i = !1), (this.#n ||= new Response("404 Not Found", { status: 404 })) ); } set res(e) { if (((this.#i = !1), this.#n && e)) { this.#n.headers.delete("content-type"); for (let [t, s] of this.#n.headers.entries()) if (t === "set-cookie") { let r = this.#n.headers.getSetCookie(); e.headers.delete("set-cookie"); for (let n of r) e.headers.append("set-cookie", n); } else e.headers.set(t, s); } (this.#n = e), (this.finalized = !0); } render = (...e) => ((this.#l ??= t => this.html(t)), this.#l(...e)); setLayout = e => (this.#u = e); getLayout = () => this.#u; setRenderer = e => { this.#l = e; }; header = (e, t, s) => { if (t === void 0) { this.#e ? this.#e.delete(e) : this.#t && delete this.#t[e.toLocaleLowerCase()], this.finalized && this.res.headers.delete(e); return; } s?.append ? (this.#e || ((this.#i = !1), (this.#e = new Headers(this.#t)), (this.#t = {})), this.#e.append(e, t)) : this.#e ? this.#e.set(e, t) : ((this.#t ??= {}), (this.#t[e.toLowerCase()] = t)), this.finalized && (s?.append ? this.res.headers.append(e, t) : this.res.headers.set(e, t)); }; status = e => { (this.#i = !1), (this.#c = e); }; set = (e, t) => { (this.#a ??= {}), (this.#a[e] = t); }; get = e => (this.#a ? this.#a[e] : void 0); get var() { return { ...this.#a }; } newResponse = (e, t, s) => { if (this.#i && !s && !t && this.#c === 200) return new Response(e, { headers: this.#t }); if (t && typeof t != "number") { let n = new Headers(t.headers); this.#e && this.#e.forEach((c, a) => { a === "set-cookie" ? n.append(a, c) : n.set(a, c); }); let o = V(n, this.#t); return new Response(e, { headers: o, status: t.status ?? this.#c }); } let r = typeof t == "number" ? t : this.#c; (this.#t ??= {}), (this.#e ??= new Headers()), V(this.#e, this.#t), this.#n && (this.#n.headers.forEach((n, o) => { o === "set-cookie" ? this.#e?.append(o, n) : this.#e?.set(o, n); }), V(this.#e, this.#t)), (s ??= {}); for (let [n, o] of Object.entries(s)) if (typeof o == "string") this.#e.set(n, o); else { this.#e.delete(n); for (let c of o) this.#e.append(n, c); } return new Response(e, { status: r, headers: this.#e }); }; body = (e, t, s) => typeof t == "number" ? this.newResponse(e, t, s) : this.newResponse(e, t); text = (e, t, s) => { if (!this.#t) { if (this.#i && !s && !t) return new Response(e); this.#t = {}; } return ( (this.#t["content-type"] = Me), typeof t == "number" ? this.newResponse(e, t, s) : this.newResponse(e, t) ); }; json = (e, t, s) => { let r = JSON.stringify(e); return ( (this.#t ??= {}), (this.#t["content-type"] = "application/json; charset=UTF-8"), typeof t == "number" ? this.newResponse(r, t, s) : this.newResponse(r, t) ); }; html = (e, t, s) => ( (this.#t ??= {}), (this.#t["content-type"] = "text/html; charset=UTF-8"), typeof e == "object" && (e instanceof Promise || (e = e.toString()), e instanceof Promise) ? e .then(r => F(r, ae.Stringify, !1, {})) .then(r => typeof t == "number" ? this.newResponse(r, t, s) : this.newResponse(r, t) ) : typeof t == "number" ? this.newResponse(e, t, s) : this.newResponse(e, t) ); redirect = (e, t) => ( (this.#e ??= new Headers()), this.#e.set("Location", e), this.newResponse(null, t ?? 302) ); notFound = () => ((this.#h ??= () => new Response()), this.#h(this)); }; var W = (e, t, s) => (r, n) => { let o = -1; return c(0); async function c(a) { if (a <= o) throw new Error("next() called multiple times"); o = a; let i, l = !1, h; if ( (e[a] ? ((h = e[a][0][0]), r instanceof v && (r.req.routeIndex = a)) : (h = (a === e.length && n) || void 0), !h) ) r instanceof v && r.finalized === !1 && s && (i = await s(r)); else try { i = await h(r, () => c(a + 1)); } catch (u) { if (u instanceof Error && r instanceof v && t) (r.error = u), (i = await t(u, r)), (l = !0); else throw u; } return i && (r.finalized === !1 || l) && (r.res = i), r; } }; var d = "ALL", ce = "all", le = ["get", "post", "put", "delete", "options", "patch"], B = "Can not add a route since the matcher is already built.", I = class extends Error {}; var Be = Symbol("composedHandler"), Ie = e => e.text("404 Not Found", 404), he = (e, t) => "getResponse" in e ? e.getResponse() : (console.error(e), t.text("Internal Server Error", 500)), G = class { get; post; put; delete; options; patch; all; on; use; router; getPath; _basePath = "/"; #r = "/"; routes = []; constructor(e = {}) { [...le, ce].forEach(r => { this[r] = (n, ...o) => ( typeof n == "string" ? (this.#r = n) : this.addRoute(r, this.#r, n), o.forEach(c => { typeof c != "string" && this.addRoute(r, this.#r, c); }), this ); }), (this.on = (r, n, ...o) => { for (let c of [n].flat()) { this.#r = c; for (let a of [r].flat()) o.map(i => { this.addRoute(a.toUpperCase(), this.#r, i); }); } return this; }), (this.use = (r, ...n) => ( typeof r == "string" ? (this.#r = r) : ((this.#r = "*"), n.unshift(r)), n.forEach(o => { this.addRoute(d, this.#r, o); }), this )); let s = e.strict ?? !0; delete e.strict, Object.assign(this, e), (this.getPath = s ? (e.getPath ?? K) : se); } clone() { let e = new G({ router: this.router, getPath: this.getPath }); return (e.routes = this.routes), e; } notFoundHandler = Ie; errorHandler = he; route(e, t) { let s = this.basePath(e); return ( t.routes.map(r => { let n; t.errorHandler === he ? (n = r.handler) : ((n = async (o, c) => (await W([], t.errorHandler)(o, () => r.handler(o, c))).res), (n[Be] = r.handler)), s.addRoute(r.method, r.path, n); }), this ); } basePath(e) { let t = this.clone(); return (t._basePath = b(this._basePath, e)), t; } onError = e => ((this.errorHandler = e), this); notFound = e => ((this.notFoundHandler = e), this); mount(e, t, s) { let r, n; s && (typeof s == "function" ? (n = s) : ((n = s.optionHandler), (r = s.replaceRequest))); let o = n ? a => { let i = n(a); return Array.isArray(i) ? i : [i]; } : a => { let i; try { i = a.executionCtx; } catch {} return [a.env, i]; }; r ||= (() => { let a = b(this._basePath, e), i = a === "/" ? 0 : a.length; return l => { let h = new URL(l.url); return (h.pathname = h.pathname.slice(i) || "/"), new Request(h, l); }; })(); let c = async (a, i) => { let l = await t(r(a.req.raw), ...o(a)); if (l) return l; await i(); }; return this.addRoute(d, b(e, "*"), c), this; } addRoute(e, t, s) { (e = e.toUpperCase()), (t = b(this._basePath, t)); let r = { path: t, method: e, handler: s }; this.router.add(e, t, [s, r]), this.routes.push(r); } matchRoute(e, t) { return this.router.match(e, t); } handleError(e, t) { if (e instanceof Error) return this.errorHandler(e, t); throw e; } dispatch(e, t, s, r) { if (r === "HEAD") return (async () => new Response(null, await this.dispatch(e, t, s, "GET")))(); let n = this.getPath(e, { env: s }), o = this.matchRoute(r, n), c = new v(e, { path: n, matchResult: o, env: s, executionCtx: t, notFoundHandler: this.notFoundHandler, }); if (o[0].length === 1) { let i; try { i = o[0][0][0][0](c, async () => { c.res = await this.notFoundHandler(c); }); } catch (l) { return this.handleError(l, c); } return i instanceof Promise ? i .then(l => l || (c.finalized ? c.res : this.notFoundHandler(c))) .catch(l => this.handleError(l, c)) : (i ?? this.notFoundHandler(c)); } let a = W(o[0], this.errorHandler, this.notFoundHandler); return (async () => { try { let i = await a(c); if (!i.finalized) throw new Error( "Context is not finalized. Did you forget to return a Response object or `await next()`?" ); return i.res; } catch (i) { return this.handleError(i, c); } })(); } fetch = (e, ...t) => this.dispatch(e, t[1], t[0], e.method); request = (e, t, s, r) => { if (e instanceof Request) return t !== void 0 && (e = new Request(e, t)), this.fetch(e, s, r); e = e.toString(); let n = /^https?:\/\//.test(e) ? e : `http://localhost${b("/", e)}`, o = new Request(n, t); return this.fetch(o, s, r); }; fire = () => { addEventListener("fetch", e => { e.respondWith(this.dispatch(e.request, e, void 0, e.request.method)); }); }; }; var U = "[^/]+", H = ".*", T = "(?:|/.*)", C = Symbol(), Ue = new Set(".\\+*[^]$()"); function qe(e, t) { return e.length === 1 ? t.length === 1 ? e < t ? -1 : 1 : -1 : t.length === 1 || e === H || e === T ? 1 : t === H || t === T ? -1 : e === U ? 1 : t === U ? -1 : e.length === t.length ? e < t ? -1 : 1 : t.length - e.length; } var q = class { index; varIndex; children = Object.create(null); insert(e, t, s, r, n) { if (e.length === 0) { if (this.index !== void 0) throw C; if (n) return; this.index = t; return; } let [o, ...c] = e, a = o === "*" ? c.length === 0 ? ["", "", H] : ["", "", U] : o === "/*" ? ["", "", T] : o.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/), i; if (a) { let l = a[1], h = a[2] || U; if ( l && a[2] && ((h = h.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:")), /\((?!\?:)/.test(h)) ) throw C; if (((i = this.children[h]), !i)) { if (Object.keys(this.children).some(u => u !== H && u !== T)) throw C; if (n) return; (i = this.children[h] = new q()), l !== "" && (i.varIndex = r.varIndex++); } !n && l !== "" && s.push([l, i.varIndex]); } else if (((i = this.children[o]), !i)) { if ( Object.keys(this.children).some(l => l.length > 1 && l !== H && l !== T) ) throw C; if (n) return; i = this.children[o] = new q(); } i.insert(c, t, s, r, n); } buildRegExpStr() { let t = Object.keys(this.children) .sort(qe) .map(s => { let r = this.children[s]; return ( (typeof r.varIndex == "number" ? `(${s})@${r.varIndex}` : Ue.has(s) ? `\\${s}` : s) + r.buildRegExpStr() ); }); return ( typeof this.index == "number" && t.unshift(`#${this.index}`), t.length === 0 ? "" : t.length === 1 ? t[0] : "(?:" + t.join("|") + ")" ); } }; var ue = class { context = { varIndex: 0 }; root = new q(); insert(e, t, s) { let r = [], n = []; for (let c = 0; ; ) { let a = !1; if ( ((e = e.replace(/\{[^}]+\}/g, i => { let l = `@\\${c}`; return (n[c] = [l, i]), c++, (a = !0), l; })), !a) ) break; } let o = e.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []; for (let c = n.length - 1; c >= 0; c--) { let [a] = n[c]; for (let i = o.length - 1; i >= 0; i--) if (o[i].indexOf(a) !== -1) { o[i] = o[i].replace(a, n[c][1]); break; } } return this.root.insert(o, t, r, this.context, s), r; } buildRegExp() { let e = this.root.buildRegExpStr(); if (e === "") return [/^$/, [], []]; let t = 0, s = [], r = []; return ( (e = e.replace(/#(\d+)|@(\d+)|\.\*\$/g, (n, o, c) => typeof o < "u" ? ((s[++t] = Number(o)), "$()") : (typeof c < "u" && (r[Number(c)] = ++t), "") )), [new RegExp(`^${e}`), s, r] ); } }; var de = [], Ne = [/^$/, [], Object.create(null)], fe = Object.create(null); function pe(e) { return (fe[e] ??= new RegExp( e === "*" ? "" : `^${e.replace(/\/\*$|([.\\+*[^\]$()])/g, (t, s) => (s ? `\\${s}` : "(?:|/.*)"))}$` )); } function Le() { fe = Object.create(null); } function De(e) { let t = new ue(), s = []; if (e.length === 0) return Ne; let r = e .map(l => [!/\*|\/:/.test(l[0]), ...l]) .sort(([l, h], [u, p]) => (l ? 1 : u ? -1 : h.length - p.length)), n = Object.create(null); for (let l = 0, h = -1, u = r.length; l < u; l++) { let [p, R, f] = r[l]; p ? (n[R] = [f.map(([y]) => [y, Object.create(null)]), de]) : h++; let m; try { m = t.insert(R, h, p); } catch (y) { throw y === C ? new I(R) : y; } p || (s[h] = f.map(([y, E]) => { let P = Object.create(null); for (E -= 1; E >= 0; E--) { let [x, j] = m[E]; P[x] = j; } return [y, P]; })); } let [o, c, a] = t.buildRegExp(); for (let l = 0, h = s.length; l < h; l++) for (let u = 0, p = s[l].length; u < p; u++) { let R = s[l][u]?.[1]; if (!R) continue; let f = Object.keys(R); for (let m = 0, y = f.length; m < y; m++) R[f[m]] = a[R[f[m]]]; } let i = []; for (let l in c) i[l] = s[c[l]]; return [o, i, n]; } function O(e, t) { if (e) { for (let s of Object.keys(e).sort((r, n) => n.length - r.length)) if (pe(s).test(t)) return [...e[s]]; } } var z = class { name = "RegExpRouter"; middleware; routes; constructor() { (this.middleware = { [d]: Object.create(null) }), (this.routes = { [d]: Object.create(null) }); } add(e, t, s) { let { middleware: r, routes: n } = this; if (!r || !n) throw new Error(B); r[e] || [r, n].forEach(a => { (a[e] = Object.create(null)), Object.keys(a[d]).forEach(i => { a[e][i] = [...a[d][i]]; }); }), t === "/*" && (t = "*"); let o = (t.match(/\/:/g) || []).length; if (/\*$/.test(t)) { let a = pe(t); e === d ? Object.keys(r).forEach(i => { r[i][t] ||= O(r[i], t) || O(r[d], t) || []; }) : (r[e][t] ||= O(r[e], t) || O(r[d], t) || []), Object.keys(r).forEach(i => { (e === d || e === i) && Object.keys(r[i]).forEach(l => { a.test(l) && r[i][l].push([s, o]); }); }), Object.keys(n).forEach(i => { (e === d || e === i) && Object.keys(n[i]).forEach(l => a.test(l) && n[i][l].push([s, o])); }); return; } let c = k(t) || [t]; for (let a = 0, i = c.length; a < i; a++) { let l = c[a]; Object.keys(n).forEach(h => { (e === d || e === h) && ((n[h][l] ||= [...(O(r[h], l) || O(r[d], l) || [])]), n[h][l].push([s, o - i + a + 1])); }); } } match(e, t) { Le(); let s = this.buildAllMatchers(); return ( (this.match = (r, n) => { let o = s[r] || s[d], c = o[2][n]; if (c) return c; let a = n.match(o[0]); if (!a) return [[], de]; let i = a.indexOf("", 1); return [o[1][i], a]; }), this.match(e, t) ); } buildAllMatchers() { let e = Object.create(null); return ( [...Object.keys(this.routes), ...Object.keys(this.middleware)].forEach( t => { e[t] ||= this.buildMatcher(t); } ), (this.middleware = this.routes = void 0), e ); } buildMatcher(e) { let t = [], s = e === d; return ( [this.middleware, this.routes].forEach(r => { let n = r[e] ? Object.keys(r[e]).map(o => [o, r[e][o]]) : []; n.length !== 0 ? ((s ||= !0), t.push(...n)) : e !== d && t.push(...Object.keys(r[d]).map(o => [o, r[d][o]])); }), s ? De(t) : null ); } }; var Y = class { name = "SmartRouter"; routers = []; routes = []; constructor(e) { Object.assign(this, e); } add(e, t, s) { if (!this.routes) throw new Error(B); this.routes.push([e, t, s]); } match(e, t) { if (!this.routes) throw new Error("Fatal error"); let { routers: s, routes: r } = this, n = s.length, o = 0, c; for (; o < n; o++) { let a = s[o]; try { r.forEach(i => { a.add(...i); }), (c = a.match(e, t)); } catch (i) { if (i instanceof I) continue; throw i; } (this.match = a.match.bind(a)), (this.routers = [a]), (this.routes = void 0); break; } if (o === n) throw new Error("Fatal error"); return (this.name = `SmartRouter + ${this.activeRouter.name}`), c; } get activeRouter() { if (this.routes || this.routers.length !== 1) throw new Error("No active router has been determined yet."); return this.routers[0]; } }; var Q = class { methods; children; patterns; order = 0; name; params = Object.create(null); constructor(e, t, s) { if ( ((this.children = s || Object.create(null)), (this.methods = []), (this.name = ""), e && t) ) { let r = Object.create(null); (r[e] = { handler: t, possibleKeys: [], score: 0, name: this.name }), (this.methods = [r]); } this.patterns = []; } insert(e, t, s) { (this.name = `${e} ${t}`), (this.order = ++this.order); let r = this, n = re(t), o = []; for (let i = 0, l = n.length; i < l; i++) { let h = n[i]; if (Object.keys(r.children).includes(h)) { r = r.children[h]; let p = $(h); p && o.push(p[1]); continue; } r.children[h] = new Q(); let u = $(h); u && (r.patterns.push(u), o.push(u[1])), (r = r.children[h]); } r.methods.length || (r.methods = []); let c = Object.create(null), a = { handler: s, possibleKeys: o.filter((i, l, h) => h.indexOf(i) === l), name: this.name, score: this.order, }; return (c[e] = a), r.methods.push(c), r; } gHSets(e, t, s, r) { let n = []; for (let o = 0, c = e.methods.length; o < c; o++) { let a = e.methods[o], i = a[t] || a[d], l = Object.create(null); i !== void 0 && ((i.params = Object.create(null)), i.possibleKeys.forEach(h => { let u = l[i.name]; (i.params[h] = r[h] && !u ? r[h] : (s[h] ?? r[h])), (l[i.name] = !0); }), n.push(i)); } return n; } search(e, t) { let s = []; this.params = Object.create(null); let n = [this], o = D(t); for (let a = 0, i = o.length; a < i; a++) { let l = o[a], h = a === i - 1, u = []; for (let p = 0, R = n.length; p < R; p++) { let f = n[p], m = f.children[l]; m && ((m.params = f.params), h === !0 ? (m.children["*"] && s.push( ...this.gHSets( m.children["*"], e, f.params, Object.create(null) ) ), s.push(...this.gHSets(m, e, f.params, Object.create(null)))) : u.push(m)); for (let y = 0, E = f.patterns.length; y < E; y++) { let P = f.patterns[y], x = { ...f.params }; if (P === "*") { let N = f.children["*"]; N && (s.push(...this.gHSets(N, e, f.params, Object.create(null))), u.push(N)); continue; } if (l === "") continue; let [j, Z, A] = P, w = f.children[j], ee = o.slice(a).join("/"); if (A instanceof RegExp && A.test(ee)) { (x[Z] = ee), s.push(...this.gHSets(w, e, f.params, x)); continue; } (A === !0 || (A instanceof RegExp && A.test(l))) && typeof j == "string" && ((x[Z] = l), h === !0 ? (s.push(...this.gHSets(w, e, x, f.params)), w.children["*"] && s.push(...this.gHSets(w.children["*"], e, x, f.params))) : ((w.params = x), u.push(w))); } } n = u; } return [ s .sort((a, i) => a.score - i.score) .map(({ handler: a, params: i }) => [a, i]), ]; } }; var X = class { name = "TrieRouter"; node; constructor() { this.node = new Q(); } add(e, t, s) { let r = k(t); if (r) { for (let n of r) this.node.insert(e, n, s); return; } this.node.insert(e, t, s); } match(e, t) { return this.node.search(e, t); } }; var J = class extends G { constructor(e = {}) { super(e), (this.router = e.router ?? new Y({ routers: [new z(), new X()] })); } }; var me = e => { let s = { ...{ origin: "*", allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"], allowHeaders: [], exposeHeaders: [], }, ...e, }, r = (n => typeof n == "string" ? () => n : typeof n == "function" ? n : o => (n.includes(o) ? o : n[0]))(s.origin); return async function (o, c) { function a(l, h) { o.res.headers.set(l, h); } let i = r(o.req.header("origin") || "", o); if ((i && a("Access-Control-Allow-Origin", i), s.origin !== "*")) { let l = o.req.header("Vary"); l ? a("Vary", l) : a("Vary", "Origin"); } if ( (s.credentials && a("Access-Control-Allow-Credentials", "true"), s.exposeHeaders?.length && a("Access-Control-Expose-Headers", s.exposeHeaders.join(",")), o.req.method === "OPTIONS") ) { s.maxAge != null && a("Access-Control-Max-Age", s.maxAge.toString()), s.allowMethods?.length && a("Access-Control-Allow-Methods", s.allowMethods.join(",")); let l = s.allowHeaders; if (!l?.length) { let h = o.req.header("Access-Control-Request-Headers"); h && (l = h.split(/\s*,\s*/)); } return ( l?.length && (a("Access-Control-Allow-Headers", l.join(",")), o.res.headers.append("Vary", "Access-Control-Request-Headers")), o.res.headers.delete("Content-Length"), o.res.headers.delete("Content-Type"), new Response(null, { headers: o.res.headers, status: 204, statusText: o.res.statusText, }) ); } await c(); }; }; async function ge(e) { let t = e.req.param("key"), s = await e.env.R2_BUCKET.get(t); if (s === null) return new Response("Object Not Found", { status: 404 }); let r = new Headers(); return ( s.writeHttpMetadata(r), r.set("etag", s.httpEtag), r.set("Access-Control-Allow-Origin", "*"), new Response(s.body, { headers: r }) ); } async function ye(e) { let t = e.req.query("cursor"), s = await e.env.R2_BUCKET.list({ cursor: t || void 0 }); return e.json(s); } async function Re(e) { let t = e.req.param("key"), s = await e.req.blob(); return t ? (await e.env.R2_BUCKET.put(t, s), e.text("Done")) : e.text("file name is required", 400); } async function xe(e) { let t = e.req.param("key"); return await e.env.R2_BUCKET.delete(t), new Response(null, { status: 204 }); } async function Ee(e) { let t = e.req.param("key"), s = await e.env.R2_BUCKET.createMultipartUpload(t); return e.json({ key: s.key, uploadId: s.uploadId }); } async function we(e) { let t = e.req.query("uploadId"), s = e.req.query("partNumber") || "", r = e.req.param("key"); if (!t) return e.text("uploadId is required", 400); if (!s) return e.text("partNumber is required", 400); let n = Number(s); if (isNaN(n)) return e.text("partNumber must be a number", 400); let o = e.env.R2_BUCKET.resumeMultipartUpload(r, t); try { let c = await o.uploadPart(n, e.req.raw.body); return new Response(JSON.stringify(c)); } catch (c) { return new Response(c.message, { status: 400 }); } } async function be(e) { let t = e.req.param("key"), s = e.req.query("uploadId"); if (!s) return e.text("uploadId is required", 400); let r = e.env.R2_BUCKET.resumeMultipartUpload(t, s); try { await r.abort(); } catch (n) { return new Response(n.message, { status: 400 }); } return new Response(null, { status: 204 }); } async function ve(e) { let t = e.req.param("key"), s = e.req.query("uploadId"); if (!s) return e.text("uploadId is required", 400); let r = e.env.R2_BUCKET.resumeMultipartUpload(t, s), n; try { n = await e.req.json(); } catch (o) { return ( console.log("parsing complete body failed"), console.log(o), e.text("invalid json", 400) ); } try { let o = await r.complete(n.parts); return new Response(null, { headers: { etag: o.httpEtag } }); } catch (o) { return new Response(o.message, { status: 400 }); } } function Ce(e) { return e.text("yes"); } function $e(e) { if (!e.env.AUTH_KEY_SECRET) return e.text("AUTH_KEY_SECRET is not set", 403); let t = e.req.header("x-api-key") === e.env.AUTH_KEY_SECRET; return e.req.method === "GET" ? e.env.PRIVATE_BUCKET ? t : !0 : ["POST", "PATCH", "PUT", "DELETE"].includes(e.req.method) ? t : e.req.method === "OPTIONS"; } async function Oe(e, t) { return ( $e(e) && (console.log("Header is valid"), await t()), e.json({ status: 401, message: "Unauthorized" }) ); } var g = new J(); g.use("*", Oe); g.use(me()); g.get("/", e => e.text("Hello R2! v2024.07.12")); g.get("/support_mpu", Ce); g.post("/mpu/create/:key{.*}", Ee); g.put("/mpu/:key{.*}", we); g.delete("/mpu/:key{.*}", be); g.post("/mpu/complete/:key{.*}", ve); g.get("/:key{.*}", ge); g.patch("/", ye); g.put("/:key{.*}", Re); g.delete("/:key{.*}", xe); g.all("*", e => e.text("404 Not Found")); var gr = g; export { gr as default }; //# sourceMappingURL=index.js.map 7. 进入到「设置」页面,选择「变量」,然后点击「编辑变量」按钮,添加环境变量。首先设置第一个变量名为 AUTH_KEY_SECRET,变量值为随机字符,可以在此处生成一个。然后设置第二个变量名为 PRIVATE_BUCKET,变量值为 true。因为默认情况下,Worker 会允许所有 GET 请求通过,这意味着只要知道 Woker 的 URL,任何人都可以访问您的文件。所以添加这个环境变量,将使 Worker 检查每个请求的 x-api-key 标头,并只允许带有正确 API 密钥的请求通过。添加完成后,点击「部署」按钮。 8. 下拉这个页面找到「R2 存储桶绑定」,点击「编辑变量」按钮,添加存储桶。在变量名称初填写 R2_BUCKET,然后选择您的存储桶,点击「部署」按钮。 9. 在这个页面中选择「触发器」,点击「添加自定义域」按钮,为 Worker 添加一个域名。如果您身处中国大陆,建议添加,因为默认的 workers.dev 域名被 GFW 屏蔽了,不能正常工作。您也可以不添加自定义域名,那么就是使用红框中的路由地址。 10. 大功告成,R2 Uploader 的 Worker 就配置好了。现在点击您在上一步中添加的自定义域名或者路由地址,就会在新页面中看到 {"status":401,"message":"Unauthorized"} 的字样,这是因为在第七步中我们添加了存储桶私有的环境变量。如果您在第七步中没有添加该变量,将会看到 Hello R2! 的字样。 部署前端界面 理论上,当配置完 R2 Uploader 的 Worker 后,就可以使用了。例如,您可以直接使用该项目的开发者提供的前端界面。但我更建议自己部署一个前端界面。 R2 Uploader 的社区贡献者提供了使用 Docker 部署的方式。 以在 Zeabur 上部署为例,简单介绍。您可以选择在 VPS 上部署。 1. 在 GitHub 上打开 R2 Uploader 的项目页面,将其 fork 到自己的仓库。 2. 在 Zeabur 上新建项目,选择从 GitHub 仓库部署,选择刚 fork 的 R2 Uploader 项目。 3. 点击「部署」按钮,等待部署完成。 4. 在「网络」中生成一个域名或添加自定义域名。 5. 大功告成,现在您就可以访问前端界面了。 在前端界面进行设置 1. 展开「Endpoints」 2. 在 Wokers Endpoint 中输入您在配置 R2 Uploader Worker 时,在「触发器」中自定义的域名或者 worker 的默认路由。 3. 在 Workers Endpoint API Key 中输入您在配置 R2 Uploader Worker 时,在第七步中设置的环境变量 AUTH_KEY_SECRET 的值,是您生成的一段随机字符,可以在 Woker 的变量中查看。 4. 点击 Save To LocalStorage。R2 Uploader 不会将您的 Endpoint 或 API Key 存储在云中,而是存储在浏览器的 LocalStorage 中,这意味着只有您才能访问。所有流量都会经过 Worker 和您的 R2 存储桶。 5. (可选)您可以选择登陆 GitHub,保存您的 Endpoints 中的设置的数据。不过我并没有这样做,而是将这些数据保存在 Bitwarden 中。 现在您就可以在这个前端页面中看到您在 R2 中存储的文件了。 在 Upload Files 中可以选择上传单个文件或者文件夹。 File List 中会显示您的存储桶中有多少个文件,使用了多少空间。文件列表默认按照文件夹的形式进行展示,您可以点击文件名称在新页面打开文件,或者点击 “Delete” 按钮删除文件。如果您不像以文件夹的形式展示,取消 “Folder Structure” 的勾选即可。 如果您和我一样,按照 Pseudoyu 的教程为 R2 中保存的文件配置了 WebP Cloud,您会发现点击文件名无法打开图片。这是因为在配置 WebP Cloud 时,在 Cloudflare 的防火墙中设置了禁止请求文件源站。 不过不必担心,在 Endpoints 中点击 “edit” 按钮,在 Custom Domain 中输入 WebP Cloud 为您分配的域名(不需要有 “https://”),然后点击保存即可。 现在您在点击文件名,就可以直接通过 WebP Cloud 的代理打开图片了。 另外在提一点,WebP Cloud 刚刚上线了「自适应调整大小」的功能,在 Proxy 中打开 Adaptive Resizer 即可。WebP Cloud 将根据访问者的设备处理不同尺寸的图片渲染。 以上就是部署和配置 R2 Uploader 的全部步骤。 REFERENCE 1. https://r2.jw1.dev/setup-guide/ 2. https://github.com/jw-12138/r2-uploader © Leslie 2015-2024. CC BY-NC-SA 4.0 DEED Home Posts About