fix service worker auto reload
This commit is contained in:
parent
cbab0edec2
commit
f8071c722a
|
|
@ -6,8 +6,9 @@ export default page({
|
||||||
url: "**",
|
url: "**",
|
||||||
component: ({}) => {
|
component: ({}) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (localStorage.getItem("prasi-session")) {
|
if (localStorage.getItem("prasi-session")) {
|
||||||
navigate("/editor");
|
navigate("/editor/");
|
||||||
} else {
|
} else {
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,10 +135,13 @@ export default page({
|
||||||
const Editor = local.Editor;
|
const Editor = local.Editor;
|
||||||
if (local.loading || !Editor) return <Loading note="base-page" />;
|
if (local.loading || !Editor) return <Loading note="base-page" />;
|
||||||
|
|
||||||
navigator.serviceWorker.controller?.postMessage({
|
const sw = navigator.serviceWorker.controller;
|
||||||
|
if (sw) {
|
||||||
|
sw.postMessage({
|
||||||
type: "add-cache",
|
type: "add-cache",
|
||||||
url: location.href,
|
url: location.href,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Editor session={local.session} site_id={site_id} page_id={page_id} />
|
<Editor session={local.session} site_id={site_id} page_id={page_id} />
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import { createAPI, createDB, reloadDBAPI } from "./utils/script/init-api";
|
||||||
import { w } from "./utils/types/general";
|
import { w } from "./utils/types/general";
|
||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
registerServiceWorker();
|
const sw = await registerServiceWorker();
|
||||||
|
|
||||||
defineReact();
|
defineReact();
|
||||||
await defineWindow(false);
|
await defineWindow(false);
|
||||||
const base = `${location.protocol}//${location.host}`;
|
const base = `${location.protocol}//${location.host}`;
|
||||||
|
|
@ -15,6 +16,37 @@ const start = async () => {
|
||||||
w.api = createAPI(base);
|
w.api = createAPI(base);
|
||||||
w.db = createDB(base);
|
w.db = createDB(base);
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener("message", (e) => {
|
||||||
|
if (e.data.type === "activated") {
|
||||||
|
if (e.data.shouldRefresh && sw) {
|
||||||
|
sw.unregister().then(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.data.type === "ready") {
|
||||||
|
const sw = navigator.serviceWorker.controller;
|
||||||
|
|
||||||
|
if (sw) {
|
||||||
|
const routes = Object.entries(w.prasiApi[base].apiEntry).map(
|
||||||
|
([k, v]: any) => ({
|
||||||
|
url: v.url,
|
||||||
|
name: k,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
sw.postMessage({
|
||||||
|
type: "add-cache",
|
||||||
|
url: location.href,
|
||||||
|
});
|
||||||
|
sw.postMessage({
|
||||||
|
type: "define-route",
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const el = document.getElementById("root");
|
const el = document.getElementById("root");
|
||||||
if (el) {
|
if (el) {
|
||||||
createRoot(el).render(<Root />);
|
createRoot(el).render(<Root />);
|
||||||
|
|
@ -24,7 +56,7 @@ const start = async () => {
|
||||||
const registerServiceWorker = async () => {
|
const registerServiceWorker = async () => {
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
try {
|
try {
|
||||||
await navigator.serviceWorker.register(
|
return await navigator.serviceWorker.register(
|
||||||
new URL("./sworker.ts", import.meta.url),
|
new URL("./sworker.ts", import.meta.url),
|
||||||
{
|
{
|
||||||
type: "module",
|
type: "module",
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export const InternalAPI: FC<{
|
||||||
const reloadStatus = () => {
|
const reloadStatus = () => {
|
||||||
if (p.site) {
|
if (p.site) {
|
||||||
const s = api.srvapi_check.bind({ apiUrl: "https://api.prasi.app" });
|
const s = api.srvapi_check.bind({ apiUrl: "https://api.prasi.app" });
|
||||||
s(p.site.id).then((e) => {
|
s(p.site.id).then((e: any) => {
|
||||||
local.status = e;
|
local.status = e;
|
||||||
checkApi(e === "started");
|
checkApi(e === "started");
|
||||||
local.render();
|
local.render();
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,38 @@
|
||||||
import { manifest, version } from "@parcel/service-worker";
|
import { manifest, version } from "@parcel/service-worker";
|
||||||
|
import { RadixRouter, createRouter } from "radix3";
|
||||||
const g = {
|
const g = {
|
||||||
cache: null as null | Cache,
|
router: null as null | RadixRouter<any>,
|
||||||
dev: false,
|
broadcast(msg: any) {
|
||||||
baseUrl: "",
|
// @ts-ignore
|
||||||
|
const c: Clients = self.clients;
|
||||||
|
c.matchAll({ includeUncontrolled: true }).then((clients) => {
|
||||||
|
clients.forEach((client) => {
|
||||||
|
client.postMessage(msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
const cache = await caches.open(version);
|
const cache = await caches.open(version);
|
||||||
g.cache = cache;
|
|
||||||
await cache.addAll(manifest);
|
await cache.addAll(manifest);
|
||||||
|
g.broadcast({ type: "installed" });
|
||||||
}
|
}
|
||||||
addEventListener("install", (e) => (e as ExtendableEvent).waitUntil(install()));
|
addEventListener("install", (e) => (e as ExtendableEvent).waitUntil(install()));
|
||||||
|
|
||||||
async function activate() {
|
async function activate() {
|
||||||
|
let shouldRefresh = false;
|
||||||
const keys = await caches.keys();
|
const keys = await caches.keys();
|
||||||
await Promise.all(keys.map((key) => key !== version && caches.delete(key)));
|
await Promise.all(
|
||||||
|
keys.map(async (key) => {
|
||||||
|
if (key !== version) {
|
||||||
|
await caches.delete(key);
|
||||||
|
shouldRefresh = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
g.broadcast({ type: "activated", shouldRefresh });
|
||||||
}
|
}
|
||||||
addEventListener("activate", (e) =>
|
addEventListener("activate", (e) =>
|
||||||
(e as ExtendableEvent).waitUntil(activate())
|
(e as ExtendableEvent).waitUntil(activate())
|
||||||
|
|
@ -24,44 +41,29 @@ addEventListener("activate", (e) =>
|
||||||
addEventListener("fetch", async (evt) => {
|
addEventListener("fetch", async (evt) => {
|
||||||
const e = evt as FetchEvent;
|
const e = evt as FetchEvent;
|
||||||
|
|
||||||
if (g.baseUrl) {
|
const url = new URL(e.request.url);
|
||||||
const url = e.request.url;
|
|
||||||
if (url.startsWith(g.baseUrl)) {
|
if (g.router) {
|
||||||
|
const found = g.router.lookup(url.pathname);
|
||||||
|
if (found) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.respondWith(
|
e.respondWith(
|
||||||
(async () => {
|
(async () => {
|
||||||
if (!g.cache) {
|
const r = await caches.match(e.request);
|
||||||
g.cache = await caches.open(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!g.baseUrl) {
|
|
||||||
const keys = await g.cache.keys();
|
|
||||||
const url = new URL(keys[0].url);
|
|
||||||
url.pathname = "";
|
|
||||||
g.baseUrl = url.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const cache = g.cache;
|
|
||||||
|
|
||||||
const r = await cache.match(e.request);
|
|
||||||
if (r) {
|
if (r) {
|
||||||
cache.add(e.request);
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
return await fetch(e.request.url);
|
return fetch(e.request);
|
||||||
})()
|
})()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
g.broadcast({ type: "ready" });
|
||||||
addEventListener("message", async (e) => {
|
addEventListener("message", async (e) => {
|
||||||
const type = e.data.type;
|
const type = e.data.type;
|
||||||
|
const cache = await caches.open(version);
|
||||||
if (!g.cache) {
|
|
||||||
g.cache = await caches.open(version);
|
|
||||||
}
|
|
||||||
const cache = g.cache;
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "add-cache":
|
case "add-cache":
|
||||||
|
|
@ -69,5 +71,13 @@ addEventListener("message", async (e) => {
|
||||||
await cache.add(e.data.url);
|
await cache.add(e.data.url);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "define-route":
|
||||||
|
console.log("defining route", e.data.routes);
|
||||||
|
g.router = createRouter({ strictTrailingSlash: false });
|
||||||
|
for (const route of e.data.routes) {
|
||||||
|
g.router.insert(route.url, route);
|
||||||
|
}
|
||||||
|
await activate();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -130,16 +130,27 @@ export const reloadDBAPI = async (
|
||||||
|
|
||||||
await set(url, JSON.stringify(w.prasiApi[url]), cache);
|
await set(url, JSON.stringify(w.prasiApi[url]), cache);
|
||||||
};
|
};
|
||||||
|
const prasiBase = `${location.protocol}//${location.host}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const found = await get(url, cache);
|
const found = await get(url, cache);
|
||||||
if (found) {
|
if (found) {
|
||||||
w.prasiApi[url] = JSON.parse(found);
|
w.prasiApi[url] = JSON.parse(found);
|
||||||
forceReload();
|
forceReload().catch(() => {
|
||||||
|
if (url === prasiBase) {
|
||||||
|
console.error("Failed to load prasi. Reloading...");
|
||||||
|
setTimeout(() => location.reload(), 3000);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await forceReload();
|
await forceReload();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Failed to load API");
|
console.warn("Failed to load API");
|
||||||
|
|
||||||
|
if (url === prasiBase) {
|
||||||
|
console.error("Failed to load prasi. Reloading...");
|
||||||
|
setTimeout(() => location.reload(), 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import { prepareApiRoutes } from "./server/api-scan";
|
||||||
g.status = "init";
|
g.status = "init";
|
||||||
|
|
||||||
await createLogger();
|
await createLogger();
|
||||||
|
g.api = {};
|
||||||
g.datadir = g.mode === "dev" ? ".data" : "../data";
|
g.datadir = g.mode === "dev" ? ".data" : "../data";
|
||||||
g.port = parseInt(process.env.PORT || "4550");
|
g.port = parseInt(process.env.PORT || "4550");
|
||||||
g.mode = process.argv.includes("dev") ? "dev" : "prod";
|
g.mode = process.argv.includes("dev") ? "dev" : "prod";
|
||||||
|
|
@ -31,7 +32,7 @@ if (g.db) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await createServer();
|
createServer();
|
||||||
await parcelBuild();
|
await parcelBuild();
|
||||||
await generateAPIFrm();
|
await generateAPIFrm();
|
||||||
await prepareApiRoutes();
|
await prepareApiRoutes();
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ export const prepareApiRoutes = async () => {
|
||||||
path: importPath.substring((root || path).length + 1),
|
path: importPath.substring((root || path).length + 1),
|
||||||
};
|
};
|
||||||
g.api[filename] = route;
|
g.api[filename] = route;
|
||||||
g.router.insert(route.url.replace(/\*/gi, "**"), g.api[filename]);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
g.log.warn(
|
g.log.warn(
|
||||||
`Failed to import app/srv/api${importPath.substring(
|
`Failed to import app/srv/api${importPath.substring(
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,25 @@ import { dir } from "../utils/dir";
|
||||||
import { g } from "../utils/global";
|
import { g } from "../utils/global";
|
||||||
import { serveAPI } from "./serve-api";
|
import { serveAPI } from "./serve-api";
|
||||||
import { WebSocketHandler } from "bun";
|
import { WebSocketHandler } from "bun";
|
||||||
|
import { waitUntil } from "web-utils/src/wait-until";
|
||||||
|
|
||||||
const cache = { static: {} as Record<string, any> };
|
const cache = {
|
||||||
|
static: {} as Record<
|
||||||
|
string,
|
||||||
|
{ type: string; content: ReadableStream<Uint8Array> }
|
||||||
|
>,
|
||||||
|
};
|
||||||
|
|
||||||
export type WSData = { url: URL };
|
export type WSData = { url: URL };
|
||||||
|
|
||||||
export const createServer = async () => {
|
export const createServer = async () => {
|
||||||
g.api = {};
|
await waitUntil(() => g.status !== "init");
|
||||||
g.router = createRouter({ strictTrailingSlash: false });
|
g.router = createRouter({ strictTrailingSlash: false });
|
||||||
|
|
||||||
|
for (const route of Object.values(g.api)) {
|
||||||
|
g.router.insert(route.url.replace(/\*/gi, "**"), route);
|
||||||
|
}
|
||||||
|
|
||||||
g.server = Bun.serve({
|
g.server = Bun.serve({
|
||||||
port: g.port,
|
port: g.port,
|
||||||
websocket: {
|
websocket: {
|
||||||
|
|
@ -45,7 +56,6 @@ export const createServer = async () => {
|
||||||
},
|
},
|
||||||
} as WebSocketHandler<WSData>,
|
} as WebSocketHandler<WSData>,
|
||||||
async fetch(req, server) {
|
async fetch(req, server) {
|
||||||
if (g.status === "init") return new Response("initializing...");
|
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
|
|
||||||
if (wsHandler[url.pathname]) {
|
if (wsHandler[url.pathname]) {
|
||||||
|
|
@ -71,14 +81,20 @@ export const createServer = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (cache.static[url.pathname]) {
|
const found = cache.static[url.pathname];
|
||||||
return new Response(cache.static[url.pathname]);
|
if (found || g.mode === "prod") {
|
||||||
|
const res = new Response(found.content);
|
||||||
|
res.headers.set("Content-Type", found.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = Bun.file(dir.path(`app/static${url.pathname}`));
|
const file = Bun.file(dir.path(`app/static${url.pathname}`));
|
||||||
if ((await file.exists()) && file.type !== "application/octet-stream") {
|
if ((await file.exists()) && file.type !== "application/octet-stream") {
|
||||||
cache.static[url.pathname] = file;
|
cache.static[url.pathname] = {
|
||||||
return new Response(file as any);
|
type: file.type,
|
||||||
|
content: file.stream(),
|
||||||
|
};
|
||||||
|
const found = cache.static[url.pathname];
|
||||||
|
return new Response(found.content);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
g.log.error(e);
|
g.log.error(e);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue