From 177f13d1fc46b4b760cad38ba0b61be6bdd72a61 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Sun, 1 Jun 2025 19:37:10 +0000 Subject: [PATCH] feat: vite dashboard tweaks/fixes --- dashboard/index.html | 5 ++- dashboard/package.json | 2 +- dashboard/serve.js | 18 ++++++--- dashboard/src/api.ts | 5 +-- dashboard/src/auth.ts | 4 +- dashboard/src/components/Splash.vue | 53 +++++++++++++++++++++++++ dashboard/src/{init-vue.ts => index.ts} | 20 +++------- dashboard/src/main.ts | 33 --------------- dashboard/src/routes.ts | 3 ++ dashboard/src/splash.html | 32 --------------- dashboard/src/style/app.css | 6 ++- dashboard/src/style/content.css | 5 +-- dashboard/src/style/initial.css | 3 -- dashboard/src/style/splash.css | 11 +---- dashboard/src/vite-env.d.ts | 4 ++ dashboard/vite.config.ts | 18 +-------- 16 files changed, 94 insertions(+), 128 deletions(-) create mode 100644 dashboard/src/components/Splash.vue rename dashboard/src/{init-vue.ts => index.ts} (69%) delete mode 100644 dashboard/src/main.ts delete mode 100644 dashboard/src/splash.html delete mode 100644 dashboard/src/style/initial.css diff --git a/dashboard/index.html b/dashboard/index.html index aedadb14..f7448266 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -14,9 +14,10 @@

Zeppelin

The Zeppelin website requires JavaScript to load. + +
-
- + diff --git a/dashboard/package.json b/dashboard/package.json index 839ebb0f..832e1b3f 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -6,7 +6,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vue-tsc -b && vite build", + "build": "vite build", "preview": "vite preview" }, "devDependencies": { diff --git a/dashboard/serve.js b/dashboard/serve.js index 2b94dde5..936b4fbc 100644 --- a/dashboard/serve.js +++ b/dashboard/serve.js @@ -2,20 +2,26 @@ import Fastify from "fastify"; import fastifyStatic from "@fastify/static"; import path from "node:path"; -const fastify = Fastify({ logger: true }); +const fastify = Fastify({ + // We already get logs from nginx, so disable here + logger: false, +}); -fastify.get("/env.js", (req, reply) => { - reply.header("Content-Type", "application/javascript; charset=utf8"); - reply.send(`window.API_URL = ${JSON.stringify(process.env.API_URL)}`); +fastify.addHook("preHandler", (req, reply, done) => { + if (req.url === "/env.js") { + reply.header("Content-Type", "application/javascript; charset=utf8"); + reply.send(`window.API_URL = ${JSON.stringify(process.env.API_URL)};`); + } + done(); }); fastify.register(fastifyStatic, { - root: path.join(__dirname, "dist"), + root: path.join(import.meta.dirname, "dist"), wildcard: false, }); fastify.get("*", (req, reply) => { - reply.header("Content-Type", "text/html; charset=utf8").send(indexContent); + reply.sendFile("index.html"); }); fastify.listen({ port: 3002, host: '0.0.0.0' }, (err, address) => { diff --git a/dashboard/src/api.ts b/dashboard/src/api.ts index 27047bed..92d62baf 100644 --- a/dashboard/src/api.ts +++ b/dashboard/src/api.ts @@ -1,5 +1,4 @@ import { RootStore } from "./store"; -const apiUrl = window.API_URL; type QueryParamObject = { [key: string]: string | null }; @@ -28,7 +27,7 @@ function buildQueryString(params: QueryParamObject) { } export function request(resource, fetchOpts: RequestInit = {}) { - return fetch(`${apiUrl}/${resource}`, fetchOpts).then(async (res) => { + return fetch(`${window.API_URL}/${resource}`, fetchOpts).then(async (res) => { if (!res.ok) { if (res.status === 401) { RootStore.dispatch("auth/expiredLogin"); @@ -74,7 +73,7 @@ type FormPostOpts = { export function formPost(resource: string, body: Record = {}, opts: FormPostOpts = {}) { body["X-Api-Key"] = RootStore.state.auth.apiKey; const form = document.createElement("form"); - form.action = `${apiUrl}/${resource}`; + form.action = `${window.API_URL}/${resource}`; form.method = "POST"; form.enctype = "multipart/form-data"; if (opts.target != null) { diff --git a/dashboard/src/auth.ts b/dashboard/src/auth.ts index 92fcd936..1bc0cc04 100644 --- a/dashboard/src/auth.ts +++ b/dashboard/src/auth.ts @@ -11,7 +11,7 @@ const isAuthenticated = async () => { export const authGuard: NavigationGuard = async (to, from, next) => { if (await isAuthenticated()) return next(); - window.location.href = `${process.env.API_URL}/auth/login`; + window.location.href = `${window.API_URL}/auth/login`; }; export const loginCallbackGuard: NavigationGuard = async (to, from, next) => { @@ -26,6 +26,6 @@ export const loginCallbackGuard: NavigationGuard = async (to, from, next) => { export const authRedirectGuard: NavigationGuard = async (to, form, next) => { if (await isAuthenticated()) return next("/dashboard"); - window.location.href = `${process.env.API_URL}/auth/login`; + window.location.href = `${window.API_URL}/auth/login`; return next(); }; diff --git a/dashboard/src/components/Splash.vue b/dashboard/src/components/Splash.vue new file mode 100644 index 00000000..c75b681b --- /dev/null +++ b/dashboard/src/components/Splash.vue @@ -0,0 +1,53 @@ + + + diff --git a/dashboard/src/init-vue.ts b/dashboard/src/index.ts similarity index 69% rename from dashboard/src/init-vue.ts rename to dashboard/src/index.ts index b881ce69..8fe06b93 100644 --- a/dashboard/src/init-vue.ts +++ b/dashboard/src/index.ts @@ -2,8 +2,8 @@ import "./style/app.css"; import { createApp } from "vue"; -import VueHighlightJS from "vue3-highlightjs"; import "highlight.js/styles/base16/ocean.css"; +import VueHighlightJS from "vue3-highlightjs"; import { router } from "./routes"; import { RootStore } from "./store"; @@ -13,24 +13,14 @@ import "./directives/trim-indents"; import App from "./components/App.vue"; import { trimIndents } from "./directives/trim-indents"; +if (!window.API_URL) { + throw new Error("Missing API_URL"); +} + const app = createApp(App); app.use(router); app.use(RootStore); - -// Set up a read-only global variable to access specific env vars -app.mixin({ - data() { - return { - get env() { - return Object.freeze({ - API_URL: process.env.API_URL, - }); - }, - }; - }, -}); - app.use(VueHighlightJS); app.directive("trim-indents", trimIndents); diff --git a/dashboard/src/main.ts b/dashboard/src/main.ts deleted file mode 100644 index 07011ba0..00000000 --- a/dashboard/src/main.ts +++ /dev/null @@ -1,33 +0,0 @@ -import "./style/initial.css"; -import splashHtml from "./splash.html"; - -if (window.location.pathname !== "/") { - import("./init-vue"); -} else { - // @ts-ignore - document.querySelector("#app").innerHTML = splashHtml; - - const queryParams: any = window.location.search - .slice(1) - .split("&") - .reduce((map, str) => { - const pair = str.split("="); - map[pair[0]] = pair[1]; - return map; - }, {}); - - if (queryParams.error) { - const errorElement = document.querySelector("#error") as HTMLElement; - errorElement.classList.add("has-error"); - - const errorMessages = { - noAccess: "No dashboard access. If you think this is a mistake, please contact your server owner.", - expiredLogin: "Dashboard login expired. Please log in again.", - }; - - const errorMessageElem = document.createElement("div"); - errorMessageElem.classList.add("message"); - errorMessageElem.innerText = errorMessages[queryParams.error] || "Unexpected error"; - errorElement.appendChild(errorMessageElem); - } -} diff --git a/dashboard/src/routes.ts b/dashboard/src/routes.ts index d1b0e2df..96f8d524 100644 --- a/dashboard/src/routes.ts +++ b/dashboard/src/routes.ts @@ -1,9 +1,12 @@ import { createRouter, createWebHistory } from "vue-router"; import { authGuard, authRedirectGuard, loginCallbackGuard } from "./auth"; +import Splash from "./components/Splash.vue"; export const router = createRouter({ history: createWebHistory(), routes: [ + { path: "/", component: Splash }, + { path: "/login", components: {}, beforeEnter: authRedirectGuard }, { path: "/login-callback", component: {}, beforeEnter: loginCallbackGuard }, diff --git a/dashboard/src/splash.html b/dashboard/src/splash.html deleted file mode 100644 index d4ada971..00000000 --- a/dashboard/src/splash.html +++ /dev/null @@ -1,32 +0,0 @@ -
-
-
-
- -
-
-

Zeppelin

-
- Zeppelin is a private moderation bot for Discord, designed with large servers and reliability in mind. -
- - -
-
-
diff --git a/dashboard/src/style/app.css b/dashboard/src/style/app.css index 896d7b3d..18d44803 100644 --- a/dashboard/src/style/app.css +++ b/dashboard/src/style/app.css @@ -1,9 +1,11 @@ -@import "tailwindcss"; +@import "./reset.css"; +@import "./base.css"; +@import "./splash.css"; +@import "tailwindcss"; @import "vue-material-design-icons/styles.css"; @import "./content.css"; - @import "./docs.css"; /* Reset some icon default styles for more predictable alignment */ diff --git a/dashboard/src/style/content.css b/dashboard/src/style/content.css index 48023def..f2c0e880 100644 --- a/dashboard/src/style/content.css +++ b/dashboard/src/style/content.css @@ -66,15 +66,14 @@ } } -@screen lg { +@media (width >= theme(--breakpoint-lg)) { .main-content { & h1 { @apply text-5xl; } } } - -@screen xl { +@media (width >= theme(--breakpoint-xl)) { .main-content { & a:not([class]), & a[class=""] { diff --git a/dashboard/src/style/initial.css b/dashboard/src/style/initial.css deleted file mode 100644 index c8dc9ab6..00000000 --- a/dashboard/src/style/initial.css +++ /dev/null @@ -1,3 +0,0 @@ -@import "./reset.css"; -@import "./base.css"; -@import "./splash.css"; diff --git a/dashboard/src/style/splash.css b/dashboard/src/style/splash.css index d25bb505..0ef4fb92 100644 --- a/dashboard/src/style/splash.css +++ b/dashboard/src/style/splash.css @@ -15,17 +15,14 @@ color: #fff; } - & > #error { + & > .error { + display: flex; width: 100%; max-width: 750px; flex-direction: row; justify-content: center; margin-top: 16px; - &.has-error { - display: flex; - } - & .message { flex: 0 1 auto; text-align: left; @@ -154,7 +151,3 @@ } } } - -@media screen and (min-width: 1024px) { - -} diff --git a/dashboard/src/vite-env.d.ts b/dashboard/src/vite-env.d.ts index 733a4556..ead6f8ec 100644 --- a/dashboard/src/vite-env.d.ts +++ b/dashboard/src/vite-env.d.ts @@ -4,3 +4,7 @@ declare module '*.html' { const value: string; export default value; } + +interface Window { + API_URL: string; +} diff --git a/dashboard/vite.config.ts b/dashboard/vite.config.ts index f6ba8958..01ff64dd 100644 --- a/dashboard/vite.config.ts +++ b/dashboard/vite.config.ts @@ -1,22 +1,7 @@ -import { defineConfig, Plugin } from "vite"; +import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import tailwind from "@tailwindcss/vite"; -function htmlImport(): Plugin { - return { - name: "html-import", - transform(code, id) { - if (id.endsWith(".html")) { - return { - code: `export default ${JSON.stringify(code)};`, - map: null, - }; - } - return null; - }, - }; -} - export default defineConfig((configEnv) => { return { server: { @@ -34,7 +19,6 @@ export default defineConfig((configEnv) => { }, }), tailwind(), - htmlImport(), ], }; });