astropaperblog.pages.dev
Open in
urlscan Pro
188.114.97.3
Public Scan
URL:
https://astropaperblog.pages.dev/posts/2024-022/
Submission Tags: @ecarlesi possiblethreat phishing Search All
Submission: On September 05 via api from IT — Scanned from NL
Submission Tags: @ecarlesi possiblethreat phishing Search All
Submission: On September 05 via api from IT — Scanned from NL
Form analysis
0 forms found in the DOMText Content
Skip to content 槿呈Goidea * Posts * Newsletter * MemEX * Tags * Search * -------------------------------------------------------------------------------- Go back CLOUDFLARE R2 的完美搭档:R2 UPLOADER 使用指南 Published:2024年8月8日 | at 12:29 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 完美配合,进一步增强了其实用性。 TABLE OF CONTENTS# Open Table of contents * 配置 R2 Uploader Worker * 部署前端界面 * 在前端界面进行设置 * Reference 配置 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 Copy 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 * tech Back to Top Share this post on: Share this post via WhatsApp Share this post on Facebook Tweet this post Share this post via Telegram Share this post on Pinterest Share this post via email 昵称 邮箱 网址 0/500 * OωO * |´・ω・)ノ * ヾ(≧∇≦*)ゝ * (☆ω☆) * (╯‵□′)╯︵┴─┴ *  ̄﹃ ̄ * (/ω\) * ∠( ᐛ 」∠)_ * (๑•̀ㅁ•́ฅ) * →_→ * ୧(๑•̀⌄•́๑)૭ * ٩(ˊᗜˋ*)و * (ノ°ο°)ノ * (´இ皿இ`) * ⌇●﹏●⌇ * (ฅ´ω`ฅ) * (╯°A°)╯︵○○○ * φ( ̄∇ ̄o) * ヾ(´・ ・`。)ノ" * ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃ * (ó﹏ò。) * Σ(っ °Д °;)っ * ( ,,´・ω・)ノ"(´っω・`。) * ╮(╯▽╰)╭ * o(*////▽////*)q * >﹏< * ( ๑´•ω•) "(ㆆᴗㆆ) * 😂 * 😀 * 😅 * 😊 * 🙂 * 🙃 * 😌 * 😍 * 😘 * 😜 * 😝 * 😏 * 😒 * 🙄 * 😳 * 😡 * 😔 * 😫 * 😱 * 😭 * 💩 * 👻 * 🙌 * 🖕 * 👍 * 👫 * 👬 * 👭 * 🌚 * 🌝 * 🙈 * 💊 * 😶 * 🙏 * 🍦 * 🍉 * 😣 * * * * * * * * * * * * * * * * * * * * * * 颜文字 * Emoji * Bilibili 预览 发送 2 条评论 小饿 2024-08-10 1 1 「合理设置Cloudflare R2数据访问频率」可以专门写个教程,感觉很有必要哇 L,.G. 博主 2024-08-11 1 回复 @小饿 : 其实就是对于常用的文件不要设置成「不频繁读取」,否则账单会很高。 试试 2024-08-09 1 1 试试upic,好看又好用… L,.G. 博主 2024-08-09 1 回复 @试试 : 我一直再用 uPic 😀 Powered by Twikoo v1.6.39 Twikoo 评论管理 密码 登录 -------------------------------------------------------------------------------- Send an email to 槿呈Goidea 槿呈Goidea on Twitter 槿呈Goidea on Telegram CC BY-NC-SA 4.0 DEED | 2015 - 2024