mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-28 04:05:21 +08:00
feature (turboload): decrease load time via sw
This commit is contained in:
@ -134,9 +134,9 @@ async function ctrlNavigationPane(render, { $sidebar, nRestart }) {
|
||||
const $active = qs($sidebar, `[data-path="${chunk.toString()}"] a`);
|
||||
$active.classList.add("active");
|
||||
if (checkVisible($active) === false) {
|
||||
$active.offsetTop < window.innerHeight ?
|
||||
$sidebar.firstChild.scrollTo({top: 0, behavior: "smooth"}) :
|
||||
$active.scrollIntoView({ behavior: "smooth" });
|
||||
$active.offsetTop < window.innerHeight
|
||||
? $sidebar.firstChild.scrollTo({ top: 0, behavior: "smooth" })
|
||||
: $active.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
@import url("./designsystem_alert.css");
|
||||
|
||||
:root {
|
||||
--bg-color: #f9f9fa; /*#fafafa;*/
|
||||
--bg-color: #f9f9fa;
|
||||
--color: #57595A;
|
||||
--emphasis: #466372;
|
||||
--primary: #9AD1ED;
|
||||
|
||||
111
public/assets/sw.js
Normal file
111
public/assets/sw.js
Normal file
@ -0,0 +1,111 @@
|
||||
const VERSION = "v1";
|
||||
const CACHENAME = "assets";
|
||||
|
||||
/*
|
||||
* This Service Worker is an optional optimisation to load the app faster.
|
||||
* Whenever using raw es module without any build, we had a large number
|
||||
* of assets getting through the network. When we looked through the
|
||||
* developer console -> network, and look at the timing, 98% of the time
|
||||
* was spent "waiting for the server response".
|
||||
* HTTP2/3 should solve that issue but we don't control the proxy side of
|
||||
* things of how people install Filestash, hence the idea to bulk download
|
||||
* as much as we can through SSE, store it onto a cache and get our
|
||||
* service worker to inject the response.
|
||||
* This approach alone make the app a lot faster to load but relies on
|
||||
* the server being able to bundle our assets via SSE.
|
||||
*
|
||||
* TODO:
|
||||
* - wait until browser support DecompressionStream("brotli") natively
|
||||
* and use that. As of 2025, downloading a brotli decompress library
|
||||
* make the gain br / gz negative for our app
|
||||
* - wait until Firefox support SSE within service worker. As of 2025,
|
||||
* someone was implementing it in Firefox but it's not everywhere yet
|
||||
* Once that's done, we want to be 100% sure everything is working great
|
||||
*/
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
if (!self.EventSource) throw new Error("turboload not supported on this platform");
|
||||
|
||||
event.waitUntil((async() => {
|
||||
await self.skipWaiting();
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil((async() => {
|
||||
for (const name of await caches.keys()) await caches.delete(name);
|
||||
await self.clients.claim();
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
if (!event.request.url.startsWith(location.origin + "/assets/")) return;
|
||||
|
||||
event.respondWith((async() => {
|
||||
const cachedResponse = await caches.match(event.request);
|
||||
if (cachedResponse) return cachedResponse;
|
||||
return fetch(event.request);
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener("message", (event) => {
|
||||
if (event.data.type === "preload") handlePreloadMessage(
|
||||
event.data.payload,
|
||||
() => event.source.postMessage({ type: "preload", status: "ok" }),
|
||||
(err) => event.source.postMessage({ type: "preload", status: "error", msg: err.message }),
|
||||
);
|
||||
});
|
||||
|
||||
const handlePreloadMessage = (() => {
|
||||
const cleanup = [];
|
||||
return async(chunks, resolve, reject) => {
|
||||
cleanup.forEach((fn) => fn());
|
||||
try {
|
||||
caches.delete(CACHENAME);
|
||||
const cache = await caches.open(CACHENAME);
|
||||
await Promise.all(chunks.map((urls) => {
|
||||
return preload({ urls, cache, cleanup });
|
||||
}));
|
||||
resolve();
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
async function preload({ urls, cache, cleanup }) {
|
||||
const evtsrc = new self.EventSource("/assets/bundle?" + urls.map((url) => `url=${url}`).join("&"));
|
||||
cleanup.push(() => evtsrc.close());
|
||||
|
||||
let i = 0;
|
||||
const messageHandler = (resolve, event) => {
|
||||
const url = event.lastEventId;
|
||||
let mime = "application/octet-stream";
|
||||
if (url.endsWith(".css")) mime = "text/css";
|
||||
else if (url.endsWith(".js")) mime = "application/javascript";
|
||||
|
||||
i += 1;
|
||||
cache.put(
|
||||
location.origin + event.lastEventId,
|
||||
new Response(
|
||||
new Blob([Uint8Array.from(atob(event.data), (c) => c.charCodeAt(0))])
|
||||
.stream()
|
||||
.pipeThrough(new DecompressionStream("gzip")),
|
||||
{ headers: { "Content-Type": mime } },
|
||||
),
|
||||
);
|
||||
if (i === urls.length) {
|
||||
evtsrc.close();
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
const errorHandler = (reject, err) => {
|
||||
evtsrc.close();
|
||||
reject(err);
|
||||
};
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
evtsrc.onmessage = async(event) => messageHandler(resolve, event);
|
||||
evtsrc.onerror = (err) => errorHandler(reject, err);
|
||||
});
|
||||
}
|
||||
@ -4,219 +4,386 @@
|
||||
<base href="{{ .base }}">
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="application-name" content="Filestash">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
|
||||
<link rel="stylesheet" href="./assets/{{ .version }}/css/designsystem.css">
|
||||
<link rel="stylesheet" href="custom.css">
|
||||
{{ if eq .license "agpl" }}
|
||||
<script>
|
||||
class NyanCatLoader extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = this.render();
|
||||
this.timeout = setTimeout(function(){
|
||||
const $rbw = document.querySelector("#rbw .w");
|
||||
$rbw.innerHTML = $rbw.innerHTML.repeat(10);
|
||||
|
||||
const $loader = document.querySelector("#n-lder");
|
||||
$loader.classList.add("loading");
|
||||
}, 500);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
render() {
|
||||
return `
|
||||
<style>
|
||||
html{ overflow: hidden; }
|
||||
body{ background: #f2f3f5; color: #375160; }
|
||||
body.dark-mode{ background: #232426; }
|
||||
.background-color{ background: #f2f3f5; }
|
||||
body.dark-mode .background-color{ background: #232426; }
|
||||
noscript div{ text-align:center;font-family:monospace;margin-top:5%;font-size:15px; }
|
||||
</style>
|
||||
<style>
|
||||
#n-lder{ max-width: 100%; overflow: hidden; }
|
||||
#n-lder #cat{ position: absolute; top: calc(50% + 45px); left: 0%; margin-left: -250px; margin-top: -125px; width: 100%; height: 150px; }
|
||||
#n-lder.loading #cat{ left: 20%; left: calc(50% + 125px); transition: left 4s ease-out; }
|
||||
#n-lder.loading.done #cat { left: 100%; left: calc(100% + 250px); transition: left 0.5s linear; }
|
||||
#n-lder #cat svg{ height: 160px; width: 250px; position: absolute; }
|
||||
#n-lder #cat #hide-behind{ position: absolute; top: 0; left: 55px; bottom: 0; right: -250px; }
|
||||
|
||||
#n-lder #rbw{ position: absolute; top: calc(50% + 45px); left: 0; overflow: hidden; height: 145px; margin-top: -110px; width: 100%; }
|
||||
#n-lder #rbw .w{ width: 10000px; }
|
||||
#n-lder #rbw .rbw { z-index: -1; font-size: 16em; float: left; position: relative; }
|
||||
#n-lder #rbw .rbw .wv { height: 20px; width: 55px; }
|
||||
#n-lder #rbw .rbw .wv.wv-1 { background: #ff0000; }
|
||||
#n-lder #rbw .rbw .wv.wv-2 { background: #ff9900; }
|
||||
#n-lder #rbw .rbw .wv.wv-3 { background: #ffff00; }
|
||||
#n-lder #rbw .rbw .wv.wv-4 { background: #33ff00; }
|
||||
#n-lder #rbw .rbw .wv.wv-5 { background: #0099ff; }
|
||||
#n-lder #rbw .rbw .wv.wv-6 { background: #6633ff; }
|
||||
|
||||
#n-lder #rbw .rbw{ top: 0px; animation: rbw .6s linear infinite; }
|
||||
#n-lder #rbw .rbw.f1{ animation-delay: 0s; }
|
||||
#n-lder #rbw .rbw.f2{ animation-delay: 0.1s; }
|
||||
#n-lder #rbw .rbw.f3{ animation-delay: 0.2s; }
|
||||
#n-lder #rbw .rbw.f4{ animation-delay: 0.3s; }
|
||||
#n-lder #rbw .rbw.f5{ animation-delay: 0.4s; }
|
||||
#n-lder #rbw .rbw.f6{ animation-delay: 0.5s; }
|
||||
@keyframes rbw {
|
||||
0%{ top: 0px; }
|
||||
50%{ top: 15px; }
|
||||
100%{ top: 0px; }
|
||||
}
|
||||
@keyframes nyan_all {
|
||||
0%{ transform: translateY(0px); }
|
||||
33%{ transform: translateY(0px); }
|
||||
34%{ transform: translateY(1px); }
|
||||
100%{ transform: translateY(1px); }
|
||||
} #n-lder svg g#nyan_all{ animation: nyan_all 0.40s linear infinite; }
|
||||
@keyframes nyan_head {
|
||||
0%{ transform: translateX(0px) translateY(0px); }
|
||||
16%{ transform: translateX(0px) translateY(0px); }
|
||||
17%{ transform: translateX(1px) translateY(0px); }
|
||||
66%{ transform: translateX(1px) translateY(0px); }
|
||||
67%{ transform: translateX(0px) translateY(0px); }
|
||||
83%{ transform: translateX(0px) translateY(0px); }
|
||||
84%{ transform: translateX(0px) translateY(-1px); }
|
||||
100%{ transform: translateX(0px) translateY(-1px); }
|
||||
} #n-lder svg g#nyan_head{ animation: nyan_head 0.4s linear infinite; }
|
||||
@keyframes nyan_walk {
|
||||
0%{ transform: translateX(0px); }
|
||||
16%{ transform: translateX(0px); }
|
||||
17%{ transform: translateX(1px); }
|
||||
33%{ transform: translateX(1px); }
|
||||
34%{ transform: translateX(2px); }
|
||||
50%{ transform: translateX(2px); }
|
||||
51%{ transform: translateX(1px); }
|
||||
100%{ transform: translateX(0px); }
|
||||
} #n-lder svg g#nyan_feet{ animation: nyan_walk 0.5s linear infinite; }
|
||||
@keyframes nyan_tail {
|
||||
0%{ transform: rotate(0); }
|
||||
16%{ transform: rotate(0); }
|
||||
17%{ transform: rotate(-5deg); }
|
||||
33%{ transform: rotate(-5deg); }
|
||||
34%{ transform: rotate(-10deg); }
|
||||
49%{ transform: rotate(-10deg); }
|
||||
50%{ transform: rotate(-20deg); }
|
||||
66%{ transform: rotate(-20deg); }
|
||||
67%{ transform: rotate(-10deg); }
|
||||
83%{ transform: rotate(-10deg); }
|
||||
84%{ transform: rotate(-5deg); }
|
||||
99%{ transform: rotate(-5deg); }
|
||||
100%{ transform: rotate(0deg); }
|
||||
} #n-lder svg g#nyan_tail{ animation: nyan_tail 0.5s linear infinite; transform-origin: 4px 8px; }
|
||||
</style>
|
||||
<div id="n-lder">
|
||||
<div id="cat">
|
||||
<div id="hide-behind" class="background-color"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="32" height="21" preserveAspectRatio="xMinYMin meet" viewBox="0 0 33 21">
|
||||
<g id="nyan_all">
|
||||
<g id="nyan_feet">
|
||||
<g>
|
||||
<path d="m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 5,19 0,-2 3,0 0,1 -1,0 0,1 z" style="fill:#999999;"></path>
|
||||
<path d="m 10,20 0,-2 4,0 0,1 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 11,18 2,0 0,1 -2,0 z" style="fill:#999999;"></path>
|
||||
</g>
|
||||
<g transform="matrix(-1,0,0,1,32,0)">
|
||||
<path d="m 10,20 0,-2 4,0 0,1 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 11,18 2,0 0,1 -2,0 z" style="fill:#999999;"></path>
|
||||
<path d="m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 5,19 0,-2 3,0 0,1 -1,0 0,1 z" style="fill:#999999;"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="nyan_tail">
|
||||
<path d="M 0,10 0,7 4,7 4,8 5,8 5,9 6,9 6,14 5,14 5,13 3,13 3,12 2,12 2,11 1,11 1,10 z" style="fill:#000000;" />
|
||||
<path d="m 1,9 0,-1 2,0 0,1 1,0 0,1 1,0 0,1 1,0 0,1 -2,0 0,-1 -1,0 0,-1 -1,0 0,-1 z" style="fill:#999999;" />
|
||||
</g>
|
||||
<g id="nyan_body">
|
||||
<path d="m 7,1 19,0 0,16 -19,0 z" style="fill:#ffcc99;" />
|
||||
<path d="m 8,14 0,-10 1,0 0,-1 1,0 0,-1 13,0 0,1 1,0 0,1 1,0 0,10 -1,0 0,1 -1,0 0,1 -13,0 0,-1 -1,0 0,-1 z" style="fill:#ff99ff;" />
|
||||
<path d="m 22,5 1,0 0,1 -1,0 z m -4,-2 1,0 0,1 -1,0 z m -3,0 1,0 0,1 -1,0 z m -1,4 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m -2,3 1,0 0,1 -1,0 z m -2,-4 1,0 0,1 -1,0 z m -2,2 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m 0,-10 1,0 0,1 -1,0 z" style="fill:#ff3399;" />
|
||||
<path d="m 8,17 17,0 0,1 -17,0 z m 0,-17 17,0 0,1 -17,0 z m 18,16 0,-14 1,0 0,14 z m -20,0 0,-14 1,0 0,14 z m 1,0 1,0 0,1 -1,0 z m 0,-15 1,0 0,1 -1,0 z m 18,0 1,0 0,1 -1,0 z m 0,15 1,0 0,1 -1,0 z" style="fill:#000000;" />
|
||||
</g>
|
||||
<g id="nyan_head">
|
||||
<path d="m 17,15 0,-5 1,0 0,-4 2,0 0,1 1,0 0,1 1,0 0,1 4,0 0,-1 1,0 0,-1 1,0 0,-1 2,0 0,4 1,0 0,5 -1,0 0,1 -1,0 0,1 -10,0 0,-1 -1,0 0,-1 z" style="fill:#999999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 29,16 1,0 0,1 -1,0 z m 1,-1 1,0 0,1 -1,0 z m 1,-5 1,0 0,5 -1,0 z m -1,-4 1,0 0,4 -1,0 z m -2,-1 2,0 0,1 -2,0 z m -6,3 4,0 0,1 -4,0 z m -4,-3 2,0 0,1 -2,0 z m -1,1 1,0 0,4 -1,0 z m -1,4 1,0 0,5 -1,0 z m 11,-4 1,0 0,1 -1,0 z m -1,1 1,0 0,1 -1,0 z m -5,0 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z m -1,11 10,0 0,1 -10,0 z m -1,-1 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 18,13 2,0 0,2 -2,0 z" style="fill:#ff9999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 29,13 2,0 0,2 -2,0 z" style="fill:#ff9999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 21,16 0,-2 1,0 0,1 2,0 0,-1 1,0 0,1 2,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 25,12 1,0 0,1 -1,0 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<g>
|
||||
<path d="m 27,13 0,-1 1,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 27,11 1,0 0,1 -1,0 z" style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<path d="m 20,13 0,-1 1,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 20,11 1,0 0,1 -1,0 z" style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="rbw">
|
||||
<div class="w">
|
||||
<div class="rbw f1">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f2">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f3">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f4">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f5">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f6">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
customElements.define("component-nyan", NyanCatLoader);
|
||||
</script>
|
||||
{{ end }}
|
||||
<link rel="icon" href="favicon.ico">
|
||||
<title></title>
|
||||
<script>
|
||||
{{ if eq .license "agpl" }}
|
||||
class ComponentBootScreen extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = this.render();
|
||||
this.timeout = setTimeout(function(){
|
||||
const $rbw = document.querySelector("#rbw .w");
|
||||
$rbw.innerHTML = $rbw.innerHTML.repeat(10);
|
||||
|
||||
const $loader = document.querySelector("#n-lder");
|
||||
$loader.classList.add("loading");
|
||||
}, 500);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
render() {
|
||||
return `
|
||||
<style>
|
||||
html{ overflow: hidden; }
|
||||
body{ background: #f2f3f5; color: #375160; }
|
||||
body.dark-mode{ background: #232426; }
|
||||
.background-color{ background: #f2f3f5; }
|
||||
body.dark-mode .background-color{ background: #232426; }
|
||||
</style>
|
||||
<style>
|
||||
#n-lder{ max-width: 100%; overflow: hidden; }
|
||||
#n-lder #cat{ position: absolute; top: calc(50% + 45px); left: 0%; margin-left: -250px; margin-top: -125px; width: 100%; height: 150px; }
|
||||
#n-lder.loading #cat{ left: 20%; left: calc(50% + 125px); transition: left 4s ease-out; }
|
||||
#n-lder.loading.done #cat { left: 100%; left: calc(100% + 250px); transition: left 0.5s linear; }
|
||||
#n-lder #cat svg{ height: 160px; width: 250px; position: absolute; }
|
||||
#n-lder #cat #hide-behind{ position: absolute; top: 0; left: 55px; bottom: 0; right: -250px; }
|
||||
|
||||
#n-lder #rbw{ position: absolute; top: calc(50% + 45px); left: 0; overflow: hidden; height: 145px; margin-top: -110px; width: 100%; }
|
||||
#n-lder #rbw .w{ width: 10000px; }
|
||||
#n-lder #rbw .rbw { z-index: -1; font-size: 16em; float: left; position: relative; }
|
||||
#n-lder #rbw .rbw .wv { height: 20px; width: 55px; }
|
||||
#n-lder #rbw .rbw .wv.wv-1 { background: #ff0000; }
|
||||
#n-lder #rbw .rbw .wv.wv-2 { background: #ff9900; }
|
||||
#n-lder #rbw .rbw .wv.wv-3 { background: #ffff00; }
|
||||
#n-lder #rbw .rbw .wv.wv-4 { background: #33ff00; }
|
||||
#n-lder #rbw .rbw .wv.wv-5 { background: #0099ff; }
|
||||
#n-lder #rbw .rbw .wv.wv-6 { background: #6633ff; }
|
||||
|
||||
#n-lder #rbw .rbw{ top: 0px; animation: rbw .6s linear infinite; }
|
||||
#n-lder #rbw .rbw.f1{ animation-delay: 0s; }
|
||||
#n-lder #rbw .rbw.f2{ animation-delay: 0.1s; }
|
||||
#n-lder #rbw .rbw.f3{ animation-delay: 0.2s; }
|
||||
#n-lder #rbw .rbw.f4{ animation-delay: 0.3s; }
|
||||
#n-lder #rbw .rbw.f5{ animation-delay: 0.4s; }
|
||||
#n-lder #rbw .rbw.f6{ animation-delay: 0.5s; }
|
||||
@keyframes rbw {
|
||||
0%{ top: 0px; }
|
||||
50%{ top: 15px; }
|
||||
100%{ top: 0px; }
|
||||
}
|
||||
@keyframes nyan_all {
|
||||
0%{ transform: translateY(0px); }
|
||||
33%{ transform: translateY(0px); }
|
||||
34%{ transform: translateY(1px); }
|
||||
100%{ transform: translateY(1px); }
|
||||
} #n-lder svg g#nyan_all{ animation: nyan_all 0.40s linear infinite; }
|
||||
@keyframes nyan_head {
|
||||
0%{ transform: translateX(0px) translateY(0px); }
|
||||
16%{ transform: translateX(0px) translateY(0px); }
|
||||
17%{ transform: translateX(1px) translateY(0px); }
|
||||
66%{ transform: translateX(1px) translateY(0px); }
|
||||
67%{ transform: translateX(0px) translateY(0px); }
|
||||
83%{ transform: translateX(0px) translateY(0px); }
|
||||
84%{ transform: translateX(0px) translateY(-1px); }
|
||||
100%{ transform: translateX(0px) translateY(-1px); }
|
||||
} #n-lder svg g#nyan_head{ animation: nyan_head 0.4s linear infinite; }
|
||||
@keyframes nyan_walk {
|
||||
0%{ transform: translateX(0px); }
|
||||
16%{ transform: translateX(0px); }
|
||||
17%{ transform: translateX(1px); }
|
||||
33%{ transform: translateX(1px); }
|
||||
34%{ transform: translateX(2px); }
|
||||
50%{ transform: translateX(2px); }
|
||||
51%{ transform: translateX(1px); }
|
||||
100%{ transform: translateX(0px); }
|
||||
} #n-lder svg g#nyan_feet{ animation: nyan_walk 0.5s linear infinite; }
|
||||
@keyframes nyan_tail {
|
||||
0%{ transform: rotate(0); }
|
||||
16%{ transform: rotate(0); }
|
||||
17%{ transform: rotate(-5deg); }
|
||||
33%{ transform: rotate(-5deg); }
|
||||
34%{ transform: rotate(-10deg); }
|
||||
49%{ transform: rotate(-10deg); }
|
||||
50%{ transform: rotate(-20deg); }
|
||||
66%{ transform: rotate(-20deg); }
|
||||
67%{ transform: rotate(-10deg); }
|
||||
83%{ transform: rotate(-10deg); }
|
||||
84%{ transform: rotate(-5deg); }
|
||||
99%{ transform: rotate(-5deg); }
|
||||
100%{ transform: rotate(0deg); }
|
||||
} #n-lder svg g#nyan_tail{ animation: nyan_tail 0.5s linear infinite; transform-origin: 4px 8px; }
|
||||
</style>
|
||||
<div id="n-lder">
|
||||
<div id="cat">
|
||||
<div id="hide-behind" class="background-color"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="32" height="21" preserveAspectRatio="xMinYMin meet" viewBox="0 0 33 21">
|
||||
<g id="nyan_all">
|
||||
<g id="nyan_feet">
|
||||
<g>
|
||||
<path d="m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 5,19 0,-2 3,0 0,1 -1,0 0,1 z" style="fill:#999999;"></path>
|
||||
<path d="m 10,20 0,-2 4,0 0,1 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 11,18 2,0 0,1 -2,0 z" style="fill:#999999;"></path>
|
||||
</g>
|
||||
<g transform="matrix(-1,0,0,1,32,0)">
|
||||
<path d="m 10,20 0,-2 4,0 0,1 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 11,18 2,0 0,1 -2,0 z" style="fill:#999999;"></path>
|
||||
<path d="m 4,20 0,-3 1,0 0,-1 4,0 0,3 -1,0 0,1 z" style="fill:#000000;"></path>
|
||||
<path d="m 5,19 0,-2 3,0 0,1 -1,0 0,1 z" style="fill:#999999;"></path>
|
||||
</g>
|
||||
</g>
|
||||
<g id="nyan_tail">
|
||||
<path d="M 0,10 0,7 4,7 4,8 5,8 5,9 6,9 6,14 5,14 5,13 3,13 3,12 2,12 2,11 1,11 1,10 z" style="fill:#000000;" />
|
||||
<path d="m 1,9 0,-1 2,0 0,1 1,0 0,1 1,0 0,1 1,0 0,1 -2,0 0,-1 -1,0 0,-1 -1,0 0,-1 z" style="fill:#999999;" />
|
||||
</g>
|
||||
<g id="nyan_body">
|
||||
<path d="m 7,1 19,0 0,16 -19,0 z" style="fill:#ffcc99;" />
|
||||
<path d="m 8,14 0,-10 1,0 0,-1 1,0 0,-1 13,0 0,1 1,0 0,1 1,0 0,10 -1,0 0,1 -1,0 0,1 -13,0 0,-1 -1,0 0,-1 z" style="fill:#ff99ff;" />
|
||||
<path d="m 22,5 1,0 0,1 -1,0 z m -4,-2 1,0 0,1 -1,0 z m -3,0 1,0 0,1 -1,0 z m -1,4 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m -2,3 1,0 0,1 -1,0 z m -2,-4 1,0 0,1 -1,0 z m -2,2 1,0 0,1 -1,0 z m 1,3 1,0 0,1 -1,0 z m 0,-10 1,0 0,1 -1,0 z" style="fill:#ff3399;" />
|
||||
<path d="m 8,17 17,0 0,1 -17,0 z m 0,-17 17,0 0,1 -17,0 z m 18,16 0,-14 1,0 0,14 z m -20,0 0,-14 1,0 0,14 z m 1,0 1,0 0,1 -1,0 z m 0,-15 1,0 0,1 -1,0 z m 18,0 1,0 0,1 -1,0 z m 0,15 1,0 0,1 -1,0 z" style="fill:#000000;" />
|
||||
</g>
|
||||
<g id="nyan_head">
|
||||
<path d="m 17,15 0,-5 1,0 0,-4 2,0 0,1 1,0 0,1 1,0 0,1 4,0 0,-1 1,0 0,-1 1,0 0,-1 2,0 0,4 1,0 0,5 -1,0 0,1 -1,0 0,1 -10,0 0,-1 -1,0 0,-1 z" style="fill:#999999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 29,16 1,0 0,1 -1,0 z m 1,-1 1,0 0,1 -1,0 z m 1,-5 1,0 0,5 -1,0 z m -1,-4 1,0 0,4 -1,0 z m -2,-1 2,0 0,1 -2,0 z m -6,3 4,0 0,1 -4,0 z m -4,-3 2,0 0,1 -2,0 z m -1,1 1,0 0,4 -1,0 z m -1,4 1,0 0,5 -1,0 z m 11,-4 1,0 0,1 -1,0 z m -1,1 1,0 0,1 -1,0 z m -5,0 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z m -1,11 10,0 0,1 -10,0 z m -1,-1 1,0 0,1 -1,0 z m -1,-1 1,0 0,1 -1,0 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 18,13 2,0 0,2 -2,0 z" style="fill:#ff9999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 29,13 2,0 0,2 -2,0 z" style="fill:#ff9999;fill-opacity:1;stroke:none" />
|
||||
<path d="m 21,16 0,-2 1,0 0,1 2,0 0,-1 1,0 0,1 2,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 25,12 1,0 0,1 -1,0 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<g>
|
||||
<path d="m 27,13 0,-1 1,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 27,11 1,0 0,1 -1,0 z" style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
<path d="m 20,13 0,-1 1,0 0,-1 1,0 0,2 z" style="fill:#000000;fill-opacity:1;stroke:none" />
|
||||
<path d="m 20,11 1,0 0,1 -1,0 z" style="fill:#ffffff;fill-opacity:1;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="rbw">
|
||||
<div class="w">
|
||||
<div class="rbw f1">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f2">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f3">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f4">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f5">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
<div class="rbw f6">
|
||||
<div class="wv wv-1"></div><div class="wv wv-2"></div><div class="wv wv-3"></div>
|
||||
<div class="wv wv-4"></div><div class="wv wv-5"></div><div class="wv wv-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
{{ else }}
|
||||
class ComponentBootScreen extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.innerHTML = `<div class="component_loader">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" style="width: 30px;">
|
||||
<circle fill="#57595A" stroke="#57595A" stroke-width="15" r="15" cx="40" cy="100">
|
||||
<animate attributeName="opacity" calcMode="spline" dur="2" values="1;0;1;" keySplines=".5 0 .5 1;.5 0 .5 1" repeatCount="indefinite" begin="-.4"></animate>
|
||||
</circle>
|
||||
<circle fill="#57595A" stroke="#57595A" stroke-width="15" r="15" cx="100" cy="100">
|
||||
<animate attributeName="opacity" calcMode="spline" dur="2" values="1;0;1;" keySplines=".5 0 .5 1;.5 0 .5 1" repeatCount="indefinite" begin="-.2"></animate>
|
||||
</circle>
|
||||
<circle fill="#57595A" stroke="#57595A" stroke-width="15" r="15" cx="160" cy="100">
|
||||
<animate attributeName="opacity" calcMode="spline" dur="2" values="1;0;1;" keySplines=".5 0 .5 1;.5 0 .5 1" repeatCount="indefinite" begin="0"></animate>
|
||||
</circle>
|
||||
</svg>
|
||||
<style>
|
||||
html, body, #app { height: 100%; margin: 0; }
|
||||
#app { display: flex; }
|
||||
component-bootscreen { margin: auto; }
|
||||
</style>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
{{ end }}
|
||||
customElements.define("component-bootscreen", ComponentBootScreen);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./assets/{{ .version }}/components/loader.js"></script>
|
||||
<div role="main" id="app">
|
||||
{{ if eq .license "agpl" }}
|
||||
<component-nyan></component-nyan>
|
||||
{{ else }}
|
||||
<component-loader delay="500"></component-loader>
|
||||
{{ end }}
|
||||
<component-bootscreen></component-bootscreen>
|
||||
</div>
|
||||
|
||||
<template id="head">
|
||||
<link rel="stylesheet" href="custom.css">
|
||||
<link rel="stylesheet" href="./assets/{{ .version }}/css/designsystem.css">
|
||||
</template>
|
||||
|
||||
<template id="body">
|
||||
<script type="module" src="./assets/{{ .version }}/components/loader.js"></script>
|
||||
<script type="module">
|
||||
import main from "./assets/{{ .version }}/lib/skeleton/index.js";
|
||||
import routes from "./assets/{{ .version }}/boot/router_frontoffice.js";
|
||||
main(document.getElementById("app"), routes, {
|
||||
spinner: `<component-loader></component-loader>`,
|
||||
beforeStart: import("{{ .base }}assets/{{ .version }}/boot/ctrl_boot_frontoffice.js"),
|
||||
});
|
||||
</script>
|
||||
|
||||
<component-modal></component-modal>
|
||||
<script type="module" src="./assets/{{ .version }}/components/modal.js" defer></script>
|
||||
|
||||
<component-notification></component-notification>
|
||||
<script type="module" src="./assets/{{ .version }}/components/notification.js" defer></script>
|
||||
</template>
|
||||
|
||||
<script type="module">
|
||||
import main from "./assets/{{ .version }}/lib/skeleton/index.js";
|
||||
import routes from "./assets/{{ .version }}/boot/router_frontoffice.js";
|
||||
main(document.getElementById("app"), routes, {
|
||||
spinner: `<component-loader></component-loader>`,
|
||||
beforeStart: import("{{ .base }}assets/{{ .version }}/boot/ctrl_boot_frontoffice.js"),
|
||||
});
|
||||
function boot() {
|
||||
document.head.appendChild(document.querySelector("template#head").content);
|
||||
document.body.appendChild(document.querySelector("template#body").content);
|
||||
}
|
||||
|
||||
const URLS = [
|
||||
[
|
||||
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs.min.js",
|
||||
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs-ajax.min.js",
|
||||
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs-shared.min.js",
|
||||
],
|
||||
[
|
||||
"/assets/{{ .version }}/boot/ctrl_boot_frontoffice.js",
|
||||
"/assets/{{ .version }}/locales/index.js",
|
||||
"/assets/{{ .version }}/css/designsystem.css",
|
||||
"/assets/{{ .version }}/css/designsystem_input.css",
|
||||
"/assets/{{ .version }}/css/designsystem_textarea.css",
|
||||
"/assets/{{ .version }}/css/designsystem_inputgroup.css",
|
||||
"/assets/{{ .version }}/css/designsystem_checkbox.css",
|
||||
"/assets/{{ .version }}/css/designsystem_formbuilder.css",
|
||||
"/assets/{{ .version }}/css/designsystem_button.css",
|
||||
"/assets/{{ .version }}/css/designsystem_icon.css",
|
||||
"/assets/{{ .version }}/css/designsystem_dropdown.css",
|
||||
"/assets/{{ .version }}/css/designsystem_container.css",
|
||||
"/assets/{{ .version }}/css/designsystem_box.css",
|
||||
"/assets/{{ .version }}/css/designsystem_darkmode.css",
|
||||
"/assets/{{ .version }}/css/designsystem_skeleton.css",
|
||||
"/assets/{{ .version }}/css/designsystem_utils.css",
|
||||
"/assets/{{ .version }}/css/designsystem_alert.css",
|
||||
"/assets/{{ .version }}/components/loader.js",
|
||||
"/assets/{{ .version }}/components/modal.js",
|
||||
"/assets/{{ .version }}/components/modal.css",
|
||||
"/assets/{{ .version }}/components/notification.js",
|
||||
"/assets/{{ .version }}/components/notification.css",
|
||||
"/assets/{{ .version }}/boot/router_frontoffice.js",
|
||||
"/assets/{{ .version }}/helpers/loader.js",
|
||||
"/assets/{{ .version }}/lib/skeleton/index.js",
|
||||
"/assets/{{ .version }}/lib/rx.js",
|
||||
"/assets/{{ .version }}/lib/ajax.js",
|
||||
"/assets/{{ .version }}/lib/animate.js",
|
||||
"/assets/{{ .version }}/lib/assert.js",
|
||||
"/assets/{{ .version }}/lib/dom.js",
|
||||
"/assets/{{ .version }}/lib/skeleton/router.js",
|
||||
"/assets/{{ .version }}/lib/skeleton/lifecycle.js",
|
||||
"/assets/{{ .version }}/lib/error.js",
|
||||
"/assets/{{ .version }}/model/config.js",
|
||||
"/assets/{{ .version }}/model/plugin.js",
|
||||
"/assets/{{ .version }}/model/chromecast.js",
|
||||
"/assets/{{ .version }}/model/session.js",
|
||||
"/assets/{{ .version }}/helpers/log.js",
|
||||
"/assets/{{ .version }}/boot/common.js",
|
||||
"/assets/{{ .version }}/helpers/sdk.js",
|
||||
|
||||
"/assets/{{ .version }}/components/breadcrumb.js",
|
||||
"/assets/{{ .version }}/components/breadcrumb.css",
|
||||
"/assets/{{ .version }}/components/form.js",
|
||||
"/assets/{{ .version }}/components/sidebar.js",
|
||||
"/assets/{{ .version }}/components/sidebar.css",
|
||||
"/assets/{{ .version }}/components/dropdown.js",
|
||||
"/assets/{{ .version }}/components/icon.js",
|
||||
"/assets/{{ .version }}/lib/store.js",
|
||||
"/assets/{{ .version }}/lib/random.js",
|
||||
"/assets/{{ .version }}/lib/form.js",
|
||||
"/assets/{{ .version }}/lib/path.js",
|
||||
|
||||
"/assets/{{ .version }}/components/decorator_shell_filemanager.js",
|
||||
"/assets/{{ .version }}/components/decorator_shell_filemanager.css",
|
||||
"/assets/{{ .version }}/pages/ctrl_error.js",
|
||||
],
|
||||
[
|
||||
"/assets/{{ .version }}/pages/ctrl_connectpage.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/ctrl_form.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/ctrl_forkme.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/ctrl_poweredby.js",
|
||||
"/assets/{{ .version }}/lib/path.js",
|
||||
"/assets/{{ .version }}/lib/form.js",
|
||||
"/assets/{{ .version }}/lib/settings.js",
|
||||
"/assets/{{ .version }}/components/form.js",
|
||||
"/assets/{{ .version }}/model/session.js",
|
||||
"/assets/{{ .version }}/pages/ctrl_error.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/model_backend.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/model_config.js",
|
||||
"/assets/{{ .version }}/pages/connectpage/ctrl_form_state.js",
|
||||
"/assets/{{ .version }}/lib/random.js",
|
||||
"/assets/{{ .version }}/components/icon.js",
|
||||
|
||||
"/assets/{{ .version }}/pages/ctrl_connectpage.css",
|
||||
"/assets/{{ .version }}/pages/connectpage/ctrl_form.css",
|
||||
],
|
||||
[
|
||||
"/assets/{{ .version }}/pages/ctrl_filespage.js",
|
||||
"/assets/{{ .version }}/pages/ctrl_filespage.css",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_filesystem.js",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_submenu.js",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_newitem.js",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_upload.js",
|
||||
"/assets/{{ .version }}/pages/filespage/cache.js",
|
||||
"/assets/{{ .version }}/pages/filespage/state_config.js",
|
||||
"/assets/{{ .version }}/pages/filespage/thing.js",
|
||||
"/assets/{{ .version }}/pages/filespage/state_newthing.js",
|
||||
"/assets/{{ .version }}/pages/filespage/helper.js",
|
||||
"/assets/{{ .version }}/pages/filespage/model_files.js",
|
||||
"/assets/{{ .version }}/pages/filespage/model_virtual_layer.js",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_share.js",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_tag.js",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_rename.js",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_delete.js",
|
||||
"/assets/{{ .version }}/pages/filespage/state_selection.js",
|
||||
"/assets/{{ .version }}/pages/filespage/model_acl.js",
|
||||
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_filesystem.css",
|
||||
"/assets/{{ .version }}/pages/filespage/thing.css",
|
||||
"/assets/{{ .version }}/pages/filespage/modal.css",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_submenu.css",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_share.css",
|
||||
"/assets/{{ .version }}/pages/filespage/modal_tag.css",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_newitem.css",
|
||||
"/assets/{{ .version }}/pages/filespage/ctrl_upload.css",
|
||||
],
|
||||
];
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
const register = await navigator.serviceWorker.register("sw.js");
|
||||
await new Promise((resolve) => {
|
||||
register.active ?
|
||||
resolve() :
|
||||
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
register.active.postMessage({ "type": "preload", "payload": URLS });
|
||||
await new Promise((resolve, reject) => navigator.serviceWorker.addEventListener("message", (event) => {
|
||||
if (event.data && event.data.type === "preload") {
|
||||
if (event.data.status !== "ok") console.log(`turboload failure data=${JSON.stringify(event.data)}`);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
}
|
||||
boot();
|
||||
</script>
|
||||
|
||||
<component-modal></component-modal>
|
||||
<script type="module" src="./assets/{{ .version }}/components/modal.js" defer></script>
|
||||
|
||||
<component-notification></component-notification>
|
||||
<script type="module" src="./assets/{{ .version }}/components/notification.js" defer></script>
|
||||
|
||||
<noscript>
|
||||
<div>
|
||||
<div style="text-align:center;font-family:monospace;margin-top:5%;font-size:15px;">
|
||||
<h2>Error: Javascript is off</h2>
|
||||
<p>You need to enable Javascript to run this application</p>
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ package ctrl
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -141,64 +142,10 @@ func ServeBackofficeHandler(ctx *App, res http.ResponseWriter, req *http.Request
|
||||
http.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
preloadScripts := []string{
|
||||
"/admin/assets/boot/router_backoffice.js", "/admin/assets/boot/router_backoffice.js", "/admin/assets/boot/ctrl_boot_backoffice.js", "/admin/assets/boot/common.js",
|
||||
"/admin/assets/pages/adminpage/decorator.js", "/admin/assets/pages/adminpage/decorator_sidemenu.js", "/admin/assets/pages/adminpage/decorator_admin_only.js",
|
||||
"/admin/assets/components/icon.js", "/admin/assets/locales/index.js", "/admin/assets/lib/animate.js",
|
||||
"/admin/assets/lib/skeleton/router.js", "/admin/assets/lib/skeleton/lifecycle.js",
|
||||
"/admin/assets/lib/vendor/rxjs/rxjs-shared.min.js", "/admin/assets/lib/vendor/rxjs/rxjs-ajax.min.js", "/admin/assets/lib/ajax.js",
|
||||
"/admin/assets/lib/rx.js", "/admin/assets/lib/vendor/rxjs/rxjs.min.js",
|
||||
}
|
||||
switch TrimBase(url) {
|
||||
case "/admin/backend":
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/admin/assets/pages/adminpage/ctrl_backend.js", "/admin/assets/pages/adminpage/ctrl_backend_component_storage.js", "/admin/assets/pages/adminpage/ctrl_backend_component_authentication.js",
|
||||
"/admin/assets/model/config.js", "/admin/assets/model/backend.js",
|
||||
"/admin/assets/pages/adminpage/model_backend.js", "/admin/assets/pages/adminpage/model_auth_middleware.js",
|
||||
"/admin/assets/lib/random.js", "/admin/assets/lib/form.js", "/admin/assets/components/form.js",
|
||||
"/admin/assets/components/skeleton.js", "/admin/assets/pages/adminpage/ctrl_backend_state.js", "/admin/assets/pages/adminpage/component_box-item.js", "/admin/assets/pages/adminpage/helper_form.js",
|
||||
)
|
||||
case "/admin/settings":
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/admin/assets/pages/adminpage/ctrl_settings.js", "/admin/assets/model/config.js",
|
||||
"/admin/assets/lib/random.js", "/admin/assets/lib/form.js", "/admin/assets/components/form.js",
|
||||
"/admin/assets/components/skeleton.js", "/admin/assets/pages/adminpage/helper_form.js",
|
||||
)
|
||||
case "/admin/logs":
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/admin/assets/pages/adminpage/ctrl_log.js", "/admin/assets/model/config.js", "/admin/assets/lib/random.js",
|
||||
"/admin/assets/pages/adminpage/helper_form.js", "/admin/assets/pages/adminpage/model_log.js",
|
||||
"/admin/assets/pages/adminpage/ctrl_log_form.js", "/admin/assets/pages/adminpage/ctrl_log_viewer.js", "/admin/assets/pages/adminpage/ctrl_log_audit.js",
|
||||
"/admin/assets/lib/form.js", "/admin/assets/components/form.js", "/admin/assets/components/skeleton.js",
|
||||
)
|
||||
case "/admin/about":
|
||||
preloadScripts = append(preloadScripts, "/admin/assets/pages/adminpage/ctrl_about.js")
|
||||
default:
|
||||
preloadScripts = append(preloadScripts, "/admin/assets/pages/ctrl_adminpage.js")
|
||||
}
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/admin/assets/pages/ctrl_error.js", "/admin/assets/pages/adminpage/ctrl_login.js", "/admin/assets/lib/dom.js", "/admin/assets/lib/error.js",
|
||||
"/admin/assets/pages/adminpage/animate.js", "/admin/assets/helpers/log.js", "/admin/assets/helpers/loader.js",
|
||||
"/admin/assets/pages/adminpage/model_config.js", "/admin/assets/pages/adminpage/model_admin_session.js", "/admin/assets/pages/adminpage/model_release.js",
|
||||
"/admin/assets/pages/adminpage/model_audit.js",
|
||||
)
|
||||
head := res.Header()
|
||||
head.Set("Cache-Control", "no-cache")
|
||||
head.Set("Pragma", "no-cache")
|
||||
head.Set("Expires", "0")
|
||||
for _, href := range preloadScripts {
|
||||
head.Add(
|
||||
"Link",
|
||||
fmt.Sprintf(`<%s>; rel="preload"; as="script"; crossorigin="anonymous";`, WithBase(
|
||||
strings.Replace(href, "/assets/", "/assets/"+BUILD_REF+"/", 1),
|
||||
)),
|
||||
)
|
||||
}
|
||||
head.Add("Link", `<`+WithBase("/about")+`>; rel="preload"; as="fetch"; crossorigin="use-credentials";`)
|
||||
|
||||
ServeIndex("index.backoffice.html")(ctx, res, req)
|
||||
return
|
||||
@ -232,65 +179,10 @@ func ServeFrontofficeHandler(ctx *App, res http.ResponseWriter, req *http.Reques
|
||||
return
|
||||
}
|
||||
|
||||
preloadScripts := []string{
|
||||
"/assets/boot/router_frontoffice.js", "/assets/boot/ctrl_boot_frontoffice.js",
|
||||
|
||||
"/assets/lib/skeleton/index.js", "/assets/lib/skeleton/router.js", "/assets/lib/skeleton/lifecycle.js",
|
||||
"/assets/lib/vendor/rxjs/rxjs.min.js", "/assets/lib/vendor/rxjs/rxjs-ajax.min.js", "/assets/lib/vendor/rxjs/rxjs-shared.min.js",
|
||||
"/assets/lib/rx.js", "/assets/lib/ajax.js",
|
||||
"/assets/lib/path.js", "/assets/lib/error.js", "/assets/lib/assert.js",
|
||||
"/assets/lib/dom.js", "/assets/lib/animate.js",
|
||||
|
||||
"/assets/components/loader.js", "/assets/components/modal.js", "/assets/components/notification.js",
|
||||
"/assets/components/icon.js",
|
||||
}
|
||||
turl := TrimBase(url)
|
||||
if strings.HasPrefix(turl, "/login") {
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/assets/pages/ctrl_connectpage.js",
|
||||
"/assets/pages/connectpage/ctrl_form.js", "/assets/pages/connectpage/ctrl_forkme.js",
|
||||
"/assets/pages/connectpage/ctrl_poweredby.js",
|
||||
"/assets/pages/connectpage/model_config.js", "/assets/pages/connectpage/model_backend.js", "/assets/pages/connectpage/ctrl_form_state.js",
|
||||
|
||||
"/assets/lib/form.js", "/assets/lib/settings.js", "/assets/lib/random.js", "/assets/helpers/log.js",
|
||||
"/assets/model/session.js", "/assets/components/form.js",
|
||||
"/assets/pages/ctrl_error.js",
|
||||
)
|
||||
} else if strings.HasPrefix(turl, "/files/") {
|
||||
preloadScripts = append(
|
||||
preloadScripts,
|
||||
"/assets/pages/filespage/ctrl_filesystem.js", "/assets/pages/filespage/modal_share.js",
|
||||
"/assets/pages/filespage/ctrl_upload.js", "/assets/components/sidebar.js", "/assets/components/breadcrumb.js",
|
||||
"/assets/lib/form.js", "/assets/components/dropdown.js", "/assets/components/decorator_shell_filemanager.js",
|
||||
"/assets/lib/random.js",
|
||||
|
||||
"/assets/pages/filespage/ctrl_submenu.js", "/assets/pages/filespage/ctrl_newitem.js",
|
||||
"/assets/pages/filespage/cache.js", "/assets/pages/ctrl_filespage.js",
|
||||
"/assets/pages/filespage/thing.js", "/assets/pages/ctrl_error.js", "/assets/pages/filespage/model_virtual_layer.js",
|
||||
"/assets/pages/filespage/model_files.js", "/assets/pages/filespage/helper.js",
|
||||
"/assets/pages/filespage/model_acl.js", "/assets/pages/filespage/state_config.js",
|
||||
"/assets/pages/filespage/state_newthing.js", "/assets/pages/filespage/state_selection.js",
|
||||
"/assets/pages/filespage/modal_delete.js", "/assets/pages/filespage/modal_rename.js", "/assets/pages/filespage/modal_tag.js",
|
||||
|
||||
"/assets/components/form.js",
|
||||
"/assets/helpers/log.js", "/assets/lib/error.js", "/assets/model/config.js",
|
||||
"/assets/lib/assert.js", "/assets/model/session.js", "/assets/lib/store.js",
|
||||
"/assets/pages/filespage/state_config.js",
|
||||
)
|
||||
}
|
||||
head := res.Header()
|
||||
head.Set("Cache-Control", "no-cache")
|
||||
head.Set("Pragma", "no-cache")
|
||||
head.Set("Expires", "0")
|
||||
for _, href := range preloadScripts {
|
||||
head.Add(
|
||||
"Link",
|
||||
fmt.Sprintf(`<%s>; rel="preload"; as="script"; crossorigin="anonymous";`, WithBase(
|
||||
strings.Replace(href, "/assets/", "/assets/"+BUILD_REF+"/", 1),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
ServeIndex("index.frontoffice.html")(ctx, res, req)
|
||||
}
|
||||
@ -438,6 +330,11 @@ func ServeFile(chroot string) func(*App, http.ResponseWriter, *http.Request) {
|
||||
)
|
||||
head := res.Header()
|
||||
|
||||
if filePath == "/assets/bundle" {
|
||||
ServeBundle(ctx, res, req)
|
||||
return
|
||||
}
|
||||
|
||||
// case: patch must be apply because of a "StaticPatch" plugin
|
||||
for _, patch := range Hooks.Get.StaticPatch() {
|
||||
patchFile, err := patch.Open(strings.TrimPrefix(filePath, "/"))
|
||||
@ -552,6 +449,32 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func ServeBundle(ctx *App, res http.ResponseWriter, req *http.Request) {
|
||||
res.Header().Set("Content-Type", "text/event-stream")
|
||||
res.Header().Set("Cache-Control", "no-cache")
|
||||
res.Header().Set("Connection", "keep-alive")
|
||||
res.WriteHeader(http.StatusOK)
|
||||
|
||||
urls := req.URL.Query()["url"]
|
||||
for i := 0; i < len(urls); i++ {
|
||||
curPath := "assets" + strings.TrimPrefix(urls[i], "/assets/"+BUILD_REF)
|
||||
file, err := WWWPublic.Open(curPath + ".gz")
|
||||
if err != nil {
|
||||
Log.Warning("static::sse failed to find file %s", curPath)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(res, "id: %s\n", urls[i])
|
||||
fmt.Fprintf(res, "data: ")
|
||||
b, _ := io.ReadAll(file)
|
||||
res.Write([]byte(base64.StdEncoding.EncodeToString(b)))
|
||||
fmt.Fprintf(res, "\n\n")
|
||||
res.(http.Flusher).Flush()
|
||||
file.Close()
|
||||
}
|
||||
fmt.Fprint(res, "\n")
|
||||
res.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func InitPluginList(code []byte) {
|
||||
listOfPackages := regexp.MustCompile(`\t_?\s*\"(github.com/[^\"]+)`).FindAllStringSubmatch(string(code), -1)
|
||||
for _, packageNameMatch := range listOfPackages {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -58,6 +59,10 @@ func (w *ResponseWriter) Write(b []byte) (int, error) {
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
func (w *ResponseWriter) Flush() {
|
||||
w.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func PluginInjector(fn HandlerFunc) HandlerFunc {
|
||||
for _, middleware := range Hooks.Get.Middleware() {
|
||||
fn = middleware(fn)
|
||||
|
||||
@ -83,11 +83,18 @@ func logger(ctx App, res http.ResponseWriter, req *http.Request) {
|
||||
telemetry.Record(point)
|
||||
}
|
||||
if Config.Get("log.enable").Bool() {
|
||||
Log.Stdout("HTTP %3d %3s %6.1fms %s", point.Status, point.Method, point.Duration, point.RequestURI)
|
||||
Log.Stdout("HTTP %3d %3s %6.1fms %s", point.Status, point.Method, point.Duration, limit(point.RequestURI, 200))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func limit(input string, maxLength int) string {
|
||||
if len(input) > maxLength {
|
||||
return input[:maxLength] + "..."
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
func (this *Telemetry) Record(point LogEntry) {
|
||||
this.mu.Lock()
|
||||
this.Data = append(this.Data, point)
|
||||
|
||||
@ -100,6 +100,7 @@ func Build(a App) *mux.Router {
|
||||
r.HandleFunc(WithBase("/sw_cache.js"), NewMiddlewareChain(LegacyStaticHandler("/assets/worker/"), middlewares, a)).Methods("GET")
|
||||
} else { // TODO: remove this after migration is done
|
||||
r.PathPrefix(WithBase("/assets")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeFile("/"), middlewares, a))).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc(WithBase("/sw.js"), http.HandlerFunc(NewMiddlewareChain(ServeFile("/assets/"), middlewares, a))).Methods("GET")
|
||||
r.HandleFunc(WithBase("/favicon.ico"), NewMiddlewareChain(ServeFavicon, middlewares, a)).Methods("GET")
|
||||
r.HandleFunc(WithBase("/plugin/{name}/{path:.+}"), NewMiddlewareChain(PluginStaticHandler, middlewares, a)).Methods("GET")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user