mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-27 10:42:36 +01:00
feature (fastboot): optimise boot sequence
This commit is contained in:
parent
6e0037bfa7
commit
e72cbe3c35
5 changed files with 216 additions and 313 deletions
1
Jenkinsfile
vendored
1
Jenkinsfile
vendored
|
|
@ -28,6 +28,7 @@ pipeline {
|
|||
sh "make build_frontend"
|
||||
}
|
||||
docker.image("golang:1.24-bookworm").inside("--user=root") {
|
||||
sh "apt update -y && apt install -y libbrotli-dev brotli"
|
||||
sh "sed -i 's|plg_image_c|plg_image_golang|' server/plugin/index.go"
|
||||
sh "make build_init"
|
||||
sh "make build_backend"
|
||||
|
|
|
|||
|
|
@ -1,27 +1,5 @@
|
|||
let VERSION = null;
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
|
|
@ -58,104 +36,27 @@ self.addEventListener("message", (event) => {
|
|||
);
|
||||
});
|
||||
|
||||
async function handlePreloadMessage(chunks, clear, version, resolve, reject) {
|
||||
async function handlePreloadMessage(imports, clear, version, resolve, reject) {
|
||||
VERSION = version;
|
||||
const cleanup = [];
|
||||
try {
|
||||
let execHTTP = true;
|
||||
await caches.keys().then(async(names) => {
|
||||
for (let i=0; i<names.length; i++) {
|
||||
if (names[i] === VERSION && !clear) {
|
||||
execHTTP = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i<names.length; i++) {
|
||||
await caches.delete(names[i]);
|
||||
}
|
||||
});
|
||||
if (execHTTP) {
|
||||
const cache = await caches.open(VERSION);
|
||||
chunks = await Promise.all(chunks.map(async(urls) => {
|
||||
const missing = [];
|
||||
await Promise.all(urls.map(async(url) => {
|
||||
if (!await cache.match(location.origin + url)) missing.push(url);
|
||||
}));
|
||||
return missing;
|
||||
}));
|
||||
if (chunks.filter((urls) => urls.length > 0).length > 0) {
|
||||
await Promise.all(chunks.map((urls) => {
|
||||
return preload({ urls, cache, cleanup });
|
||||
}));
|
||||
}
|
||||
const cache = await caches.open(VERSION);
|
||||
for (const path in imports) {
|
||||
let mime = "application/octet-stream";
|
||||
if (path.endsWith(".css")) mime = "text/css";
|
||||
else if (path.endsWith(".js")) mime = "application/javascript";
|
||||
await cache.put(location.origin + path, new Response(
|
||||
new Blob([imports[path]]),
|
||||
{ headers: { "Content-Type": mime } },
|
||||
));
|
||||
}
|
||||
resolve();
|
||||
} catch (err) {
|
||||
console.log("ERR", err);
|
||||
reject(err);
|
||||
} finally {
|
||||
cleanup.forEach((fn) => fn());
|
||||
}
|
||||
};
|
||||
|
||||
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 = async(resolve, event, decoder) => {
|
||||
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;
|
||||
await cache.put(
|
||||
location.origin + url,
|
||||
new Response(
|
||||
decoder(new Blob([base128Decode(event.data)]).stream()),
|
||||
{ headers: { "Content-Type": mime } },
|
||||
),
|
||||
);
|
||||
if (i === urls.length) {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
const errorHandler = (reject, err) => {
|
||||
reject(err);
|
||||
};
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
evtsrc.addEventListener("static::raw", (event) => messageHandler(
|
||||
resolve,
|
||||
event,
|
||||
(stream) => stream,
|
||||
));
|
||||
evtsrc.addEventListener("static::gzip", (event) => messageHandler(
|
||||
resolve,
|
||||
event,
|
||||
(stream) => stream.pipeThrough(new DecompressionStream("gzip")),
|
||||
));
|
||||
evtsrc.onerror = (err) => {
|
||||
if (i === urls.length) return;
|
||||
errorHandler(reject, err);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function base128Decode(s) { // encoder is in server/ctrl/static.go -> encodeB128
|
||||
const out = new Uint8Array(Math.floor((s.length * 7) / 8) + 1);
|
||||
let acc = 0;
|
||||
let bits = 0;
|
||||
let oi = 0;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
const ch = s.charCodeAt(i);
|
||||
const digit = ch & 0x7F; // undo 0x80 masking for NUL/LF/CR
|
||||
acc = (acc << 7) | digit;
|
||||
bits += 7;
|
||||
while (bits >= 8) {
|
||||
bits -= 8;
|
||||
out[oi++] = (acc >> bits) & 0xFF;
|
||||
acc &= (1 << bits) - 1;
|
||||
}
|
||||
}
|
||||
return out.subarray(0, oi);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
<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">
|
||||
|
|
@ -30,36 +29,47 @@
|
|||
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 id="preload" type="application/json">{{ .preload }}</script>
|
||||
|
||||
<script type="module">
|
||||
function boot() {
|
||||
function liftoff() {
|
||||
document.head.appendChild(document.querySelector("template#head").content);
|
||||
document.body.appendChild(document.querySelector("template#body").content);
|
||||
}
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
const URLS = JSON.parse(document.getElementById("preload").textContent);
|
||||
async function ignitionSequence() {
|
||||
if (!("serviceWorker" in navigator)) return
|
||||
try {
|
||||
const register = await navigator.serviceWorker.register("sw.js");
|
||||
await new Promise((resolve) => {
|
||||
register.active ?
|
||||
resolve() :
|
||||
window.bundler = (function () {
|
||||
let modules = [];
|
||||
return {
|
||||
register: (path, code) => modules[path] = code,
|
||||
state: () => modules,
|
||||
};
|
||||
})();
|
||||
const [register] = await Promise.all([
|
||||
navigator.serviceWorker.register("sw.js").then((register) => new Promise((resolve) => {
|
||||
register.active ?
|
||||
resolve(register) :
|
||||
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
||||
resolve();
|
||||
resolve(register);
|
||||
});
|
||||
});
|
||||
})),
|
||||
new Promise((resolve, reject) => {
|
||||
const $script = document.createElement("script");
|
||||
$script.type = "module";
|
||||
$script.src = "./assets/bundle.js?version={{ slice .version 0 7 }}::{{ .hash }}";
|
||||
document.head.appendChild($script);
|
||||
$script.onload = resolve;
|
||||
$script.onerror = reject;
|
||||
}),
|
||||
]);
|
||||
register.active.postMessage({
|
||||
"type": "preload",
|
||||
"payload": URLS,
|
||||
"payload": bundler.state(),
|
||||
"version": "{{ slice .version 0 7 }}::{{ .hash }}",
|
||||
"clear": {{ .clear }},
|
||||
});
|
||||
|
|
@ -71,14 +81,40 @@
|
|||
}));
|
||||
} catch (err) { console.error(err); }
|
||||
}
|
||||
boot();
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// /\
|
||||
// / \
|
||||
// || / \
|
||||
// || /______\
|
||||
// ||| |
|
||||
// | | |
|
||||
// | | |
|
||||
// |__|________|
|
||||
// |___________|
|
||||
// | | |
|
||||
// |__| || |\
|
||||
// ||| || | \
|
||||
// /||| || | \
|
||||
// /_|||...||...|___\
|
||||
// |||::::::::|
|
||||
// || \::::::/
|
||||
// || ||__||
|
||||
// || ||
|
||||
// || \\_______________
|
||||
// _______________||______`---------------
|
||||
// |
|
||||
// | |
|
||||
await ignitionSequence() // |
|
||||
// |
|
||||
liftoff() // |
|
||||
// | |
|
||||
// |_____________________________________________|
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<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>
|
||||
</noscript>
|
||||
<noscript><div style="text-align:center;font-family:monospace;margin-top:5%;font-size:15px;"><h2>Error: Javascript is off</h2></div></noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
. "github.com/mickael-kerjean/filestash/server/common"
|
||||
|
||||
"github.com/bluekeyes/go-gitdiff/gitdiff"
|
||||
"github.com/google/brotli/go/cbrotli"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -234,7 +235,6 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
|
|||
"base": base,
|
||||
"version": BUILD_REF,
|
||||
"license": LICENSE,
|
||||
"preload": preload(),
|
||||
"clear": clear,
|
||||
"hash": sign,
|
||||
"favicon": favicon(),
|
||||
|
|
@ -257,71 +257,158 @@ 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)
|
||||
func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
|
||||
paths := []string{
|
||||
"/assets/" + BUILD_REF + "/boot/ctrl_boot_frontoffice.js",
|
||||
"/assets/" + BUILD_REF + "/boot/router_frontoffice.js",
|
||||
"/assets/" + BUILD_REF + "/boot/common.js",
|
||||
|
||||
urls := req.URL.Query()["url"]
|
||||
for i := 0; i < len(urls); i++ {
|
||||
curPath := "/assets/" + strings.TrimPrefix(urls[i], "/assets/"+BUILD_REF+"/")
|
||||
var file io.ReadCloser
|
||||
var err error
|
||||
if f := applyPatch(curPath); f != nil {
|
||||
file = io.NopCloser(f)
|
||||
fmt.Fprintf(res, "event: %s\n", "static::raw")
|
||||
} else {
|
||||
file, err = WWWPublic.Open(curPath + ".gz")
|
||||
"/assets/" + BUILD_REF + "/css/designsystem.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_input.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_textarea.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_inputgroup.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_checkbox.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_formbuilder.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_button.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_icon.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_dropdown.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_container.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_box.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_darkmode.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_skeleton.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_utils.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_alert.css",
|
||||
|
||||
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.css",
|
||||
"/assets/" + BUILD_REF + "/components/loader.js",
|
||||
"/assets/" + BUILD_REF + "/components/modal.js",
|
||||
"/assets/" + BUILD_REF + "/components/modal.css",
|
||||
"/assets/" + BUILD_REF + "/components/notification.js",
|
||||
"/assets/" + BUILD_REF + "/components/notification.css",
|
||||
"/assets/" + BUILD_REF + "/components/sidebar.js",
|
||||
"/assets/" + BUILD_REF + "/components/sidebar.css",
|
||||
"/assets/" + BUILD_REF + "/components/dropdown.js",
|
||||
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.js",
|
||||
"/assets/" + BUILD_REF + "/components/form.js",
|
||||
"/assets/" + BUILD_REF + "/components/icon.js",
|
||||
"/assets/" + BUILD_REF + "/components/breadcrumb.js",
|
||||
"/assets/" + BUILD_REF + "/components/breadcrumb.css",
|
||||
"/assets/" + BUILD_REF + "/components/skeleton.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/helpers/loader.js",
|
||||
"/assets/" + BUILD_REF + "/helpers/log.js",
|
||||
"/assets/" + BUILD_REF + "/helpers/sdk.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs.min.js",
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-ajax.min.js",
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-shared.min.js",
|
||||
"/assets/" + BUILD_REF + "/lib/store.js",
|
||||
"/assets/" + BUILD_REF + "/lib/form.js",
|
||||
"/assets/" + BUILD_REF + "/lib/path.js",
|
||||
"/assets/" + BUILD_REF + "/lib/random.js",
|
||||
"/assets/" + BUILD_REF + "/lib/settings.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/index.js",
|
||||
"/assets/" + BUILD_REF + "/lib/rx.js",
|
||||
"/assets/" + BUILD_REF + "/lib/ajax.js",
|
||||
"/assets/" + BUILD_REF + "/lib/animate.js",
|
||||
"/assets/" + BUILD_REF + "/lib/assert.js",
|
||||
"/assets/" + BUILD_REF + "/lib/dom.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/router.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/lifecycle.js",
|
||||
"/assets/" + BUILD_REF + "/lib/error.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/locales/index.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/model/config.js",
|
||||
"/assets/" + BUILD_REF + "/model/chromecast.js",
|
||||
"/assets/" + BUILD_REF + "/model/session.js",
|
||||
"/assets/" + BUILD_REF + "/model/plugin.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.css",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_forkme.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_poweredby.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/model_backend.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/model_config.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form_state.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_logout.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_error.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_acl.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/cache.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/thing.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/thing.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_config.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/helper.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_files.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_virtual_layer.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_rename.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_delete.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_selection.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_newthing.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css",
|
||||
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/mimetype.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/model_files.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/common.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/application_downloader.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css",
|
||||
}
|
||||
|
||||
var bundlePlain bytes.Buffer
|
||||
for _, path := range paths {
|
||||
curPath := "/assets/" + strings.TrimPrefix(path, "/assets/"+BUILD_REF+"/")
|
||||
f := applyPatch(curPath)
|
||||
if f == nil {
|
||||
file, err := WWWPublic.Open(curPath)
|
||||
if err != nil {
|
||||
file, err = WWWPublic.Open(curPath)
|
||||
if err != nil {
|
||||
Log.Warning("static::sse failed to find file %s", curPath)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(res, "event: %s\n", "static::raw")
|
||||
} else {
|
||||
fmt.Fprintf(res, "event: %s\n", "static::gzip")
|
||||
Log.Warning("static::bundler failed to find file %s", err.Error())
|
||||
continue
|
||||
}
|
||||
f = new(bytes.Buffer)
|
||||
if _, err := io.Copy(f, file); err != nil {
|
||||
Log.Warning("static::bundler msg=copy_error err=%s", err.Error())
|
||||
continue
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
fmt.Fprintf(res, "id: %s\n", urls[i])
|
||||
fmt.Fprintf(res, "data: ")
|
||||
b, _ := io.ReadAll(file)
|
||||
res.Write([]byte(encodeB128(b)))
|
||||
fmt.Fprintf(res, "\n\n")
|
||||
res.(http.Flusher).Flush()
|
||||
file.Close()
|
||||
code, err := json.Marshal(f.String())
|
||||
if err != nil {
|
||||
Log.Warning("static::bundle msg=marshal_failed path=%s err=%s", path, err.Error())
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&bundlePlain, "bundler.register(%q, %s);\n", path, code)
|
||||
}
|
||||
fmt.Fprint(res, "\n")
|
||||
res.(http.Flusher).Flush()
|
||||
}
|
||||
bundleBr, _ := cbrotli.Encode(bundlePlain.Bytes(), cbrotli.WriterOptions{Quality: 8})
|
||||
|
||||
func encodeB128(src []byte) string { // decoder is in public/assets/sw.js
|
||||
if len(src) == 0 {
|
||||
return ""
|
||||
}
|
||||
out := make([]rune, 0, len(src)+len(src)/7) // N + N/7 runes (+tail)
|
||||
var bits uint32
|
||||
var n int // bits held in 'bits'
|
||||
emit := func(d byte) {
|
||||
if d == 0x00 || d == 0x0A || d == 0x0D { // NUL, LF, CR
|
||||
out = append(out, rune(0x80|d)) // 2-byte UTF-8, still decodable as d&0x7F
|
||||
return func(ctx *App, res http.ResponseWriter, req *http.Request) {
|
||||
res.Header().Set("Content-Type", "application/javascript")
|
||||
if strings.Contains(req.Header.Get("Accept-Encoding"), "br") {
|
||||
res.Header().Set("Content-Encoding", "br")
|
||||
res.Write(bundleBr)
|
||||
return
|
||||
}
|
||||
out = append(out, rune(d)) // ASCII, 1-byte UTF-8
|
||||
res.Write(bundlePlain.Bytes())
|
||||
}
|
||||
for _, b := range src {
|
||||
bits = (bits << 8) | uint32(b)
|
||||
n += 8
|
||||
for n >= 7 {
|
||||
n -= 7
|
||||
emit(byte((bits >> n) & 0x7F))
|
||||
}
|
||||
}
|
||||
if n > 0 { // tail
|
||||
emit(byte((bits << (7 - n)) & 0x7F))
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func applyPatch(filePath string) (file *bytes.Buffer) {
|
||||
|
|
@ -373,128 +460,6 @@ func applyPatch(filePath string) (file *bytes.Buffer) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func preload() string {
|
||||
out, _ := json.Marshal([][]string{
|
||||
{
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs.min.js",
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-ajax.min.js",
|
||||
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-shared.min.js",
|
||||
|
||||
"/assets/" + BUILD_REF + "/css/designsystem.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_input.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_textarea.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_inputgroup.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_checkbox.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_formbuilder.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_button.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_icon.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_dropdown.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_container.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_box.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_darkmode.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_skeleton.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_utils.css",
|
||||
"/assets/" + BUILD_REF + "/css/designsystem_alert.css",
|
||||
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.css",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.css",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.css",
|
||||
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.css",
|
||||
},
|
||||
{
|
||||
"/assets/" + BUILD_REF + "/boot/ctrl_boot_frontoffice.js",
|
||||
"/assets/" + BUILD_REF + "/boot/router_frontoffice.js",
|
||||
"/assets/" + BUILD_REF + "/boot/common.js",
|
||||
"/assets/" + BUILD_REF + "/components/loader.js",
|
||||
"/assets/" + BUILD_REF + "/components/modal.js",
|
||||
"/assets/" + BUILD_REF + "/components/modal.css",
|
||||
"/assets/" + BUILD_REF + "/components/notification.js",
|
||||
"/assets/" + BUILD_REF + "/components/notification.css",
|
||||
"/assets/" + BUILD_REF + "/components/sidebar.js",
|
||||
"/assets/" + BUILD_REF + "/components/sidebar.css",
|
||||
"/assets/" + BUILD_REF + "/components/dropdown.js",
|
||||
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.js",
|
||||
"/assets/" + BUILD_REF + "/helpers/loader.js",
|
||||
"/assets/" + BUILD_REF + "/helpers/log.js",
|
||||
"/assets/" + BUILD_REF + "/helpers/sdk.js",
|
||||
"/assets/" + BUILD_REF + "/locales/index.js",
|
||||
"/assets/" + BUILD_REF + "/lib/store.js",
|
||||
"/assets/" + BUILD_REF + "/lib/form.js",
|
||||
"/assets/" + BUILD_REF + "/lib/path.js",
|
||||
"/assets/" + BUILD_REF + "/lib/random.js",
|
||||
"/assets/" + BUILD_REF + "/model/config.js",
|
||||
"/assets/" + BUILD_REF + "/model/chromecast.js",
|
||||
"/assets/" + BUILD_REF + "/model/session.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_acl.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/cache.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/thing.css",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.js",
|
||||
},
|
||||
{
|
||||
"/assets/" + BUILD_REF + "/components/form.js",
|
||||
"/assets/" + BUILD_REF + "/components/icon.js",
|
||||
"/assets/" + BUILD_REF + "/lib/settings.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/index.js",
|
||||
"/assets/" + BUILD_REF + "/lib/rx.js",
|
||||
"/assets/" + BUILD_REF + "/lib/ajax.js",
|
||||
"/assets/" + BUILD_REF + "/lib/animate.js",
|
||||
"/assets/" + BUILD_REF + "/lib/assert.js",
|
||||
"/assets/" + BUILD_REF + "/lib/dom.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/router.js",
|
||||
"/assets/" + BUILD_REF + "/lib/skeleton/lifecycle.js",
|
||||
"/assets/" + BUILD_REF + "/lib/error.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_error.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.js",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_viewerpage.css",
|
||||
"/assets/" + BUILD_REF + "/pages/ctrl_logout.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_forkme.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_poweredby.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/model_backend.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/model_config.js",
|
||||
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form_state.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.js",
|
||||
},
|
||||
{
|
||||
"/assets/" + BUILD_REF + "/components/breadcrumb.js",
|
||||
"/assets/" + BUILD_REF + "/components/breadcrumb.css",
|
||||
"/assets/" + BUILD_REF + "/components/skeleton.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_config.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/helper.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_files.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/model_virtual_layer.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.css",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_rename.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_delete.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_selection.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/state_newthing.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/mimetype.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/model_files.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/common.js",
|
||||
},
|
||||
{
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/thing.js",
|
||||
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.js",
|
||||
"/assets/" + BUILD_REF + "/pages/viewerpage/application_downloader.js",
|
||||
"/assets/" + BUILD_REF + "/model/plugin.js",
|
||||
},
|
||||
})
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func signature() string {
|
||||
text := BUILD_REF
|
||||
patches := Hooks.Get.StaticPatch()
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ func Build(r *mux.Router, a App) {
|
|||
r.HandleFunc(WithBase("/api/plugin"), NewMiddlewareChain(PluginExportHandler, append(middlewares, PublicCORS), a)).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc(WithBase("/api/config"), NewMiddlewareChain(PublicConfigHandler, append(middlewares, PublicCORS), a)).Methods("GET", "OPTIONS")
|
||||
middlewares = []Middleware{StaticHeaders, SecureHeaders, PublicCORS, PluginInjector}
|
||||
r.PathPrefix(WithBase("/assets/bundle")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeBundle, middlewares, a))).Methods("GET", "OPTIONS")
|
||||
r.PathPrefix(WithBase("/assets/bundle.js")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeBundle(), middlewares, a))).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc(WithBase("/assets/"+BUILD_REF+"/plugin/{name}.zip/{path:.+}"), NewMiddlewareChain(PluginStaticHandler, middlewares, a)).Methods("GET", "OPTIONS", "HEAD")
|
||||
r.HandleFunc(WithBase("/assets/"+BUILD_REF+"/plugin/{name}.zip"), NewMiddlewareChain(PluginDownloadHandler, middlewares, a)).Methods("GET")
|
||||
r.HandleFunc(WithBase("/assets/plugin/{name}.zip"), NewMiddlewareChain(PluginDownloadHandler, middlewares, a)).Methods("GET")
|
||||
|
|
|
|||
Loading…
Reference in a new issue