mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-06-06 05:38:44 +08:00
add sw
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"importMap": "./import_map.json",
|
||||
"tasks": {
|
||||
"cache": "deno cache main.ts server-dev.ts",
|
||||
"server-dev": "deno run -A --unstable --watch=static/,routes/,translation/ server-dev.ts",
|
||||
"server-dev": "deno run -A --unstable \"--watch=static/*.css,static/*.ts,static/*/,routes/,translation/\" server-dev.ts",
|
||||
"test": "deno test --allow-read=./ --allow-net --allow-write=./ --allow-run=tasklist.exe --unstable",
|
||||
"run": "deno run --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net --unstable",
|
||||
"compile": "deno compile --allow-read=./ --allow-write=./ --allow-run=tasklist.exe --allow-env=DENO_DEPLOYMENT_ID --allow-net --unstable",
|
||||
@@ -11,7 +11,7 @@
|
||||
},
|
||||
"fmt": {
|
||||
"indentWidth": 4,
|
||||
"exclude": ["config.json"]
|
||||
"exclude": ["config.json", "static/sw.js", "static/sw.meta.json"]
|
||||
},
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
|
||||
12
deno.lock
generated
12
deno.lock
generated
@@ -121,6 +121,7 @@
|
||||
"https://deno.land/x/[email protected]/mod.js": "4f4e61964a551d9c0baf5bb19e973cf631cf8c66ddaf01e70070f8a100fc938c",
|
||||
"https://deno.land/x/[email protected]/wasm.d.ts": "dc279a3a46f084484453e617c0cabcd5b8bd1920c0e562e4ea02dfc828c8f968",
|
||||
"https://deno.land/x/[email protected]/wasm.js": "4030e7b50941ec6e06704c6b5f1f6416cc0f7f35f63daf63f184b728bea79a30",
|
||||
"https://deno.land/x/[email protected]/mod.js": "ef6f332d1d96e5aee3eb12e9e9eccc64d9df4b500fbb1346bac6b8263fafb6c7",
|
||||
"https://deno.land/x/[email protected]/dev.ts": "a66c7d64be35bcd6a8e12eec9c27ae335044c70363a241f2e36ee776db468622",
|
||||
"https://deno.land/x/[email protected]/plugins/twind.ts": "c0570d6010e29ba24ee5f43b9d3f1fe735f7fac76d9a3e680c9896373d669876",
|
||||
"https://deno.land/x/[email protected]/plugins/twind/shared.ts": "023e0ffcd66668753b5049edab0de46e6d66194fb6026c679034b9bbf04ad6f3",
|
||||
@@ -198,6 +199,7 @@
|
||||
"https://esm.sh/*[email protected]": "88ec8d8706b6a3f1e0fdad3862a2690dcd9b350d87bdc8e7bd0e27fbc0f7d29e",
|
||||
"https://esm.sh/[email protected]/": "16d82ee0a75451f75b42d9a20db4da0ccae7ccc8cc09a41c73b4488aba010b94",
|
||||
"https://esm.sh/gh/SortableJS/[email protected]/Sortable.min.js": "0a3e3cf471bf4566d7a3f9823b1d0d3b2bd075404e93419cd4074bd23310a9b0",
|
||||
"https://esm.sh/[email protected]": "07fb48e0e08189de5f370ab8a1fa9d4dfd1cdb31a1843fba7e6f30d1c28575cd",
|
||||
"https://esm.sh/[email protected]/Button": "bc60923d511c6e2e33a7064339b3e643a9c15e3ef232ab063ef570af2ef83dc8",
|
||||
"https://esm.sh/[email protected]/Checkbox": "bf34f5cd8c6d015916d854d91aab2caf115463e97be9a461f8dd3370ea11a49c",
|
||||
"https://esm.sh/[email protected]/Dialog": "b0ff8da9c770456748f7e065fecda2fc90f5364ea66cae75ff5f51d57f6a87eb",
|
||||
@@ -320,7 +322,17 @@
|
||||
"https://esm.sh/v124/[email protected]/deno/twind.mjs": "ac4bf729653ee66349a518ee4949f22e84b7f626e8f74de1066e798a7c1ca12a",
|
||||
"https://esm.sh/v124/[email protected]/sheets/sheets.d.ts": "9cd4663d180023e49d9379777c8926347f3f79bf3bee546e7e5be1524fe55e70",
|
||||
"https://esm.sh/v124/[email protected]/twind.d.ts": "48c49da7d770f1236ec8a9397af053e6fb5a2bedacf431f2189eeecc1468c01b",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/binary.mjs": "3dba03d945d82efa1771daa39193e920171562b0c64868c505bd3e68c14f79f8",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/constant-time.mjs": "5a3711ba33bc29b816dd413aad3378f765b656196252f34622277eb955e7c223",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/hash.mjs": "887c7f363d7d9bb5978727f12d6344aabd79d5fc7523f1943271e2e5403d944b",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/lib/hash.d.ts": "6562fc4b9b470ee474add192278b9ad7d863b1096f57c7aeb26e6b15a86e51a7",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/hmac.mjs": "9901cc0f5e96d86a039f964e122d6f130c0a722a86bf6287a1ebbb420f81e964",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/int.mjs": "179398595f1fcf9e85c3251583cf23031bf1b5b2040d7ad731de19cdb1e61adc",
|
||||
"https://esm.sh/v125/@stablelib/[email protected]/denonext/wipe.mjs": "116b9cbca9c97e0a997d7cc3ebd9e60883d99eaa2345d48cafeb783b44808123",
|
||||
"https://esm.sh/v125/[email protected]/denonext/array-buffer-to-hex.mjs": "e9c4132a6dc310f33ffc362e0becd7ab12d209962f7028ad1b9b58439cd9b35e",
|
||||
"https://esm.sh/v125/gh/SortableJS/[email protected]/denonext/Sortable.min.js": "aeba191bb6622c4ad41ae5d8f5d4dd6bf15074f264cb3640ed223fbaf052263b",
|
||||
"https://esm.sh/v125/[email protected]/denonext/lifegpc-md5.mjs": "332f72d899b7fa2a74c4a1a4f00616fa5543bcfd9a182314b9408501c3b0bb10",
|
||||
"https://esm.sh/v125/[email protected]/lib/md5.d.ts": "82fd2b0c148e013ade0b46149953270220d0fd61835f2d12a95832fa2e0dcfc0",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/deps.ts": "b7248e5b750be62613a9417f407e65ed43726d83b11f9631d6dbb58634bbd7d1",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/mod.ts": "3e507379372361162f93325a216b86f6098defb5bb60144555b507bca26d061f",
|
||||
"https://raw.githubusercontent.com/lucacasonato/esbuild_deno_loader/8031f71afa1bbcd3237a94b11f53a2e5c5c0e7bf/src/deno.ts": "71bee6b14e72ca193c0686d8b4f1f47d639a64745b6f5c7576f7a3616f436f57",
|
||||
|
||||
24
fresh.gen.ts
24
fresh.gen.ts
@@ -3,22 +3,26 @@
|
||||
// This file is automatically updated during development when running `dev.ts`.
|
||||
|
||||
import config from "./deno.json" assert { type: "json" };
|
||||
import * as $0 from "./routes/api/config.ts";
|
||||
import * as $1 from "./routes/api/exit.ts";
|
||||
import * as $2 from "./routes/api/export/gallery/zip/[gid].ts";
|
||||
import * as $3 from "./routes/api/task.ts";
|
||||
import * as $4 from "./routes/index.tsx";
|
||||
import * as $0 from "./routes/_middleware.ts";
|
||||
import * as $1 from "./routes/api/config.ts";
|
||||
import * as $2 from "./routes/api/deploy_id.ts";
|
||||
import * as $3 from "./routes/api/exit.ts";
|
||||
import * as $4 from "./routes/api/export/gallery/zip/[gid].ts";
|
||||
import * as $5 from "./routes/api/task.ts";
|
||||
import * as $6 from "./routes/index.tsx";
|
||||
import * as $$0 from "./islands/Container.tsx";
|
||||
import * as $$1 from "./islands/Settings.tsx";
|
||||
import * as $$2 from "./islands/TaskManager.tsx";
|
||||
|
||||
const manifest = {
|
||||
routes: {
|
||||
"./routes/api/config.ts": $0,
|
||||
"./routes/api/exit.ts": $1,
|
||||
"./routes/api/export/gallery/zip/[gid].ts": $2,
|
||||
"./routes/api/task.ts": $3,
|
||||
"./routes/index.tsx": $4,
|
||||
"./routes/_middleware.ts": $0,
|
||||
"./routes/api/config.ts": $1,
|
||||
"./routes/api/deploy_id.ts": $2,
|
||||
"./routes/api/exit.ts": $3,
|
||||
"./routes/api/export/gallery/zip/[gid].ts": $4,
|
||||
"./routes/api/task.ts": $5,
|
||||
"./routes/index.tsx": $6,
|
||||
},
|
||||
islands: {
|
||||
"./islands/Container.tsx": $$0,
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
"$std/": "https://deno.land/[email protected]/",
|
||||
"preact-material-components/": "https://esm.sh/[email protected]/",
|
||||
"accept-language-parser/": "https://esm.sh/[email protected]/",
|
||||
"sortable": "https://esm.sh/gh/SortableJS/[email protected]/Sortable.min.js"
|
||||
"sortable": "https://esm.sh/gh/SortableJS/[email protected]/Sortable.min.js",
|
||||
"esbuild/": "https://deno.land/x/[email protected]/",
|
||||
"lifegpc-md5": "https://esm.sh/[email protected]"
|
||||
}
|
||||
}
|
||||
|
||||
53
routes/_middleware.ts
Normal file
53
routes/_middleware.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { MiddlewareHandlerContext } from "$fresh/server.ts";
|
||||
import { build } from "esbuild/mod.js";
|
||||
import { join, resolve } from "std/path/mod.ts";
|
||||
import { asyncForEach, calFileMd5, checkMapFile } from "../utils.ts";
|
||||
|
||||
export async function handler(req: Request, ctx: MiddlewareHandlerContext) {
|
||||
const url = new URL(req.url);
|
||||
if (url.pathname == "/sw.js") {
|
||||
const base = import.meta.resolve("../static").slice(8);
|
||||
const map_file = join(base, "sw.meta.json");
|
||||
if (!(await checkMapFile(map_file))) {
|
||||
const data = await build({
|
||||
entryPoints: [join(base, "sw.ts")],
|
||||
outfile: join(base, "sw.js"),
|
||||
metafile: true,
|
||||
});
|
||||
const map = data.metafile;
|
||||
await asyncForEach(
|
||||
Object.getOwnPropertyNames(map.inputs),
|
||||
async (k) => {
|
||||
const p = resolve(k);
|
||||
if (p !== k) {
|
||||
map.inputs[p] = map.inputs[k];
|
||||
delete map.inputs[k];
|
||||
k = p;
|
||||
}
|
||||
const data = map.inputs[k];
|
||||
data.md5 = await calFileMd5(k);
|
||||
},
|
||||
);
|
||||
await asyncForEach(
|
||||
Object.getOwnPropertyNames(map.outputs),
|
||||
async (k) => {
|
||||
const p = resolve(k);
|
||||
if (p !== k) {
|
||||
map.outputs[p] = map.outputs[k];
|
||||
delete map.outputs[k];
|
||||
k = p;
|
||||
}
|
||||
const data = map.outputs[k];
|
||||
data.md5 = await calFileMd5(k);
|
||||
},
|
||||
);
|
||||
await Deno.writeTextFile(map_file, JSON.stringify(map));
|
||||
console.log("Rebuild.");
|
||||
}
|
||||
}
|
||||
const res = await ctx.next();
|
||||
if (url.pathname == "/sw.js") {
|
||||
res.headers.delete("etag");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
10
routes/api/deploy_id.ts
Normal file
10
routes/api/deploy_id.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
|
||||
export const handler: Handlers = {
|
||||
GET(_req, _ctx) {
|
||||
const data = { id: Deno.env.get("DENO_DEPLOYMENT_ID") };
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
},
|
||||
};
|
||||
2
static/.gitignore
vendored
2
static/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
preact-material-components/
|
||||
sw.js
|
||||
sw.meta.json
|
||||
|
||||
57
static/sw.ts
Normal file
57
static/sw.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import {
|
||||
ActivateEvent,
|
||||
} from "https://gist.githubusercontent.com/ithinkihaveacat/227bfe8aa81328c5d64ec48f4e4df8e5/raw/f69f0783e69f5827b20dbe3f3509ddbf73933768/service-worker.d.ts";
|
||||
|
||||
async function get_deploy_id(): Promise<string | undefined> {
|
||||
const re = await (await fetch("/api/deploy_id")).json();
|
||||
return re.id;
|
||||
}
|
||||
|
||||
let deploy_id: string | undefined = undefined;
|
||||
|
||||
const deleteCache = async (key: string) => {
|
||||
await caches.delete(key);
|
||||
};
|
||||
|
||||
const deleteOldCaches = async () => {
|
||||
deploy_id = await get_deploy_id();
|
||||
const keyList = await caches.keys();
|
||||
const cachesToDelete = keyList.filter((key) => key !== deploy_id);
|
||||
await Promise.all(cachesToDelete.map(deleteCache));
|
||||
};
|
||||
|
||||
/**@ts-ignore */
|
||||
self.addEventListener("activate", (event: ActivateEvent) => {
|
||||
event.waitUntil(deleteOldCaches());
|
||||
});
|
||||
|
||||
const CACHES = ["/common.css", "/preact-material-components/style.css"];
|
||||
|
||||
function match_url(u: URL) {
|
||||
const pn = u.pathname;
|
||||
const ori = u.origin;
|
||||
if (ori == self.location.origin) {
|
||||
if (CACHES.includes(pn)) return true;
|
||||
if (pn.startsWith("/_frsh/")) return true;
|
||||
}
|
||||
if (ori === "https://fonts.gstatic.com") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**@ts-ignore */
|
||||
self.addEventListener("fetch", async (e: FetchEvent) => {
|
||||
const r = e.request;
|
||||
const responseFromCache = await caches.match(r);
|
||||
if (responseFromCache) {
|
||||
return responseFromCache;
|
||||
}
|
||||
const res = await fetch(r);
|
||||
if (res.ok) {
|
||||
const url = new URL(r.url);
|
||||
if (deploy_id && match_url(url)) {
|
||||
const cache = await caches.open(deploy_id);
|
||||
await cache.put(r, res.clone());
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
67
utils.ts
67
utils.ts
@@ -2,6 +2,7 @@ import { exists, existsSync } from "std/fs/exists.ts";
|
||||
import { extname } from "std/path/mod.ts";
|
||||
import { initParser } from "deno_dom/deno-dom-wasm-noinit.ts";
|
||||
import { configure } from "zipjs/index.js";
|
||||
import { MD5 } from "lifegpc-md5";
|
||||
|
||||
export function sleep(time: number): Promise<undefined> {
|
||||
return new Promise((r) => {
|
||||
@@ -164,3 +165,69 @@ export function add_suffix_to_path(path: string, suffix?: string) {
|
||||
return `${path}-${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
export async function calFileMd5(p: string | URL) {
|
||||
const h = new MD5();
|
||||
const f = await Deno.open(p);
|
||||
try {
|
||||
const buf = new Uint8Array(4096);
|
||||
let readed: number | null = null;
|
||||
do {
|
||||
readed = await f.read(buf);
|
||||
if (readed) {
|
||||
h.update(buf.slice(0, readed));
|
||||
}
|
||||
} while (readed !== null);
|
||||
return h.digest_hex();
|
||||
} finally {
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
export async function checkMapFile(p: string | URL, signal?: AbortSignal) {
|
||||
if (!(await exists(p))) return false;
|
||||
const map = JSON.parse(await Deno.readTextFile(p, { signal }));
|
||||
if (
|
||||
!(await asyncEvery(
|
||||
Object.getOwnPropertyNames(map.inputs),
|
||||
async (k) => {
|
||||
const data = map.inputs[k];
|
||||
const md5 = data.md5;
|
||||
if (!md5) return false;
|
||||
if (!(await exists(k))) return false;
|
||||
const m = await calFileMd5(k);
|
||||
return md5 === m;
|
||||
},
|
||||
))
|
||||
) return false;
|
||||
if (
|
||||
!(await asyncEvery(
|
||||
Object.getOwnPropertyNames(map.outputs),
|
||||
async (k) => {
|
||||
const data = map.outputs[k];
|
||||
const md5 = data.md5;
|
||||
if (!md5) return false;
|
||||
if (!(await exists(k))) return false;
|
||||
const m = await calFileMd5(k);
|
||||
return md5 === m;
|
||||
},
|
||||
))
|
||||
) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function asyncEvery<T, V>(
|
||||
arr: ArrayLike<T>,
|
||||
callback: (
|
||||
this: V | undefined,
|
||||
element: T,
|
||||
index: number,
|
||||
array: ArrayLike<T>,
|
||||
) => Promise<boolean>,
|
||||
thisArg?: V,
|
||||
) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (!await callback.apply(thisArg, [arr[i], i, arr])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@ import { assert, assertEquals } from "std/testing/asserts.ts";
|
||||
import { check_running } from "./pid_check.ts";
|
||||
import {
|
||||
add_suffix_to_path,
|
||||
asyncEvery,
|
||||
asyncFilter,
|
||||
asyncForEach,
|
||||
calFileMd5,
|
||||
filterFilename,
|
||||
promiseState,
|
||||
PromiseStatus,
|
||||
sleep,
|
||||
sure_dir,
|
||||
} from "./utils.ts";
|
||||
import { md5 } from "lifegpc-md5";
|
||||
|
||||
Deno.test("promiseState_test", async () => {
|
||||
const p1 = new Promise((res) => setTimeout(() => res(100), 100));
|
||||
@@ -79,3 +83,48 @@ Deno.test("add_suffix_to_path_test", () => {
|
||||
assert(t.startsWith("test-"));
|
||||
assert(t.endsWith(".ts"));
|
||||
});
|
||||
|
||||
Deno.test("calFileMd5_test", async () => {
|
||||
await sure_dir();
|
||||
const text = `Hello World.te${Math.random()}`;
|
||||
await Deno.writeTextFile("./test/test.txt", text);
|
||||
assertEquals(await calFileMd5("./test/test.txt"), md5(text));
|
||||
});
|
||||
|
||||
Deno.test("asyncEvery_test", async () => {
|
||||
const e = [new Promise<number>((res) => setTimeout(() => res(100), 100))];
|
||||
const e2 = [
|
||||
new Promise<number>((res) => setTimeout(() => res(100), 100)),
|
||||
new Promise<number>((res) => setTimeout(() => res(200), 100)),
|
||||
];
|
||||
const e3 = [
|
||||
new Promise<number>((res) => setTimeout(() => res(100), 100)),
|
||||
new Promise<number>((res) => setTimeout(() => res(200), 100)),
|
||||
new Promise<number>((res) => setTimeout(() => res(150), 100)),
|
||||
];
|
||||
const t = { test: 2 };
|
||||
assertEquals(
|
||||
await asyncEvery(e, async function (e) {
|
||||
assertEquals(this, t);
|
||||
return (await e) === 100;
|
||||
}, t),
|
||||
true,
|
||||
);
|
||||
assertEquals(
|
||||
await asyncEvery(e2, async function (e) {
|
||||
assertEquals(this, t);
|
||||
const d = await e;
|
||||
return d === 100;
|
||||
}, t),
|
||||
false,
|
||||
);
|
||||
assertEquals(
|
||||
await asyncEvery(e3, async function (e) {
|
||||
assertEquals(this, t);
|
||||
const d = await e;
|
||||
if (d === 150) throw Error("Should exited.");
|
||||
return d === 100;
|
||||
}, t),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user