3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-07-05 10:17:20 +00:00

refactor: build dashboard with vite

This commit is contained in:
Dragory 2025-06-01 14:43:40 +00:00
parent f5806932af
commit aaac328138
No known key found for this signature in database
45 changed files with 1284 additions and 9668 deletions

View file

@ -5,5 +5,12 @@
"service": "devenv",
"remoteUser": "ubuntu",
"workspaceFolder": "/workspace/zeppelin"
"workspaceFolder": "/workspace/zeppelin",
"customizations": {
"vscode": {
"extensions": [
"Vue.volar"
]
}
}
}

View file

@ -42,7 +42,6 @@
"cross-env": "^7.0.3",
"deep-diff": "^1.0.2",
"discord.js": "^14.19.3",
"dotenv": "^4.0.0",
"emoji-regex": "^8.0.0",
"escape-string-regexp": "^1.0.5",
"express": "^4.20.0",

View file

@ -1,3 +0,0 @@
module.exports = {
collapseWhitespace: false,
};

View file

@ -15,6 +15,8 @@
The Zeppelin website requires JavaScript to load.
</noscript>
<script type="text/javascript" src="/env.js"></script>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>

View file

@ -3,54 +3,37 @@
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"scripts": {
"build": "rimraf dist && cross-env NODE_ENV=production webpack --config webpack.config.js",
"build-debug": "rimraf dist && cross-env NODE_ENV=development webpack --config webpack.config.js",
"watch": "cross-env NODE_ENV=development webpack-dev-server"
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"babel-loader": "^9.1.2",
"@tailwindcss/vite": "^4.1.8",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/tsconfig": "^0.7.0",
"cross-env": "^7.0.3",
"css-loader": "^6.8.1",
"cssnano": "^4.1.10",
"dotenv": "^16.4.5",
"file-loader": "^6.2.0",
"html-loader": "^4.2.0",
"html-webpack-plugin": "^5.5.3",
"postcss-import": "^15.1.0",
"postcss-loader": "^7.3.3",
"postcss-nesting": "^11.3.0",
"postcss-preset-env": "^8.5.1",
"source-map-loader": "^4.0.1",
"tailwindcss": "^1.9.6",
"ts-loader": "^9.4.3",
"vue-loader": "^17.4.2",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.7.14",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.9.0"
},
"dependencies": {
"@fastify/static": "^7.0.1",
"fastify": "^4.26.2",
"highlight.js": "^11.8.0",
"humanize-duration": "^3.27.0",
"js-yaml": "^4.1.0",
"marked": "^5.1.0",
"modern-css-reset": "^1.4.0",
"moment": "^2.29.4",
"postcss-nesting": "^13.0.1",
"tailwindcss": "^4.1.8",
"vite": "npm:rolldown-vite@latest",
"vue": "^3.5.13",
"vue-material-design-icons": "^5.3.1",
"vue-router": "^4.5.0",
"vue-tsc": "^2.2.10",
"vue3-ace-editor": "^2.2.4",
"vue3-highlightjs": "^1.0.5",
"vuex": "^4.1.0"
},
"dependencies": {
"@fastify/static": "^7.0.1",
"fastify": "^4.26.2"
},
"browserslist": [
"last 2 Chrome versions"
]

View file

@ -0,0 +1,8 @@
import nesting from "postcss-nesting";
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: [nesting]
}
export default config;

2
dashboard/public/env.js Normal file
View file

@ -0,0 +1,2 @@
// Don't edit this directly, it uses env vars in prod via serve.js
window.API_URL = "/api";

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

View file

@ -1,6 +1,13 @@
const fastify = require("fastify")({ logger: true });
const fastifyStatic = require("@fastify/static");
const path = require("path");
import Fastify from "fastify";
import fastifyStatic from "@fastify/static";
import path from "node:path";
const fastify = Fastify({ logger: true });
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.register(fastifyStatic, {
root: path.join(__dirname, "dist"),
@ -8,7 +15,7 @@ fastify.register(fastifyStatic, {
});
fastify.get("*", (req, reply) => {
reply.sendFile("index.html");
reply.header("Content-Type", "text/html; charset=utf8").send(indexContent);
});
fastify.listen({ port: 3002, host: '0.0.0.0' }, (err, address) => {

View file

@ -1,5 +1,5 @@
import { RootStore } from "./store";
const apiUrl = process.env.API_URL;
const apiUrl = window.API_URL;
type QueryParamObject = { [key: string]: string | null };

View file

@ -20,7 +20,7 @@
</template>
<style scoped>
@import "../style/components.pcss";
@reference "../style/app.css";
.expandable {
--animation-time: 400ms;

View file

@ -87,6 +87,6 @@
</template>
<script type="ts">
import "../style/privacy-policy.pcss";
import "../style/privacy-policy.css";
export default {};
</script>

View file

@ -5,6 +5,8 @@
</template>
<style scoped>
@reference "../style/app.css";
li {
padding-bottom: 1px;
@ -26,7 +28,7 @@
}
}
.active a {
.active :deep(a) {
@apply text-gray-200;
}
</style>

View file

@ -25,7 +25,7 @@
lang="yaml"
theme="tomorrow_night"
ref="aceEditor"
v-options="{
:options="{
useSoftTabs: true,
tabSize: 2
}"

View file

@ -2,7 +2,7 @@
<div>
<h1>Guild Info</h1>
<p>
<img class="inline-block w-16 mr-4" style="vertical-align: -20px" src="../../img/squint.png"> What are you doing here
<img class="inline-block w-16 mr-4" style="vertical-align: -20px" src="/img/squint.png"> What are you doing here
</p>
</div>
</template>

View file

@ -4,7 +4,7 @@
<nav class="flex items-stretch flex-wrap pl-4 pr-2 py-1 border border-gray-700 rounded bg-gray-800 shadow-xl mb-8">
<div class="flex-full md:flex-initial flex items-center">
<img class="w-10 mr-5" :src="logoUrl" alt="" aria-hidden="true">
<img class="w-10 mr-5" src="/img/logo.png" alt="" aria-hidden="true">
<router-link to="/dashboard">
<h1 class="font-semibold">Zeppelin Dashboard</h1>
@ -48,7 +48,6 @@
<script>
import Title from "../Title.vue";
import logoUrl from "../../img/logo.png";
export default {
components: {
@ -60,8 +59,5 @@
window.location.pathname = '/';
}
},
data() {
return { logoUrl };
},
};
</script>

View file

@ -104,8 +104,8 @@
</template>
<script>
import CodeBlock from "./CodeBlock";
import Expandable from "../Expandable";
import CodeBlock from "./CodeBlock.vue";
import Expandable from "../Expandable.vue";
export default {
components: {

View file

@ -346,8 +346,8 @@
</template>
<script>
import CodeBlock from "./CodeBlock";
import Expandable from "../Expandable";
import CodeBlock from "./CodeBlock.vue";
import Expandable from "../Expandable.vue";
export default {
components: { CodeBlock, Expandable },

View file

@ -5,7 +5,7 @@
<!-- Top bar -->
<nav class="flex items-stretch pl-4 pr-2 py-1 border border-gray-700 rounded bg-gray-800 shadow-xl">
<div class="flex-initial flex items-center">
<img class="flex-auto w-10 mr-5" :src="logoUrl" alt="" aria-hidden="true">
<img class="flex-auto w-10 mr-5" src="/img/logo.png" alt="" aria-hidden="true">
<router-link to="/docs">
<h1 class="flex-auto font-semibold">Zeppelin Documentation</h1>
@ -27,9 +27,9 @@
<a class="sr-only-when-not-focused text-center block py-2" href="#main-anchor">Skip to main content</a>
<!-- Content wrapper -->
<div class="flex flex-wrap items-start mt-8">
<div class="flex flex-wrap lg:flex-nowrap items-start mt-8 gap-8">
<!-- Sidebar -->
<nav class="docs-sidebar px-4 pt-2 pb-3 mr-8 mb-4 border border-gray-700 rounded bg-gray-800 shadow-md flex-full lg:flex-none lg:block" v-bind:class="{ closed: !mobileMenuOpen }">
<nav class="docs-sidebar px-4 pt-2 pb-3 border border-gray-700 rounded bg-gray-800 shadow-md flex-full lg:flex-none lg:block" v-bind:class="{ closed: !mobileMenuOpen }">
<div role="none" v-for="(group, index) in menu">
<h1 class="font-bold" :aria-owns="'menu-group-' + index" :class="{'mt-4': typeof index === 'number' && index !== 0}">{{ group.label }}</h1>
<ul v-bind:id="'menu-group-' + index" role="group" class="list-none pl-2">
@ -41,7 +41,7 @@
</nav>
<!-- Content -->
<main class="docs-content main-content flex-flexible overflow-x-hidden">
<main class="docs-content main-content flex-auto overflow-x-hidden">
<a id="main-anchor" ref="main-anchor" tabindex="-1" class="sr-only"></a>
<router-view :key="$route.fullPath"></router-view>
</main>
@ -53,7 +53,6 @@
import {mapState} from "vuex";
import Menu from 'vue-material-design-icons/Menu.vue';
import Title from "../Title.vue";
import logoUrl from "../../img/logo.png";
type TMenuItem = {
to: string;
@ -132,12 +131,12 @@
data() {
return {
mobileMenuOpen: false,
logoUrl,
};
},
methods: {
toggleMobileMenu() {
console.log('hi');
this.mobileMenuOpen = !this.mobileMenuOpen;
},

View file

@ -81,7 +81,7 @@
</template>
<script>
import CodeBlock from "./CodeBlock";
import CodeBlock from "./CodeBlock.vue";
export default {
components: { CodeBlock },

View file

@ -9,7 +9,7 @@
<MarkdownBlock :content="data.info.description" class="content"></MarkdownBlock>
<div v-if="data.info.type === 'legacy'">
<div class="px-3 py-2 mb-4 rounded bg-gray-800 shadow-md inline-block flex">
<div class="px-3 py-2 mb-4 rounded bg-gray-800 shadow-md flex">
<div class="flex-none mr-2">
<alert class="inline-icon mr-1 text-yellow-300" />
</div>
@ -163,6 +163,8 @@
</template>
<style scoped>
@reference "../../style/app.css";
.command.target {
@apply mt-5 mb-3;
@apply pt-2 pb-2 pl-4 pr-4;
@ -174,7 +176,6 @@
</style>
<script lang="ts">
import Vue from "vue";
import {mapState} from "vuex";
import yaml from "js-yaml";
import CodeBlock from "./CodeBlock.vue";

View file

@ -1,4 +1,4 @@
import "./style/app.pcss";
import "./style/app.css";
import { createApp } from "vue";

View file

@ -1,5 +1,5 @@
import "./style/initial.pcss";
const splashHtml = require("./splash.html");
import "./style/initial.css";
import splashHtml from "./splash.html";
if (window.location.pathname !== "/") {
import("./init-vue");

View file

@ -2,7 +2,7 @@
<div id="error"></div>
<div class="wrapper">
<div class="logo-column">
<img class="logo" src="./img/logo.png" alt="Zeppelin Logo" />
<img class="logo" src="/img/logo.png" alt="Zeppelin Logo" />
</div>
<div class="info-column">
<h1>Zeppelin</h1>

View file

@ -1,13 +1,10 @@
@import "~tailwindcss/base.css";
@import "~tailwindcss/components.css";
@import "~tailwindcss/utilities.css";
@import "tailwindcss";
@import "~vue-material-design-icons/styles.css";
@import "vue-material-design-icons/styles.css";
@import "components.pcss";
@import "content.pcss";
@import "./content.css";
@import "docs.pcss";
@import "./docs.css";
/* Reset some icon default styles for more predictable alignment */
.material-design-icon > .material-design-icon__svg {

View file

@ -1,3 +1,5 @@
@import "./components.css";
.main-content {
& h1 {
@apply text-3xl;
@ -25,7 +27,12 @@
& a:not([class]),
& a[class=""] {
@apply link;
@apply text-blue-400;
@apply underline;
&:hover {
@apply text-blue-200;
}
}
& ul:not([class]) {
@ -47,7 +54,11 @@
}
& code:not([class]) {
@apply inline-code;
@apply inline-block;
@apply bg-gray-800;
@apply px-1;
@apply rounded;
@apply text-sm;
}
& .expandable:not(.wide) {

View file

@ -4,7 +4,7 @@
}
}
@screen until-lg {
@media (width < theme(--breakpoint-lg)) {
.docs-sidebar.closed:not(:focus-within) {
@apply sr-only;
}

View file

@ -0,0 +1,3 @@
@import "./reset.css";
@import "./base.css";
@import "./splash.css";

View file

@ -1,3 +0,0 @@
@import "./reset.pcss";
@import "./base.pcss";
@import "./splash.pcss";

View file

@ -0,0 +1,50 @@
@layer base {
/* Box sizing rules */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default padding */
ul,
ol {
padding: 0;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
ul,
ol,
li,
figure,
figcaption,
blockquote,
dl,
dd {
margin: 0;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
/* Remove all animations and transitions for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
}

View file

@ -1,48 +0,0 @@
/* Box sizing rules */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* Remove default padding */
ul,
ol {
padding: 0;
}
/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
ul,
ol,
li,
figure,
figcaption,
blockquote,
dl,
dd {
margin: 0;
}
/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
font: inherit;
}
/* Remove all animations and transitions for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

6
dashboard/src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
/// <reference types="vite/client" />
declare module '*.html' {
const value: string;
export default value;
}

View file

@ -1,28 +0,0 @@
module.exports = {
important: true,
theme: {
extend: {
lineHeight: {
zero: "0",
},
flex: {
full: "0 0 100%",
flexible: "1 1 0",
},
},
screens: {
sm: "640px",
md: "768px",
"until-lg": { max: "1023px" },
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
},
variants: {},
plugins: [],
purge: ["./src/**/*.{html,vue}"],
future: {
purgeLayersByDefault: true,
},
};

View file

@ -1,10 +0,0 @@
declare module "*.vue" {
import { DefineComponent } from "vue";
const component: DefineComponent;
export default component;
}
declare module "*.png" {
const value: string;
export default value;
}

View file

@ -15,7 +15,7 @@
"esModuleInterop": true,
"allowJs": true
},
"include": ["src/**/*.ts", "src/**/*.vue", "./ts-vue-shim.d.ts"],
"include": ["src/**/*.ts", "src/**/*.vue"],
"references": [
{
"path": "../shared/tsconfig.json"

40
dashboard/vite.config.ts Normal file
View file

@ -0,0 +1,40 @@
import { defineConfig, Plugin } 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: {
port: 3002,
host: "0.0.0.0",
allowedHosts: true,
},
plugins: [
vue({
template: {
compilerOptions: {
// Needed to prevent hardcoded code blocks from breaking in docs
whitespace: "preserve",
},
},
}),
tailwind(),
htmlImport(),
],
};
});

View file

@ -1,196 +0,0 @@
const path = require("path");
const { VueLoaderPlugin } = require("vue-loader");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { merge } = require("webpack-merge");
const webpack = require("webpack");
const dotenv = require("dotenv");
dotenv.config({ path: path.resolve(process.cwd(), "../.env") });
const targetDir = path.normalize(path.join(__dirname, "dist"));
if (!process.env.NODE_ENV) {
console.error("Please set NODE_ENV");
process.exit(1);
}
if (!process.env.API_URL) {
console.error("API_URL missing from environment variables");
process.exit(1);
}
const babelOpts = {
presets: ["@babel/preset-env"],
};
const tsconfig = require("./tsconfig.json");
const pathAliases = Object.entries(tsconfig.compilerOptions.paths || []).reduce((aliases, pair) => {
let alias = pair[0];
if (alias.endsWith("/*")) alias = alias.slice(0, -2);
let aliasPath = pair[1][0];
if (aliasPath.endsWith("/*")) aliasPath = aliasPath.slice(0, -2);
aliases[alias] = path.resolve(__dirname, aliasPath);
return aliases;
}, {});
const postcssPlugins = [
require("postcss-import")({
resolve(id, base, options) {
// Since WebStorm doesn't resolve imports from node_modules without a tilde (~) prefix,
// strip the tilde here to get the best of both worlds (webstorm support + postcss-import support)
if (id[0] === "~") id = id.slice(1);
// Call the original resolver after stripping the tilde
return require("postcss-import/lib/resolve-id")(id, base, options);
},
}),
require("postcss-nesting")(),
require("tailwindcss")(),
];
if (process.env.NODE_ENV === "production") {
postcssPlugins.push(require("postcss-preset-env")(), require("cssnano")());
}
let config = {
entry: "./src/main.ts",
output: {
filename: "[name].[fullhash].js",
path: targetDir,
publicPath: "/",
},
module: {
rules: [
// Vue / Babel / Typescript
{
test: /\.vue$/,
loader: "vue-loader",
options: {
compilerOptions: {
whitespace: 'preserve', // not the default despite the docs saying so
},
}
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: babelOpts,
},
{
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
},
},
],
},
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: babelOpts,
},
},
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
},
// Stylesheets
{
test: /\.p?css$/,
use: [
"vue-style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1,
},
},
{
loader: "postcss-loader",
options: {
// ident: "postcss",
postcssOptions: {
plugins: postcssPlugins,
},
},
},
],
},
// Images/files
{
test: /\.(png|jpg)$/i,
use: {
loader: "file-loader",
options: {
name: "[name]-[hash].[ext]",
},
},
},
// HTML
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
esModule: false,
...(process.env.NODE_ENV === "production" && {
minimize: true,
}),
},
},
],
},
],
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: "src/index.html",
files: {
css: ["./src/style/initial.pcss"],
js: ["./src/main.ts"],
},
}),
new webpack.EnvironmentPlugin(["API_URL"]),
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
})
],
resolve: {
extensions: [".ts", ".tsx", ".js", ".mjs", ".vue"],
alias: pathAliases,
roots: [path.resolve(__dirname, "src")],
},
};
if (process.env.NODE_ENV === "production") {
config = merge(config, {
mode: "production",
devtool: "source-map",
});
} else {
config = merge(config, {
mode: "development",
devtool: "eval",
devServer: {
allowedHosts: "all",
historyApiFallback: true,
port: 3002,
},
});
}
module.exports = config;

View file

@ -12,6 +12,11 @@ server {
# This is the address of the internal docker compose DNS server.
resolver 127.0.0.11;
proxy_pass $dashboard_upstream$uri$is_args$args;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
}
location /api {

10387
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -27,5 +27,8 @@
"shared",
"backend",
"dashboard"
]
],
"dependencies": {
"dotenv": "^16.5.0"
}
}