Add ability to use vite dev alongside the go backend

The point is to not have to rebuild every time to test a change, but make use of Vite's HMR and other goodies.
Some things are not yet working in dev mode:
- Upload is stuck and fails
- Reloading page while on a file will give 404 error
- Prob more that i didnt catch
This commit is contained in:
Kloon ImKloon 2023-08-18 18:25:24 +02:00
parent 74d15d08ad
commit 8c60137fe4
No known key found for this signature in database
GPG Key ID: CCF1C86A995C5B6A
10 changed files with 315 additions and 210 deletions

View File

@ -8,25 +8,19 @@
content="width=device-width, initial-scale=1, user-scalable=no" content="width=device-width, initial-scale=1, user-scalable=no"
/> />
[{[ if .ReCaptcha -]}] <title>File Browser</title>
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
[{[ end ]}]
<title>
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]
</title>
<link <link
rel="icon" rel="icon"
type="image/png" type="image/png"
sizes="32x32" sizes="32x32"
href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png" href="/img/icons/favicon-32x32.png"
/> />
<link <link
rel="icon" rel="icon"
type="image/png" type="image/png"
sizes="16x16" sizes="16x16"
href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png" href="/img/icons/favicon-16x16.png"
/> />
<!-- Add to home screen for Android and modern mobile browsers --> <!-- Add to home screen for Android and modern mobile browsers -->
@ -35,49 +29,63 @@
id="manifestPlaceholder" id="manifestPlaceholder"
crossorigin="use-credentials" crossorigin="use-credentials"
/> />
<meta <meta name="theme-color" content="#2979ff" />
name="theme-color"
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
/>
<!-- Add to home screen for Safari on iOS/iPadOS --> <!-- Add to home screen for Safari on iOS/iPadOS -->
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="assets" /> <meta name="apple-mobile-web-app-title" content="assets" />
<link <link rel="apple-touch-icon" href="/img/icons/apple-touch-icon.png" />
rel="apple-touch-icon"
href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"
/>
<!-- Add to home screen for Windows --> <!-- Add to home screen for Windows -->
<meta <meta
name="msapplication-TileImage" name="msapplication-TileImage"
content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png" content="/img/icons/mstile-144x144.png"
/>
<meta
name="msapplication-TileColor"
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
/> />
<meta name="msapplication-TileColor" content="#2979ff" />
<!-- Inject Some Variables and generate the manifest json --> <!-- Inject Some Variables and generate the manifest json -->
<script> <script>
<!-- Json is actually a JS object, assign it directly --> // We can assign JSON directly
window.FileBrowser = [{[ .Json ]}]; window.FileBrowser = {
<!-- Global function to prepend static url --> AuthMethod: "json",
BaseURL: "http://localhost:8080",
CSS: false,
Color: "",
DisableExternal: false,
DisableUsedPercentage: false,
EnableExec: true,
EnableThumbs: true,
LoginPage: true,
Name: "",
NoAuth: false,
ReCaptcha: false,
ResizePreview: true,
Signup: false,
StaticURL: "",
Theme: "",
TusSettings: { chunkSize: 10485760, retryCount: 5 },
Version: "(untracked)",
};
// Global function to prepend static url
window.__prependStaticUrl = (url) => { window.__prependStaticUrl = (url) => {
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, '')}`; return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
}; };
var dynamicManifest = { var dynamicManifest = {
name: window.FileBrowser.Name || "File Browser", name: window.FileBrowser.Name || "File Browser",
short_name: window.FileBrowser.Name || "File Browser", short_name: window.FileBrowser.Name || "File Browser",
icons: [ icons: [
{ {
src: window.__prependStaticUrl("/img/icons/android-chrome-192x192.png"), src: window.__prependStaticUrl(
"/img/icons/android-chrome-192x192.png"
),
sizes: "192x192", sizes: "192x192",
type: "image/png", type: "image/png",
}, },
{ {
src: window.__prependStaticUrl("/img/icons/android-chrome-512x512.png"), src: window.__prependStaticUrl(
"/img/icons/android-chrome-512x512.png"
),
sizes: "512x512", sizes: "512x512",
type: "image/png", type: "image/png",
}, },
@ -180,14 +188,5 @@
</div> </div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
[{[ if .Theme -]}]
<link
rel="stylesheet"
href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css"
/>
[{[ end ]}] [{[ if .CSS -]}]
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
[{[ end ]}]
</body> </body>
</html> </html>

View File

@ -1,63 +1,99 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <meta
name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no"
/>
[{[ if .ReCaptcha -]}] [{[ if .ReCaptcha -]}]
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script> <script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
[{[ end ]}] [{[ end ]}]
<title>[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]</title> <title>
[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]
</title>
<link rel="icon" type="image/png" sizes="32x32" href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png"> <link
<link rel="icon" type="image/png" sizes="16x16" href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png"> rel="icon"
type="image/png"
sizes="32x32"
href="[{[ .StaticURL ]}]/img/icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="[{[ .StaticURL ]}]/img/icons/favicon-16x16.png"
/>
<!-- Add to home screen for Android and modern mobile browsers --> <!-- Add to home screen for Android and modern mobile browsers -->
<link rel="manifest" id="manifestPlaceholder" crossorigin="use-credentials"> <link
<meta name="theme-color" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"> rel="manifest"
id="manifestPlaceholder"
crossorigin="use-credentials"
/>
<meta
name="theme-color"
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
/>
<!-- Add to home screen for Safari on iOS/iPadOS --> <!-- Add to home screen for Safari on iOS/iPadOS -->
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="assets"> <meta name="apple-mobile-web-app-title" content="assets" />
<link rel="apple-touch-icon" href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"> <link
rel="apple-touch-icon"
href="[{[ .StaticURL ]}]/img/icons/apple-touch-icon.png"
/>
<!-- Add to home screen for Windows --> <!-- Add to home screen for Windows -->
<meta name="msapplication-TileImage" content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png"> <meta
<meta name="msapplication-TileColor" content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"> name="msapplication-TileImage"
content="[{[ .StaticURL ]}]/img/icons/mstile-144x144.png"
/>
<meta
name="msapplication-TileColor"
content="[{[ if .Color -]}][{[ .Color ]}][{[ else ]}]#2979ff[{[ end ]}]"
/>
<!-- Inject Some Variables and generate the manifest json --> <!-- Inject Some Variables and generate the manifest json -->
<script> <script>
window.FileBrowser = JSON.parse('[{[ .Json ]}]'); // We can assign JSON directly
window.FileBrowser = [{[ .Json ]}];
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL; // Global function to prepend static url
window.__prependStaticUrl = (url) => {
return `${window.FileBrowser.StaticURL}/${url.replace(/^\/+/, "")}`;
};
var dynamicManifest = { var dynamicManifest = {
"name": window.FileBrowser.Name || 'File Browser', name: window.FileBrowser.Name || "File Browser",
"short_name": window.FileBrowser.Name || 'File Browser', short_name: window.FileBrowser.Name || "File Browser",
"icons": [ icons: [
{ {
"src": fullStaticURL + "/img/icons/android-chrome-192x192.png", src: window.__prependStaticUrl("/img/icons/android-chrome-192x192.png"),
"sizes": "192x192", sizes: "192x192",
"type": "image/png" type: "image/png",
}, },
{ {
"src": fullStaticURL + "/img/icons/android-chrome-512x512.png", src: window.__prependStaticUrl("/img/icons/android-chrome-512x512.png"),
"sizes": "512x512", sizes: "512x512",
"type": "image/png" type: "image/png",
} },
], ],
"start_url": window.location.origin + window.FileBrowser.BaseURL, start_url: window.location.origin + window.FileBrowser.BaseURL,
"display": "standalone", display: "standalone",
"background_color": "#ffffff", background_color: "#ffffff",
"theme_color": window.FileBrowser.Color || "#455a64" theme_color: window.FileBrowser.Color || "#455a64",
} };
const stringManifest = JSON.stringify(dynamicManifest); const stringManifest = JSON.stringify(dynamicManifest);
const blob = new Blob([stringManifest], {type: 'application/json'}); const blob = new Blob([stringManifest], { type: "application/json" });
const manifestURL = URL.createObjectURL(blob); const manifestURL = URL.createObjectURL(blob);
document.querySelector('#manifestPlaceholder').setAttribute('href', manifestURL); document
.querySelector("#manifestPlaceholder")
.setAttribute("href", manifestURL);
</script> </script>
<style> <style>
@ -69,8 +105,8 @@
height: 100%; height: 100%;
background: #fff; background: #fff;
z-index: 9999; z-index: 9999;
transition: .1s ease opacity; transition: 0.1s ease opacity;
-webkit-transition: .1s ease opacity; -webkit-transition: 0.1s ease opacity;
} }
#loading.done { #loading.done {
@ -108,17 +144,26 @@
} }
@-webkit-keyframes sk-bouncedelay { @-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) } 0%,
40% { -webkit-transform: scale(1.0) } 80%,
100% {
-webkit-transform: scale(0);
}
40% {
-webkit-transform: scale(1);
}
} }
@keyframes sk-bouncedelay { @keyframes sk-bouncedelay {
0%, 80%, 100% { 0%,
80%,
100% {
-webkit-transform: scale(0); -webkit-transform: scale(0);
transform: scale(0); transform: scale(0);
} 40% { }
-webkit-transform: scale(1.0); 40% {
transform: scale(1.0); -webkit-transform: scale(1);
transform: scale(1);
} }
} }
</style> </style>
@ -134,10 +179,14 @@
</div> </div>
</div> </div>
<script type="module" src="/src/main.js"></script>
[{[ if .Theme -]}] [{[ if .Theme -]}]
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" /> <link
[{[ end ]}] rel="stylesheet"
[{[ if .CSS -]}] href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css"
/>
[{[ end ]}] [{[ if .CSS -]}]
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" /> <link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
[{[ end ]}] [{[ end ]}]
</body> </body>

View File

@ -33,7 +33,7 @@ const titles = {
}; };
const router = new Router({ const router = new Router({
base: baseURL, base: import.meta.env.PROD ? baseURL : "",
mode: "history", mode: "history",
routes: [ routes: [
{ {

View File

@ -1,7 +1,7 @@
import store from "@/store"; import store from "@/store";
import router from "@/router"; import router from "@/router";
import { Base64 } from "js-base64"; import { Base64 } from "js-base64";
import { baseURL } from "@/utils/constants"; import { fetchURL } from "@/api/utils";
export function parseToken(token) { export function parseToken(token) {
const parts = token.split("."); const parts = token.split(".");
@ -25,20 +25,24 @@ export async function validateLogin() {
await renew(localStorage.getItem("jwt")); await renew(localStorage.getItem("jwt"));
} }
} catch (_) { } catch (_) {
console.warn('Invalid JWT token in storage') // eslint-disable-line console.warn("Invalid JWT token in storage"); // eslint-disable-line
} }
} }
export async function login(username, password, recaptcha) { export async function login(username, password, recaptcha) {
const data = { username, password, recaptcha }; const data = { username, password, recaptcha };
const res = await fetch(`${baseURL}/api/login`, { const res = await fetchURL(
`/api/login`,
{
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); },
false
);
const body = await res.text(); const body = await res.text();
@ -50,7 +54,7 @@ export async function login(username, password, recaptcha) {
} }
export async function renew(jwt) { export async function renew(jwt) {
const res = await fetch(`${baseURL}/api/renew`, { const res = await fetchURL(`/api/renew`, {
method: "POST", method: "POST",
headers: { headers: {
"X-Auth": jwt, "X-Auth": jwt,
@ -69,13 +73,17 @@ export async function renew(jwt) {
export async function signup(username, password) { export async function signup(username, password) {
const data = { username, password }; const data = { username, password };
const res = await fetch(`${baseURL}/api/signup`, { const res = await fetchURL(
`/api/signup`,
{
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); },
false
);
if (res.status !== 200) { if (res.status !== 200) {
throw new Error(res.status); throw new Error(res.status);

View File

@ -5,23 +5,44 @@ import legacy from "@vitejs/plugin-legacy";
import vue2 from "@vitejs/plugin-vue2"; import vue2 from "@vitejs/plugin-vue2";
import { compression } from "vite-plugin-compression2"; import { compression } from "vite-plugin-compression2";
// https://vitejs.dev/config/ const plugins = [
export default defineConfig({
plugins: [
vue2(), vue2(),
legacy({ legacy({
targets: ["ie >= 11"], targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"], additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
}), }),
compression({ include: /\.js$/i, deleteOriginalAssets: true }), compression({ include: /\.js$/i, deleteOriginalAssets: true }),
], ];
resolve: {
const resolve = {
alias: { alias: {
vue: "vue/dist/vue.esm.js", vue: "vue/dist/vue.esm.js",
"@/": fileURLToPath(new URL("./src/", import.meta.url)), "@/": fileURLToPath(new URL("./src/", import.meta.url)),
}, },
}, };
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
if (command === "serve") {
return {
plugins,
resolve,
};
} else {
// command === 'build'
return {
plugins,
resolve,
base: "", base: "",
build: {
rollupOptions: {
input: {
index: fileURLToPath(
new URL(`./public/index.html`, import.meta.url)
),
},
},
},
experimental: { experimental: {
renderBuiltUrl(filename, { hostType }) { renderBuiltUrl(filename, { hostType }) {
if (hostType === "js") { if (hostType === "js") {
@ -33,4 +54,6 @@ export default defineConfig({
} }
}, },
}, },
};
}
}); });

View File

@ -49,7 +49,9 @@ func (d *data) Check(path string) bool {
func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler { func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") for k, v := range global_headers {
w.Header().Set(k, v)
}
settings, err := store.Settings.Get() settings, err := store.Settings.Get()
if err != nil { if err != nil {

9
http/headers.go Normal file
View File

@ -0,0 +1,9 @@
//go:build !dev
// +build !dev
package http
// global headers to append to every response
var global_headers = map[string]string{
"Cache-Control": "no-cache, no-store, must-revalidate",
}

15
http/headers_dev.go Normal file
View File

@ -0,0 +1,15 @@
//go:build dev
// +build dev
package http
// global headers to append to every response
// cross-origin headers are necessary to be able to
// access them from a different URL during development
var global_headers = map[string]string{
"Cache-Control": "no-cache, no-store, must-revalidate",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "*",
"Access-Control-Allow-Credentials": "true",
}

View File

@ -27,7 +27,7 @@ func NewHandler(
r := mux.NewRouter() r := mux.NewRouter()
r.Use(func(next http.Handler) http.Handler { r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", `default-src 'self'; style-src 'unsafe-inline';`) // w.Header().Set("Content-Security-Policy", `default-src 'self'; style-src 'unsafe-inline';`)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
}) })

View File

@ -105,7 +105,7 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
} }
w.Header().Set("x-xss-protection", "1; mode=block") w.Header().Set("x-xss-protection", "1; mode=block")
return handleWithStaticData(w, r, d, assetsFs, "index.html", "text/html; charset=utf-8") return handleWithStaticData(w, r, d, assetsFs, "public/index.html", "text/html; charset=utf-8")
}, "", store, server) }, "", store, server)
static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {