From e5bc6190a3fd298b8306ec2e1487cfb2aa8daed8 Mon Sep 17 00:00:00 2001 From: KimlikDAO-bot Date: Thu, 2 Jan 2025 19:36:52 -0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A6=20Rework=20kastro=20crate=20deploy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kastro/cloudflare/crate.js | 122 +++++++++++++++++++++++++++++++++++ kastro/cloudflare/targets.js | 69 -------------------- kastro/kastro.js | 28 ++++---- 3 files changed, 133 insertions(+), 86 deletions(-) create mode 100644 kastro/cloudflare/crate.js delete mode 100644 kastro/cloudflare/targets.js diff --git a/kastro/cloudflare/crate.js b/kastro/cloudflare/crate.js new file mode 100644 index 0000000..474bd3a --- /dev/null +++ b/kastro/cloudflare/crate.js @@ -0,0 +1,122 @@ +import { CompressedMimes } from "../workers/mimes"; + +/** @const {string} */ +const CloudflareV4 = "https://api.cloudflare.com/client/v4"; + +/** @const {!Array} */ +const Extensions = ['', '.br', '.gz']; + +/** + * @typedef {{ + * accountId: string, + * token: string + * }} + */ +const Auth = {}; + +/** + * @param {!Array} assets + * @return {!Set} + */ +const getFileSet = (assets) => { + /** @const {!Array} */ + const files = []; + for (const asset of assets) { + const idx = asset.lastIndexOf('.'); + if (idx != -1 && CompressedMimes[asset.slice(idx + 1)]) + files.push(asset) + else + files.push(...Extensions.map((e) => asset + e)); + } + return new Set(files); +} + +/** + * Gets existing keys from CloudFlare KV. + * + * @return {!Promise>} + */ +export const getExisting = (auth, namespaceId) => + fetch(`${CloudflareV4}/accounts/${auth.accountId}/storage/kv/namespaces/${namespaceId}/keys`, { + headers: { 'Authorization': 'Bearer ' + auth.token } + }).then((res) => res.json()) + .then((data) => new Set(data.result.map(x => x.name))) + + +const uploadAssets = async (auth, namespaceId, namedAssets) => { + const existing = await getExisting(auth, namespaceId); + const namedFiles = getFileSet(namedAssets); + + /** @const {!Array} */ + const staticFiles = await readdir("build", { withFileTypes: true }) + .then((files) => files + .filter((file) => file.isFile() && !existing.has(file.name) && !namedFiles.has(file.name)) + .map((file) => file.name) + ); + + console.log("🌀 Uploading static files:", staticFiles); +} + +/** + * @param {Auth} auth + * @param {string} name + * @param {string} code + * @param {!Array<{ + * name: string, + * namespace_id: string + * }>=} kvBindings + * @return {!Promise<*>} + */ +const uploadWorker = (auth, name, code, kvBindings) => { + /** @const {string} */ + const yesterday = new Date(Date.now() - 86400000) + .toISOString().split('T')[0]; + /** @dict */ + const metadata = { + "main_module": "a.js", + "compatibility_date": yesterday + }; + if (kvBindings && kvBindings.length) { + for (const kv of kvBindings) + kv["type"] = "kv_namespace"; + metadata["bindings"] = kvBindings; + } + /** @const {!FormData} */ + const form = new FormData(); + form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" })); + form.append("a.js", new File([code], "a.js", { type: "application/javascript+module;charset=utf-8" })); + return fetch(`${CloudflareV4}/accounts/${auth.accountId}/workers/scripts/${name}`, { + method: "PUT", + headers: { "authorization": `Bearer ${auth.token}` }, + body: form + }).then((res) => res.json()); +}; + +/** + * @param {Auth} auth + * @param {string} name + * @param {string} url + */ +const bindWorker = (auth, name, url) => fetch(`${CloudflareV4}/accounts/${auth.accountId}/workers/domains`, { + method: "PUT", + headers: { + "authorization": `Bearer ${auth.token}`, + "content-type": "application/json" + }, + body: JSON.stringify({ + "environment": "production", + "hostname": url, + "service": name + }) +}).then((res) => res.json()); + +const deployCrate = (crateName) => import(crateName) + .then((crate) => { + + }) + +export { + Auth, + bindWorker, + uploadWorker +}; diff --git a/kastro/cloudflare/targets.js b/kastro/cloudflare/targets.js deleted file mode 100644 index 60b1c43..0000000 --- a/kastro/cloudflare/targets.js +++ /dev/null @@ -1,69 +0,0 @@ - -/** - * @typedef {{ - * accountId: string, - * token: string - * }} - */ -const Auth = {}; - -/** - * @param {Auth} auth - * @param {string} name - * @param {string} url - * @param {string} code - * @param {!Array<{ - * name: string, - * namespace_id: string - * }>=} kvBindings - * @return {!Promise<*>} - */ -const uploadWorker = (auth, name, code, kvBindings) => { - /** @const {string} */ - const yesterday = new Date(Date.now() - 86400000) - .toISOString().split('T')[0]; - /** @dict */ - const metadata = { - "main_module": "a.js", - "compatibility_date": yesterday - }; - if (kvBindings && kvBindings.length) { - for (const kv of kvBindings) - kv["type"] = "kv_namespace"; - metadata["bindings"] = kvBindings; - } - /** @const {!FormData} */ - const form = new FormData(); - form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" })); - form.append("a.js", new File([code], "a.js", { type: "application/javascript+module;charset=utf-8" })); - return fetch(`https://api.cloudflare.com/client/v4/accounts/${auth.accountId}/workers/scripts/${name}`, { - method: "PUT", - headers: { "authorization": `Bearer ${auth.token}` }, - body: form - }).then((res) => res.json()); -}; - -/** - * @param {Auth} auth - * @param {string} name - * @param {string} url - */ -const bindWorker = (auth, name, url) => fetch( - `https://api.cloudflare.com/client/v4/accounts/${auth.accountId}/workers/domains`, { - method: "PUT", - headers: { - "authorization": `Bearer ${auth.token}`, - "content-type": "application/json" - }, - body: JSON.stringify({ - "environment": "production", - "hostname": url, - "service": name - }) -}).then((res) => res.json()); - -export { - Auth, - bindWorker, - uploadWorker -}; diff --git a/kastro/kastro.js b/kastro/kastro.js index aa53fcc..97de8fb 100644 --- a/kastro/kastro.js +++ b/kastro/kastro.js @@ -176,25 +176,19 @@ const buildCrate = (crateName, buildMode) => import(crateName) console.info(`${Blue}[Building]${Clear} ${props.targetName}`); await compiler.bundleTarget(props.targetName, props); } - if (crate.Page) { - const targetName = "/" + combine(`build/${getDir(crateName)}`, "kvPageWorker.js"); - console.info(`${Blue}[Building]${Clear} ${targetName}`); - await compiler.bundleTarget(targetName, { - src: "lib/kastro/cloudflare/kvPageWorker.js", - globals: { - HOST_URL: crate.HOST_URL, - }, - strict: true, - }); - } return crate; }) -const deployCrate = (crateName) => buildCrate(crateName, compiler.BuildMode.Compiled) - .then((crate) => { - console.info(`${Green}[Deploying]${Clear} ${crate.HOST_URL}`); - console.log(compiler.getNamedAssets()); - }); +/** + * @param {string} crateName + * @param {string} target +*/ +const deployCrate = (crateName, target) => Promise.all([ + buildCrate(crateName, compiler.BuildMode.Compiled), + import(`${process.cwd()}/.secrets.js`), + import(`${target}/crate.js`) +]) + .then(([_, secrets, crates]) => crates.deployCrate(crateName, secrets, compiler.getNamedAssets())); setupKastro(); @@ -207,4 +201,4 @@ if (args.command == "serve") else if (args.command == "build") buildCrate(crateName, compiler.BuildMode.Compiled); else if (args.command == "deploy") - deployCrate(crateName); + deployCrate(crateName, args["target"] || "cloudflare");