dashboard: use webpack for builds; use tailwindcss instead of bulma; all sorts of tweaks
This commit is contained in:
parent
028786d348
commit
577500af92
42 changed files with 4813 additions and 3174 deletions
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"plugins": [
|
||||
["transform-runtime", {
|
||||
"regenerator": true
|
||||
}],
|
||||
"transform-object-rest-spread"
|
||||
]
|
||||
}
|
6541
dashboard/package-lock.json
generated
6541
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,33 +4,45 @@
|
|||
"description": "",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rimraf dist && parcel build src/index.html --no-source-maps --out-dir dist",
|
||||
"build-with-report": "cross-env GENERATE_BUNDLE_SIZE_REPORT=1 npm run build",
|
||||
"build-debug": "rimraf dist && cross-env NODE_ENV=development parcel build src/index.html --no-minify --out-dir dist",
|
||||
"watch": "parcel src/index.html"
|
||||
"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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/component-compiler-utils": "^3.0.0",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"@babel/core": "^7.6.3",
|
||||
"@babel/preset-env": "^7.6.3",
|
||||
"@babel/preset-typescript": "^7.6.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"cross-env": "^5.2.0",
|
||||
"parcel-bundler": "^1.12.3",
|
||||
"parcel-plugin-bundle-visualiser": "git://github.com/Dragory/parcel-plugin-bundle-visualiser.git#explicit-env-var",
|
||||
"sass": "^1.21.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
"css-loader": "^3.2.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"dotenv-webpack": "^1.7.0",
|
||||
"file-loader": "^4.2.0",
|
||||
"html-loader": "^0.5.5",
|
||||
"html-webpack-plugin": "^4.0.0-beta.8",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-nesting": "^7.0.1",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"source-map-loader": "^0.2.4",
|
||||
"tailwindcss": "^1.1.2",
|
||||
"ts-loader": "^6.2.0",
|
||||
"vue-loader": "^15.7.1",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"webpack": "^4.41.0",
|
||||
"webpack-cli": "^3.3.9",
|
||||
"webpack-dev-server": "^3.8.2",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"buefy": "^0.7.10",
|
||||
"bulma": "^0.7.5",
|
||||
"bulmaswatch": "^0.7.2",
|
||||
"highlight.js": "^9.15.10",
|
||||
"js-cookie": "^2.2.0",
|
||||
"js-yaml": "^3.13.1",
|
||||
"marked": "^0.7.0",
|
||||
"modern-css-reset": "^1.0.4",
|
||||
"vue": "^2.6.10",
|
||||
"vue-highlightjs": "git://github.com/Dragory/vue-highlightjs.git#pass-hljs-instance",
|
||||
"vue-hot-reload-api": "^2.3.3",
|
||||
"vue-material-design-icons": "^4.1.0",
|
||||
"vue-router": "^3.0.6",
|
||||
"vue2-ace-editor": "0.0.14",
|
||||
"vuex": "^3.1.1"
|
||||
|
|
|
@ -11,7 +11,7 @@ const isAuthenticated = async () => {
|
|||
|
||||
export const authGuard: NavigationGuard = async (to, from, next) => {
|
||||
if (await isAuthenticated()) return next();
|
||||
next("/");
|
||||
window.location.href = `${process.env.API_URL}/auth/login`;
|
||||
};
|
||||
|
||||
export const loginCallbackGuard: NavigationGuard = async (to, from, next) => {
|
||||
|
@ -19,12 +19,7 @@ export const loginCallbackGuard: NavigationGuard = async (to, from, next) => {
|
|||
await RootStore.dispatch("auth/setApiKey", to.query.apiKey);
|
||||
next("/dashboard");
|
||||
} else {
|
||||
next({
|
||||
path: "/",
|
||||
query: {
|
||||
error: "noaccess",
|
||||
},
|
||||
});
|
||||
window.location.href = `/?error=noAccess`;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
<template>
|
||||
<router-view></router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
|
119
dashboard/src/components/Expandable.vue
Normal file
119
dashboard/src/components/Expandable.vue
Normal file
|
@ -0,0 +1,119 @@
|
|||
<template>
|
||||
<div class="expandable mb-4 bg-gray-800 border border-gray-600 rounded overflow-hidden" ref="root" v-bind:class="{'shadow-xl': isOpen}">
|
||||
<div role="button" class="title p-2" v-on:click="toggle">
|
||||
<chevron-down class="icon" v-bind:class="{'icon-open': isOpen}" />
|
||||
<span class="title-text"><slot name="title"></slot></span>
|
||||
</div>
|
||||
<div class="content border-t border-gray-700" ref="content">
|
||||
<div class="p-4 pb-0">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:root {
|
||||
--animation-time: 400ms;
|
||||
--target-height: auto;
|
||||
}
|
||||
|
||||
.expandable {
|
||||
transition: box-shadow var(--animation-time);
|
||||
}
|
||||
|
||||
.title {
|
||||
&:hover {
|
||||
& .title-text {
|
||||
@apply underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
transition: transform var(--animation-time);
|
||||
transform-origin: 50% 60%;
|
||||
}
|
||||
|
||||
.icon-open {
|
||||
transform: rotate(179deg);
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes open {
|
||||
0% { height: 0; }
|
||||
100% { height: var(--target-height); }
|
||||
}
|
||||
|
||||
@keyframes close {
|
||||
100% { height: 0; }
|
||||
0% { height: var(--target-height); }
|
||||
}
|
||||
|
||||
.opening {
|
||||
animation: open var(--animation-time) ease-in-out;
|
||||
}
|
||||
|
||||
.closing {
|
||||
animation: close var(--animation-time) ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="ts">
|
||||
import ChevronDown from 'vue-material-design-icons/ChevronDown.vue';
|
||||
|
||||
const ANIMATION_TIME = 400;
|
||||
|
||||
export default {
|
||||
components: { ChevronDown },
|
||||
mounted() {
|
||||
this.$refs.root.style.setProperty('--animation-time', `${ANIMATION_TIME}ms`);
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
animating: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.isOpen) this.close();
|
||||
else this.open();
|
||||
},
|
||||
open() {
|
||||
if (this.animating) return;
|
||||
this.animating = true;
|
||||
this.isOpen = true;
|
||||
|
||||
this.$refs.content.style.display = 'block';
|
||||
const targetHeight = this.$refs.content.clientHeight;
|
||||
this.$refs.content.style.setProperty('--target-height', `${targetHeight}px`);
|
||||
this.$refs.content.classList.add('opening');
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.content.classList.remove('opening');
|
||||
this.animating = false;
|
||||
}, ANIMATION_TIME);
|
||||
},
|
||||
close() {
|
||||
if (this.animating) return;
|
||||
this.animating = true;
|
||||
this.isOpen = false;
|
||||
|
||||
const targetHeight = this.$refs.content.clientHeight;
|
||||
this.$refs.content.style.setProperty('--target-height', `${targetHeight}px`);
|
||||
this.$refs.content.classList.add('closing');
|
||||
|
||||
setTimeout(() => {
|
||||
this.$refs.content.classList.remove('closing');
|
||||
this.$refs.content.style.display = 'none';
|
||||
this.animating = false;
|
||||
}, ANIMATION_TIME);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1,35 +0,0 @@
|
|||
<template>
|
||||
<div class="splash">
|
||||
<div class="wrapper">
|
||||
<div class="logo-column">
|
||||
<img class="logo" src="../img/logo.png" alt="Zeppelin Logo">
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<h1>Zeppelin</h1>
|
||||
<div class="description">
|
||||
Zeppelin is a private moderation bot for Discord, designed with large servers and reliability in mind.
|
||||
</div>
|
||||
<div class="actions">
|
||||
<router-link class="btn" to="/login">Dashboard</router-link>
|
||||
<router-link class="btn" to="/docs">Documentation</router-link>
|
||||
</div>
|
||||
<div class="error" v-if="error">
|
||||
<strong>Error</strong>
|
||||
<div v-if="error === 'noaccess'">No access</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "../style/splash.scss";
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
error() {
|
||||
return this.$route.query.error;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
11
dashboard/src/components/Tab.vue
Normal file
11
dashboard/src/components/Tab.vue
Normal file
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<li class="py-2 px-4 hover:text-gray-200" :class="{'pb-1 border-b border-gray-400 text-gray-200': active, 'text-gray-500': !active}">
|
||||
<slot></slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
props: ["active"],
|
||||
};
|
||||
</script>
|
5
dashboard/src/components/Tabs.vue
Normal file
5
dashboard/src/components/Tabs.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<ul class="list-none flex border-b border-gray-600 mb-4">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
|
@ -20,6 +20,15 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ace_editor {
|
||||
box-shadow: 0 2px 16px -4px #0000009e;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #181818;
|
||||
margin: 16px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import {mapState} from "vuex";
|
||||
import {ApiError} from "../../api";
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</style>
|
||||
|
||||
<script>
|
||||
import "../../style/dashboard.scss";
|
||||
// import "../../style/dashboard.scss";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
|
|
|
@ -1,83 +1,83 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Argument Types</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Argument Types</h1>
|
||||
<p>
|
||||
This page details the different argument types available for commands.
|
||||
</p>
|
||||
|
||||
<h2 id="string" class="z-title is-2 mt-2 mb-1">string</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="string">string</h2>
|
||||
<p>
|
||||
Any text
|
||||
</p>
|
||||
|
||||
<h2 id="number" class="z-title is-2 mt-2 mb-1">number</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="number">number</h2>
|
||||
<p>
|
||||
Any number
|
||||
</p>
|
||||
|
||||
<h2 id="user" class="z-title is-2 mt-2 mb-1">user</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="user">user</h2>
|
||||
<p>
|
||||
Anything that uniquely identifies a user. This includes:
|
||||
</p>
|
||||
<ul class="z-list z-ul mb-1">
|
||||
<ul>
|
||||
<li>User ID <code>108552944961454080</code></li>
|
||||
<li>User mention <code>@Dark#1010</code></li>
|
||||
<li>Loose user mention <code>Dark#1010</code></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="userId" class="z-title is-2 mt-2 mb-1">userId</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="userId">userId</h2>
|
||||
<p>
|
||||
A valid user ID, e.g. <code>108552944961454080</code>
|
||||
</p>
|
||||
|
||||
<h2 id="channel" class="z-title is-2 mt-2 mb-1">channel</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="channel">channel</h2>
|
||||
<p>
|
||||
Anything that uniquely identifies a channel. This includes:
|
||||
</p>
|
||||
<ul class="z-list z-ul mb-1">
|
||||
<ul>
|
||||
<li>Channel ID <code>473087035574321152</code></li>
|
||||
<li>Channel mention <code>#my-channel</code></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="channelId" class="z-title is-2 mt-2 mb-1">channelId</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="channelId">channelId</h2>
|
||||
<p>
|
||||
A valid channel ID, e.g. <code>473087035574321152</code>
|
||||
</p>
|
||||
|
||||
<h2 id="role" class="z-title is-2 mt-2 mb-1">role</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="role">role</h2>
|
||||
<p>
|
||||
Anything that uniquely identifies a role. This includes:
|
||||
</p>
|
||||
<ul class="z-list z-ul mb-1">
|
||||
<li>Role ID <code>473085927053590538</code></li>
|
||||
<ul>
|
||||
<li>Role ID <code class="inline-code">473085927053590538</code></li>
|
||||
<li>Role mention <code>@MyRole</code></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="member" class="z-title is-2 mt-2 mb-1">member</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="member">member</h2>
|
||||
<p>
|
||||
Anything that uniquely identifies a member currently on the server. This includes:
|
||||
</p>
|
||||
<ul class="z-list z-ul mb-1">
|
||||
<ul>
|
||||
<li>User ID <code>108552944961454080</code></li>
|
||||
<li>User Mention <code>@Dark#1010</code></li>
|
||||
<li>Loose user mention <code>Dark#1010</code></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="resolvedMember" class="z-title is-2 mt-2 mb-1">resolvedMember</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="resolvedMember">resolvedMember</h2>
|
||||
<p>
|
||||
See <code>member</code> above
|
||||
</p>
|
||||
|
||||
<h2 id="delay" class="z-title is-2 mt-2 mb-1">delay</h2>
|
||||
<p class="mb-1">
|
||||
<h2 id="delay">delay</h2>
|
||||
<p>
|
||||
A delay is used to specify an amount of time. It uses simple letters to specify time durations.<br>
|
||||
For example, <code>2d15h27m3s</code> would be 2 days, 15 hours, 27 minutes and 3 seconds.
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Note that the delay should always be written as 1 word, without spaces!
|
||||
</p>
|
||||
|
||||
<b-collapse :open="false" class="card mb-1">
|
||||
<div :open="false" class="card mb-1"> <!-- b-collapse -->
|
||||
<div slot="trigger" slot-scope="props" class="card-header" role="button">
|
||||
<p class="card-header-title">Additional Information</p>
|
||||
<a class="card-header-icon">
|
||||
|
@ -103,7 +103,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
<template>
|
||||
<pre class="codeblock" v-highlightjs><code :class="lang" v-trim-code="trim"><slot></slot></code></pre>
|
||||
<pre class="codeblock" v-highlightjs><code :class="lang" v-trim-indents="trim"><slot></slot></code></pre>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.codeblock {
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
max-width: 970px; /* FIXME: temp fix for overflowing code blocks, look into properly later */
|
||||
}
|
||||
|
||||
.hljs {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["lang", "trim"],
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Configuration format</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Configuration format</h1>
|
||||
<p>
|
||||
This is the basic format of the bot configuration for a guild. The basic breakdown is:
|
||||
</p>
|
||||
<ol class="z-list mb-1">
|
||||
|
||||
<ul>
|
||||
<li>Prefix (i.e. what character is preceding each command)</li>
|
||||
<li>Permission levels (see <router-link to="/docs/permissions">Permissions</router-link> for more info)</li>
|
||||
<li>Plugin-specific configuration (see <router-link to="/docs/plugin-configuration">Plugin configuration</router-link> for more info)</li>
|
||||
</ol>
|
||||
<CodeBlock lang="yaml" trim="4">
|
||||
</ul>
|
||||
|
||||
<CodeBlock lang="yaml" trim="start">
|
||||
prefix: "!"
|
||||
|
||||
# role id: level
|
||||
|
@ -33,8 +35,8 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeBlock from "./CodeBlock";
|
||||
<script lang="ts">
|
||||
import CodeBlock from "./CodeBlock.vue";
|
||||
|
||||
export default {
|
||||
components: { CodeBlock },
|
||||
|
|
|
@ -1,168 +1,134 @@
|
|||
<template>
|
||||
<div class="docs docs-cloak">
|
||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<div class="navbar-item">
|
||||
<img class="docs-logo" src="../../img/logo.png" alt="" aria-hidden="true">
|
||||
<h1 class="docs-title">Zeppelin Documentation</h1>
|
||||
</div>
|
||||
<div class="docs">
|
||||
<div class="container mx-auto px-4 py-2">
|
||||
<!-- 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="../../img/logo.png" alt="" aria-hidden="true">
|
||||
<h1 class="flex-auto">Zeppelin Documentation</h1>
|
||||
</div>
|
||||
<div class="flex-1 flex items-center justify-end">
|
||||
<router-link
|
||||
to="/dashboard"
|
||||
role="menuitem"
|
||||
class="py-1 px-2 rounded hover:bg-gray-700">
|
||||
Go to dashboard
|
||||
</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="navbar-menu is-active">
|
||||
<div class="navbar-end">
|
||||
<router-link to="/dashboard" class="navbar-item">Go to dashboard</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<!-- WIP bar -->
|
||||
<div class="mt-6 px-3 py-2 rounded bg-gray-800 shadow-md">
|
||||
<i class="mdi mdi-alert mr-1" title="Note"></i>
|
||||
This documentation is a work in progress.
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="wip-bar">
|
||||
<i class="mdi mdi-alert"></i>
|
||||
<strong>Note!</strong> This documentation is a work in progress.
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="docs-sidebar">
|
||||
<div class="docs-sidebar-content">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">General</p>
|
||||
<ul class="menu-list">
|
||||
<li><router-link to="/docs/introduction">Introduction</router-link></li>
|
||||
<li><router-link to="/docs/configuration-format">Configuration format</router-link></li>
|
||||
<li><router-link to="/docs/plugin-configuration">Plugin configuration</router-link></li>
|
||||
<li><router-link to="/docs/permissions">Permissions</router-link></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Reference</p>
|
||||
<ul class="menu-list">
|
||||
<li><router-link to="/docs/reference/argument-types">Argument types</router-link></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Setup guides</p>
|
||||
<ul class="menu-list">
|
||||
<li><router-link to="/docs/setup-guides/logs">Logs</router-link></li>
|
||||
<li><router-link to="/docs/setup-guides/moderation">Moderation</router-link></li>
|
||||
</ul>
|
||||
|
||||
<p class="menu-label">Plugins</p>
|
||||
<ul class="menu-list">
|
||||
<li v-for="plugin in plugins">
|
||||
<router-link :to="'/docs/plugins/' + plugin.name">{{ plugin.info.prettyName || plugin.name }}</router-link>
|
||||
<!-- Content wrapper -->
|
||||
<div class="flex items-start mt-8">
|
||||
<!-- Sidebar -->
|
||||
<nav class="docs-sidebar flex-none px-4 pt-2 pb-3 mr-8 border border-gray-700 rounded bg-gray-800 shadow-md">
|
||||
<div role="none" v-for="(group, index) in menu">
|
||||
<h1 class="font-bold" :aria-owns="'menu-group-' + index" :class="{'mt-4': index !== 0}">{{ group.label }}</h1>
|
||||
<ul v-bind:id="'menu-group-' + index" role="group" class="list-none pl-2">
|
||||
<li role="none" v-for="item in group.items">
|
||||
<router-link role="menuitem" :to="item.to" class="text-gray-300 hover:text-gray-500">{{ item.label }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="docs-content flex-auto overflow-x-hidden">
|
||||
<router-view :key="$route.fullPath"></router-view>
|
||||
</div>
|
||||
</div>
|
||||
<div class="docs-main">
|
||||
<router-view :key="$route.fullPath"></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.docs-cloak {
|
||||
/* Replaced by "visible" in docs.scss */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.docs {
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
border: 1px solid #4e5d6c;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 24px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.docs-logo {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.docs-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wip-bar {
|
||||
padding: 4px 10px;
|
||||
margin-bottom: 24px;
|
||||
background-color: #2B3E50;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.wip-bar i {
|
||||
color: #fdd7a5;
|
||||
font-size: 24px;
|
||||
vertical-align: -3px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.docs-sidebar {
|
||||
flex: 0 0 280px;
|
||||
}
|
||||
|
||||
.docs-sidebar-content {
|
||||
/* can't scroll with a long list before reaching the end of the page, figure out */
|
||||
/*position: sticky;*/
|
||||
/*top: 20px;*/
|
||||
}
|
||||
|
||||
.docs-sidebar .menu {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #4e5d6c;
|
||||
background-color: #2b3e50;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.docs-sidebar .menu-label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-main {
|
||||
flex: 1 1 100%;
|
||||
padding: 0 24px 24px;
|
||||
}
|
||||
|
||||
.docs-main >>> h4 {
|
||||
margin-top: 1.25em; /* ? */
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import hljs from "highlight.js/lib/highlight.js";
|
||||
import hljsYaml from "highlight.js/lib/languages/yaml.js";
|
||||
import VueHighlightJS from "vue-highlightjs";
|
||||
import Buefy from "buefy";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
import "../../style/icons.scss";
|
||||
import "buefy/dist/buefy.css";
|
||||
import "highlight.js/styles/ocean.css";
|
||||
import "../../directives/trim-code";
|
||||
import "../../style/docs.scss";
|
||||
type TMenuItem = {
|
||||
to: string;
|
||||
label: string;
|
||||
};
|
||||
type TMenuGroup = {
|
||||
label: string;
|
||||
items: TMenuItem[];
|
||||
};
|
||||
type TMenu = TMenuGroup[];
|
||||
|
||||
hljs.registerLanguage("yaml", hljsYaml);
|
||||
Vue.use(VueHighlightJS, { hljs });
|
||||
Vue.use(Buefy);
|
||||
const menu: TMenu = [
|
||||
{
|
||||
label: 'General',
|
||||
items: [
|
||||
{
|
||||
to: '/docs/introduction',
|
||||
label: 'Introduction',
|
||||
},
|
||||
{
|
||||
to: '/docs/configuration-format',
|
||||
label: 'Configuration format',
|
||||
},
|
||||
{
|
||||
to: '/docs/plugin-configuration',
|
||||
label: 'Plugin configuration',
|
||||
},
|
||||
{
|
||||
to: '/docs/permissions',
|
||||
label: 'Permissions',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Reference',
|
||||
items: [
|
||||
{
|
||||
to: '/docs/reference/argument-types',
|
||||
label: 'Argument types',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Setup guides',
|
||||
items: [
|
||||
{
|
||||
to: '/docs/setup-guides/logs',
|
||||
label: 'Logs',
|
||||
},
|
||||
{
|
||||
to: '/docs/setup-guides/moderation',
|
||||
label: 'Moderation',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
async mounted() {
|
||||
await this.$store.dispatch("docs/loadAllPlugins");
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState('docs', {
|
||||
plugins: 'allPlugins',
|
||||
}),
|
||||
menu() {
|
||||
return [
|
||||
...menu,
|
||||
{
|
||||
label: 'Plugins',
|
||||
items: this.plugins.map(plugin => ({
|
||||
label: plugin.info.prettyName || plugin.name,
|
||||
to: `/docs/plugins/${plugin.name}`,
|
||||
})),
|
||||
}
|
||||
];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Introduction</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Introduction</h1>
|
||||
<p>
|
||||
Zeppelin is a private moderation bot for Discord, designed with large servers and reliability in mind.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Getting the bot</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Getting the bot</h2>
|
||||
<p>
|
||||
Since the bot is currently private, access to the bot is granted on a case by case basis.<br>
|
||||
There are plans to streamline this process in the future.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Configuration</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Configuration</h2>
|
||||
<p>
|
||||
All Zeppelin configuration is done through the dashboard by editing a YAML config file. By default, only the server
|
||||
owner has access to this, but they can give other users access as they see fit. See <router-link to="/docs/configuration-format">Configuration format</router-link> for more details.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Plugins</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Plugins</h2>
|
||||
<p>
|
||||
Zeppelin is divided into plugins: grouped functionality that can be enabled/disabled as needed, and that have their own configurations.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Commands</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Commands</h2>
|
||||
<p>
|
||||
The commands for each plugin are listed on the plugin's page (see "Plugins" on the menu). On these pages, the command prefix is assumed to be <code>!</code> but this can be changed on a per-server basis.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Permissions</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Permissions</h1>
|
||||
<p>
|
||||
Permissions in Zeppelin are simply values in plugin configuration that are checked when the command is used.
|
||||
These values can be changed with overrides (see <router-link to="/docs/plugin-configuration">Plugin configuration</router-link> for more info)
|
||||
and can depend on e.g. user id, role id, channel id, category id, or <strong>permission level</strong>.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Permission levels</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Permission levels</h2>
|
||||
<p>
|
||||
The simplest way to control access to bot commands and features is via permission levels.
|
||||
These levels are simply a number (usually between 0 and 100), based on the user's roles or user id, that can then
|
||||
be used in permission overrides. By default, several commands are "moderator only" (level 50 and up) or "admin only" (level 100 and up).
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Additionally, having a higher permission level means that certain commands (such as !ban) can't be used against
|
||||
you by users with a lower or equal permission level (so e.g. moderators can't ban each other or admins above them).
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Permission levels are defined in the config in the <strong>levels</strong> section. For example:
|
||||
</p>
|
||||
|
||||
|
@ -28,14 +28,14 @@
|
|||
"172950000412655616": 50 # Example mod
|
||||
</CodeBlock>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Examples</h2>
|
||||
<h2>Examples</h2>
|
||||
|
||||
<h3 class="z-title is-3 mb-1">Basic overrides</h3>
|
||||
<p class="mb-1">
|
||||
<h3>Basic overrides</h3>
|
||||
<p>
|
||||
For this example, let's assume we have a plugin called <code>cats</code> which has a command <code>!cat</code> locked behind the permission <code>can_cat</code>.
|
||||
Let's say that by default, the plugin allows anyone to use <code>!cat</code>, but we want to restrict it to moderators only.
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Here's what the configuration for this would look like:
|
||||
</p>
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
|||
can_cat: true
|
||||
</CodeBlock>
|
||||
|
||||
<h3 class="z-title is-3 mt-2 mb-1">Replacing defaults</h3>
|
||||
<h3>Replacing defaults</h3>
|
||||
<p class="mb-1">
|
||||
In this example, let's assume you don't want to use the default permission levels of 50 and 100 for mods and admins respectively.
|
||||
Let's say you're using various incremental levels instead: 10, 20, 30, 40, 50...<br>
|
||||
|
|
|
@ -3,21 +3,19 @@
|
|||
Loading...
|
||||
</div>
|
||||
<div v-else>
|
||||
<h1 class="z-title is-1 mb-1">{{ data.info.prettyName || data.name }}</h1>
|
||||
<h1>{{ data.info.prettyName || data.name }}</h1>
|
||||
|
||||
<!-- Description -->
|
||||
<MarkdownBlock :content="data.info.description" class="content"></MarkdownBlock>
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<li v-bind:class="{'is-active': tab === 'usage'}">
|
||||
<router-link v-bind:to="'/docs/plugins/' + pluginName + '/usage'">Usage</router-link>
|
||||
</li>
|
||||
<li v-bind:class="{'is-active': tab === 'configuration'}">
|
||||
<router-link v-bind:to="'/docs/plugins/' + pluginName + '/configuration'">Configuration</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<Tabs>
|
||||
<Tab :active="tab === 'usage'">
|
||||
<router-link v-bind:to="'/docs/plugins/' + pluginName + '/usage'">Usage</router-link>
|
||||
</Tab>
|
||||
<Tab :active="tab === 'configuration'">
|
||||
<router-link v-bind:to="'/docs/plugins/' + pluginName + '/configuration'">Configuration</router-link>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<!-- Usage tab -->
|
||||
<div class="usage" v-if="tab === 'usage'">
|
||||
|
@ -28,38 +26,33 @@
|
|||
|
||||
<!-- Usage guide -->
|
||||
<div v-if="data.info.usageGuide">
|
||||
<h2 id="usage-guide" class="z-title is-2 mt-2 mb-1">Usage guide</h2>
|
||||
<h2 id="usage-guide">Usage guide</h2>
|
||||
<MarkdownBlock :content="data.info.usageGuide" class="content"></MarkdownBlock>
|
||||
</div>
|
||||
|
||||
<!-- Command list -->
|
||||
<div v-if="data.commands.length">
|
||||
<h2 id="commands" class="z-title is-2 mt-2 mb-1">Commands</h2>
|
||||
<div v-for="command in data.commands">
|
||||
<h3 class="z-title is-3 mt-2 mb-1">!{{ command.trigger }}</h3>
|
||||
<h2 id="commands">Commands</h2>
|
||||
<div v-for="command in data.commands" class="mb-4">
|
||||
<h3 class="text-xl">!{{ command.trigger }}</h3>
|
||||
<div v-if="command.config.extra.requiredPermission">
|
||||
Permission: <code>{{ command.config.extra.requiredPermission }}</code>
|
||||
Permission: <code class="inline-code">{{ command.config.extra.requiredPermission }}</code>
|
||||
</div>
|
||||
<div v-if="command.config.extra.info && command.config.extra.info.basicUsage">
|
||||
Basic usage: <code>{{ command.config.extra.info.basicUsage }}</code>
|
||||
Basic usage: <code class="inline-code">{{ command.config.extra.info.basicUsage }}</code>
|
||||
</div>
|
||||
<div v-if="command.config.aliases && command.config.aliases.length">
|
||||
Shortcut:
|
||||
<code style="margin-right: 4px" v-for="alias in command.config.aliases">!{{ alias }}</code>
|
||||
<code class="inline-code" style="margin-right: 4px" v-for="alias in command.config.aliases">!{{ alias }}</code>
|
||||
</div>
|
||||
|
||||
<MarkdownBlock v-if="command.config.info && command.config.info.description" :content="command.config.info.description" class="content mt-1 mb-1"></MarkdownBlock>
|
||||
<MarkdownBlock v-if="command.config.info && command.config.info.description" :content="command.config.info.description" class="content mb-4"></MarkdownBlock>
|
||||
|
||||
<b-collapse :open="false" class="card mt-1 mb-1">
|
||||
<div slot="trigger" slot-scope="props" class="card-header" role="button">
|
||||
<p class="card-header-title">Additional information</p>
|
||||
<a class="card-header-icon">
|
||||
<b-icon :icon="props.open ? 'menu-down' : 'menu-up'"></b-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<Expandable class="mt-4">
|
||||
<template v-slot:title>Additional information</template>
|
||||
<template v-slot:content>
|
||||
Signatures:
|
||||
<ul class="z-list z-ul">
|
||||
<ul>
|
||||
<li>
|
||||
<code>
|
||||
!{{ command.trigger }}
|
||||
|
@ -70,7 +63,7 @@
|
|||
|
||||
<div class="mt-2" v-if="command.parameters.length">
|
||||
Command arguments:
|
||||
<ul class="z-list z-ul">
|
||||
<ul>
|
||||
<li v-for="param in command.parameters">
|
||||
<code>{{ renderParameter(param) }}</code>
|
||||
<router-link :to="'/docs/reference/argument-types#' + (param.type || 'string')">{{ param.type || 'string' }}</router-link>
|
||||
|
@ -95,8 +88,8 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</template>
|
||||
</Expandable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -104,26 +97,26 @@
|
|||
<!-- Configuration tab -->
|
||||
<div class="configuration" v-if="tab === 'configuration'">
|
||||
<!-- Basic config info -->
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Name in config: <code>{{ data.name }}</code>
|
||||
</p>
|
||||
<p class="mt-1 mb-1">
|
||||
<p>
|
||||
To enable this plugin with default configuration, add <code>{{ data.name }}: {}</code> to the <code>plugins</code> list in config
|
||||
</p>
|
||||
|
||||
<!-- Configuration guide -->
|
||||
<div v-if="data.info.configurationGuide">
|
||||
<h2 id="configuration-guide" class="z-title is-2 mt-2 mb-1">Configuration guide</h2>
|
||||
<h2 id="configuration-guide">Configuration guide</h2>
|
||||
<MarkdownBlock :content="data.info.configurationGuide" class="content"></MarkdownBlock>
|
||||
</div>
|
||||
|
||||
<!-- Default configuration -->
|
||||
<h2 id="default-configuration" class="z-title is-2 mt-2 mb-1">Default configuration</h2>
|
||||
<h2 id="default-configuration">Default configuration</h2>
|
||||
<CodeBlock lang="yaml">{{ renderConfiguration(data.defaultOptions) }}</CodeBlock>
|
||||
|
||||
<!-- Config schema -->
|
||||
<h2 id="config-schema" class="z-title is-2 mt-2 mb-1">Config schema</h2>
|
||||
<b-collapse :open="false" class="card mt-1 mb-1">
|
||||
<h2 id="config-schema">Config schema</h2>
|
||||
<div :open="false" class="card mt-1 mb-1"> <!-- b-collapse -->
|
||||
<div slot="trigger" slot-scope="props" class="card-header" role="button">
|
||||
<p class="card-header-title">Click to expand</p>
|
||||
<a class="card-header-icon">
|
||||
|
@ -133,23 +126,27 @@
|
|||
<div class="card-content">
|
||||
<CodeBlock lang="plain">{{ data.configSchema }}</CodeBlock>
|
||||
</div>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import Vue from "vue";
|
||||
import {mapState} from "vuex";
|
||||
import yaml from "js-yaml";
|
||||
import CodeBlock from "./CodeBlock";
|
||||
import MarkdownBlock from "./MarkdownBlock";
|
||||
import CodeBlock from "./CodeBlock.vue";
|
||||
import MarkdownBlock from "./MarkdownBlock.vue";
|
||||
import Tabs from "../Tabs.vue";
|
||||
import Tab from "../Tab.vue";
|
||||
import Expandable from "../Expandable.vue";
|
||||
import { DocsState } from "../../store/types";
|
||||
|
||||
const validTabs = ['usage', 'configuration'];
|
||||
const defaultTab = 'usage';
|
||||
|
||||
export default {
|
||||
components: { CodeBlock, MarkdownBlock },
|
||||
components: { CodeBlock, MarkdownBlock, Tabs, Tab, Expandable },
|
||||
|
||||
async mounted() {
|
||||
this.loading = true;
|
||||
|
@ -201,7 +198,7 @@
|
|||
},
|
||||
computed: {
|
||||
...mapState("docs", {
|
||||
data(state) {
|
||||
data(state: DocsState) {
|
||||
return state.plugins[this.pluginName];
|
||||
},
|
||||
hasUsageInfo() {
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Plugin configuration</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Plugin configuration</h1>
|
||||
<p>
|
||||
Each plugin in Zeppelin has its own configuration options. In the config editor, you can both set the default config
|
||||
and overrides based on specific conditions. Permissions are also just values in the plugin's config, and thus follow
|
||||
the same rules with overrides etc. as other options (see <router-link to="/docs/permissions">Permissions</router-link> for more info).
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Information about each plugin's options can be found on the plugin's page on the sidebar. See <router-link to="/docs/configuration-format">Configuration format</router-link> for an example of a full config.
|
||||
</p>
|
||||
|
||||
<h2 class="z-title is-2 mt-2 mb-1">Overrides</h2>
|
||||
<p class="mb-1">
|
||||
<h2>Overrides</h2>
|
||||
<p>
|
||||
Overrides are the primary mechanism of changing options and permissions based on permission levels, roles, channels, user ids, etc.
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<p>
|
||||
Here's an example demonstrating different types of overrides:
|
||||
</p>
|
||||
|
||||
|
@ -74,8 +74,8 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CodeBlock from "./CodeBlock";
|
||||
<script lang="ts">
|
||||
import CodeBlock from "./CodeBlock.vue";
|
||||
|
||||
export default {
|
||||
components: { CodeBlock },
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="z-title is-1 mb-1">Work in progress</h1>
|
||||
<p class="mb-1">
|
||||
<h1>Work in progress</h1>
|
||||
<p>
|
||||
This page is a work in progress.
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import Vue from "vue";
|
||||
|
||||
Vue.directive("trim-code", {
|
||||
bind(el, binding) {
|
||||
el.innerHTML = el.innerHTML
|
||||
.replace(/(^\n+|\n+$)/g, "")
|
||||
.split("\n")
|
||||
.map(line => line.slice(binding.value))
|
||||
.join("\n");
|
||||
},
|
||||
});
|
25
dashboard/src/directives/trim-indents.ts
Normal file
25
dashboard/src/directives/trim-indents.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Vue from "vue";
|
||||
|
||||
Vue.directive("trim-indents", {
|
||||
bind(el, binding) {
|
||||
const withoutStartEndWhitespace = el.innerHTML.replace(/(^\n+|\n+$)/g, "");
|
||||
|
||||
const mode = binding.value != null ? binding.value : "start";
|
||||
|
||||
let spacesToTrim;
|
||||
if (mode === "start") {
|
||||
const match = withoutStartEndWhitespace.match(/^\s+/);
|
||||
spacesToTrim = match ? match[0].length : 0;
|
||||
} else if (mode === "end") {
|
||||
const match = withoutStartEndWhitespace.match(/\s+$/);
|
||||
spacesToTrim = match ? match[0].length : 0;
|
||||
} else {
|
||||
spacesToTrim = parseInt(mode, 10);
|
||||
}
|
||||
|
||||
el.innerHTML = withoutStartEndWhitespace
|
||||
.split("\n")
|
||||
.map(line => line.slice(spacesToTrim))
|
||||
.join("\n");
|
||||
},
|
||||
});
|
|
@ -8,8 +8,11 @@
|
|||
<title>Zeppelin Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<noscript>
|
||||
<h1>Zeppelin</h1>
|
||||
The Zeppelin dashboard requires JavaScript to load.
|
||||
</noscript>
|
||||
|
||||
<script src="./main.ts"></script>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
40
dashboard/src/init-vue.ts
Normal file
40
dashboard/src/init-vue.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import "./style/app.pcss";
|
||||
|
||||
import Vue from "vue";
|
||||
|
||||
import hljs from "highlight.js/lib/highlight.js";
|
||||
import hljsYaml from "highlight.js/lib/languages/yaml.js";
|
||||
import VueHighlightJS from "vue-highlightjs";
|
||||
import "highlight.js/styles/ocean.css";
|
||||
|
||||
import { RootStore } from "./store";
|
||||
import { router } from "./routes";
|
||||
|
||||
import "./directives/trim-indents";
|
||||
|
||||
import App from "./components/App.vue";
|
||||
|
||||
// Set up a read-only global variable to access specific env vars
|
||||
Vue.mixin({
|
||||
data() {
|
||||
return {
|
||||
get env() {
|
||||
return Object.freeze({
|
||||
API_URL: process.env.API_URL,
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
hljs.registerLanguage("yaml", hljsYaml);
|
||||
Vue.use(VueHighlightJS, { hljs });
|
||||
|
||||
const app = new Vue({
|
||||
router,
|
||||
store: RootStore,
|
||||
el: "#app",
|
||||
render(h) {
|
||||
return h(App);
|
||||
},
|
||||
});
|
|
@ -1,29 +1,32 @@
|
|||
import "./style/base.scss";
|
||||
import "./style/initial.pcss";
|
||||
const splashHtml = require("./splash.html");
|
||||
|
||||
import Vue from "vue";
|
||||
import { RootStore } from "./store";
|
||||
import { router } from "./routes";
|
||||
if (window.location.pathname !== "/") {
|
||||
import("./init-vue");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
document.querySelector("#app").innerHTML = splashHtml;
|
||||
|
||||
// Set up a read-only global variable to access specific env vars
|
||||
Vue.mixin({
|
||||
data() {
|
||||
return {
|
||||
get env() {
|
||||
return Object.freeze({
|
||||
API_URL: process.env.API_URL,
|
||||
});
|
||||
},
|
||||
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.",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
import App from "./components/App.vue";
|
||||
|
||||
const app = new Vue({
|
||||
router,
|
||||
store: RootStore,
|
||||
el: "#app",
|
||||
render(h) {
|
||||
return h(App);
|
||||
},
|
||||
});
|
||||
const errorMessageElem = document.createElement("div");
|
||||
errorMessageElem.classList.add("message");
|
||||
errorMessageElem.innerText = errorMessages[queryParams.error] || "Unexpected error";
|
||||
errorElement.appendChild(errorMessageElem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Vue from "vue";
|
||||
import VueRouter, { RouteConfig } from "vue-router";
|
||||
import Splash from "./components/Splash.vue";
|
||||
import { authGuard, authRedirectGuard, loginCallbackGuard } from "./auth";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
@ -8,7 +7,6 @@ Vue.use(VueRouter);
|
|||
export const router = new VueRouter({
|
||||
mode: "history",
|
||||
routes: [
|
||||
{ path: "/", component: Splash },
|
||||
{ path: "/login", beforeEnter: authRedirectGuard },
|
||||
{ path: "/login-callback", beforeEnter: loginCallbackGuard },
|
||||
|
||||
|
|
18
dashboard/src/splash.html
Normal file
18
dashboard/src/splash.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<div class="splash">
|
||||
<div id="error"></div>
|
||||
<div class="wrapper">
|
||||
<div class="logo-column">
|
||||
<img class="logo" src="./img/logo.png" alt="Zeppelin Logo">
|
||||
</div>
|
||||
<div class="info-column">
|
||||
<h1>Zeppelin</h1>
|
||||
<div class="description">
|
||||
Zeppelin is a private moderation bot for Discord, designed with large servers and reliability in mind.
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a class="btn" href="/login">Dashboard</a>
|
||||
<a class="btn" href="/docs">Documentation</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
18
dashboard/src/style/app.pcss
Normal file
18
dashboard/src/style/app.pcss
Normal file
|
@ -0,0 +1,18 @@
|
|||
@import "~tailwindcss/base.css";
|
||||
@import "~tailwindcss/components.css";
|
||||
@import "~tailwindcss/utilities.css";
|
||||
|
||||
@import "~vue-material-design-icons/styles.css";
|
||||
|
||||
@import "components.pcss";
|
||||
|
||||
@import "docs.pcss";
|
||||
|
||||
body {
|
||||
overflow-y: scroll;
|
||||
|
||||
@apply bg-gray-900;
|
||||
@apply text-gray-300;
|
||||
@apply text-base;
|
||||
@apply p-4;
|
||||
}
|
4
dashboard/src/style/base.pcss
Normal file
4
dashboard/src/style/base.pcss
Normal file
|
@ -0,0 +1,4 @@
|
|||
body {
|
||||
font: normal 18px/1.5 sans-serif;
|
||||
font-family: system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,400,600&display=swap');
|
||||
@import "~bulma/sass/base/minireset";
|
||||
|
||||
body {
|
||||
font: normal 16px/1.4 'Open Sans', sans-serif;
|
||||
}
|
21
dashboard/src/style/components.pcss
Normal file
21
dashboard/src/style/components.pcss
Normal file
|
@ -0,0 +1,21 @@
|
|||
.inline-code {
|
||||
@apply inline-block;
|
||||
@apply bg-gray-800;
|
||||
@apply px-1;
|
||||
@apply rounded;
|
||||
@apply text-sm;
|
||||
}
|
||||
|
||||
.codeblock {
|
||||
@apply bg-gray-800;
|
||||
@apply p-3;
|
||||
@apply mb-4;
|
||||
@apply rounded;
|
||||
@apply text-sm;
|
||||
@apply shadow-md;
|
||||
|
||||
& .hljs {
|
||||
@apply bg-transparent;
|
||||
@apply p-0;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,78 @@
|
|||
@import "~buefy/dist/buefy.css";
|
||||
@import "~highlight.js/styles/ocean.css";
|
||||
|
||||
$bulmaswatch-import-font: false;
|
||||
$family-primary: 'Open Sans', sans-serif;
|
||||
$list-background-color: transparent;
|
||||
|
||||
$size-1: 2.5rem;
|
||||
$size-2: 2rem;
|
||||
$size-3: 1.5rem;
|
||||
$size-4: 1.25rem;
|
||||
|
||||
@import "~bulmaswatch/superhero/_variables";
|
||||
|
||||
$tabs-link-color: $grey-light;
|
||||
$tabs-link-active-color: $grey-lighter;
|
||||
$tabs-link-active-border-bottom-color: $grey-lighter;
|
||||
|
||||
@import "~bulma/bulma";
|
||||
@import "~bulmaswatch/superhero/_overrides";
|
||||
|
||||
.dashboard-cloak {
|
||||
|
||||
.init-cloak {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.z-title {
|
||||
line-height: 1.125;
|
||||
|
||||
&.is-1 { font-size: $size-1; }
|
||||
&.is-2 { font-size: $size-2; }
|
||||
&.is-3 { font-size: $size-3; }
|
||||
&.is-4 { font-size: $size-4; }
|
||||
&.is-5 { font-size: $size-5; }
|
||||
&.is-6 { font-size: $size-6; }
|
||||
}
|
||||
|
||||
.z-list {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.z-ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.mt-1 { margin-top: 1rem; }
|
||||
.mt-2 { margin-top: 1.5rem; }
|
||||
.mt-3 { margin-top: 2rem; }
|
||||
|
||||
.mb-1 { margin-bottom: 1rem; }
|
||||
.mb-2 { margin-bottom: 1.5rem; }
|
||||
.mb-3 { margin-bottom: 2rem; }
|
||||
|
||||
.codeblock,
|
||||
.content .codeblock {
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
max-width: 970px; /* FIXME: temp fix for overflowing code blocks, look into properly later */
|
||||
}
|
||||
|
||||
.codeblock .hljs {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
&:not(:first-child) {
|
||||
margin-top: 1.4em;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-list .router-link-active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
60
dashboard/src/style/docs.pcss
Normal file
60
dashboard/src/style/docs.pcss
Normal file
|
@ -0,0 +1,60 @@
|
|||
.docs-sidebar {
|
||||
& .router-link-active {
|
||||
@apply underline;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-content {
|
||||
& h1 {
|
||||
@apply text-5xl;
|
||||
@apply font-semibold;
|
||||
@apply leading-none;
|
||||
@apply pb-4;
|
||||
}
|
||||
|
||||
& h2 {
|
||||
@apply text-2xl;
|
||||
@apply font-semibold;
|
||||
@apply pt-2;
|
||||
@apply pb-1;
|
||||
}
|
||||
|
||||
& h3 {
|
||||
@apply font-semibold;
|
||||
@apply pb-1;
|
||||
}
|
||||
|
||||
& p {
|
||||
@apply pb-4;
|
||||
|
||||
& code {
|
||||
@apply inline-code;
|
||||
}
|
||||
}
|
||||
|
||||
& a:not([class]) {
|
||||
@apply text-blue-400;
|
||||
@apply underline;
|
||||
|
||||
&:hover {
|
||||
@apply text-blue-200;
|
||||
}
|
||||
}
|
||||
|
||||
& ul:not([class]) {
|
||||
@apply list-disc;
|
||||
@apply mb-4;
|
||||
|
||||
& li {
|
||||
@apply ml-6;
|
||||
}
|
||||
|
||||
& code {
|
||||
@apply inline-code;
|
||||
}
|
||||
}
|
||||
|
||||
& .expandable {
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
@import url('https://cdn.materialdesignicons.com/2.5.94/css/materialdesignicons.min.css');
|
||||
|
||||
$bulmaswatch-import-font: false;
|
||||
$family-primary: 'Open Sans', sans-serif;
|
||||
$list-background-color: transparent;
|
||||
|
||||
$size-1: 2.5rem;
|
||||
$size-2: 2rem;
|
||||
$size-3: 1.5rem;
|
||||
$size-4: 1.25rem;
|
||||
|
||||
@import "~bulmaswatch/superhero/_variables";
|
||||
|
||||
$tabs-link-color: $grey-light;
|
||||
$tabs-link-active-color: $grey-lighter;
|
||||
$tabs-link-active-border-bottom-color: $grey-lighter;
|
||||
|
||||
@import "~bulma/bulma";
|
||||
@import "~bulmaswatch/superhero/_overrides";
|
||||
|
||||
|
||||
.docs-cloak {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.z-title {
|
||||
line-height: 1.125;
|
||||
|
||||
&.is-1 { font-size: $size-1; }
|
||||
&.is-2 { font-size: $size-2; }
|
||||
&.is-3 { font-size: $size-3; }
|
||||
&.is-4 { font-size: $size-4; }
|
||||
&.is-5 { font-size: $size-5; }
|
||||
&.is-6 { font-size: $size-6; }
|
||||
}
|
||||
|
||||
.z-list {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.z-ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.mt-1 { margin-top: 1rem; }
|
||||
.mt-2 { margin-top: 1.5rem; }
|
||||
.mt-3 { margin-top: 2rem; }
|
||||
|
||||
.mb-1 { margin-bottom: 1rem; }
|
||||
.mb-2 { margin-bottom: 1.5rem; }
|
||||
.mb-3 { margin-bottom: 2rem; }
|
||||
|
||||
.codeblock,
|
||||
.content .codeblock {
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
max-width: 970px; /* FIXME: temp fix for overflowing code blocks, look into properly later */
|
||||
}
|
||||
|
||||
.codeblock .hljs {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
&:not(:first-child) {
|
||||
margin-top: 1.4em;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-list .router-link-active {
|
||||
text-decoration: underline;
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@import url("https://cdn.materialdesignicons.com/2.5.94/css/materialdesignicons.min.css");
|
3
dashboard/src/style/initial.pcss
Normal file
3
dashboard/src/style/initial.pcss
Normal file
|
@ -0,0 +1,3 @@
|
|||
@import "./reset.pcss";
|
||||
@import "./base.pcss";
|
||||
@import "./splash.pcss";
|
48
dashboard/src/style/reset.pcss
Normal file
48
dashboard/src/style/reset.pcss
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* 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;
|
||||
}
|
||||
}
|
|
@ -1,58 +1,82 @@
|
|||
.splash {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
padding: 16px;
|
||||
|
||||
background-color: #7289da;
|
||||
background-image: linear-gradient(225deg, #7289da 0%, #5d70b4 100%);
|
||||
color: #fff;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
& a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
flex: 0 1 750px;
|
||||
& > #error {
|
||||
width: 100%;
|
||||
max-width: 750px;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
&.has-error {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& .message {
|
||||
flex: 0 1 auto;
|
||||
text-align: left;
|
||||
padding: 8px 12px;
|
||||
background-color: #404040;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 3px 12px -2px hsla(0, 0%, 0%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
& .wrapper {
|
||||
flex: 0 0 auto;
|
||||
|
||||
width: 750px;
|
||||
max-width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: start;
|
||||
align-items: flex-start;
|
||||
|
||||
.logo-column {
|
||||
& .logo-column {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.info-column {
|
||||
& .info-column {
|
||||
flex: 1 1 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
& .logo {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin-right: 64px;
|
||||
margin-right: 48px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
& h1 {
|
||||
font-size: 80px;
|
||||
font-weight: 300;
|
||||
margin-top: 40px
|
||||
}
|
||||
|
||||
.description {
|
||||
& .description {
|
||||
color: #f1f5ff;
|
||||
}
|
||||
|
||||
.actions {
|
||||
& .actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 8px;
|
||||
margin-left: -12px; // Negative button margin
|
||||
margin-left: -12px;
|
||||
|
||||
.btn {
|
||||
& .btn {
|
||||
margin: 12px;
|
||||
text-decoration: none;
|
||||
padding: 8px 24px;
|
||||
|
@ -73,7 +97,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
& .error {
|
||||
margin-top: 8px;
|
||||
background-color: hsl(224, 52%, 32%);
|
||||
padding: 12px;
|
7
dashboard/tailwind.config.js
Normal file
7
dashboard/tailwind.config.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {},
|
||||
plugins: []
|
||||
}
|
|
@ -2,28 +2,20 @@
|
|||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"target": "esnext",
|
||||
"strict": true,
|
||||
"strict": false,
|
||||
"lib": [
|
||||
"es2017",
|
||||
"esnext",
|
||||
"dom"
|
||||
],
|
||||
"baseUrl": "./",
|
||||
"baseUrl": ".",
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.vue"
|
||||
],
|
||||
"files": [
|
||||
"ts-vue-shim.d.ts"
|
||||
]
|
||||
"allowJs": true
|
||||
}
|
||||
}
|
||||
|
|
169
dashboard/webpack.config.js
Normal file
169
dashboard/webpack.config.js
Normal file
|
@ -0,0 +1,169 @@
|
|||
require('dotenv').config();
|
||||
|
||||
const path = require('path');
|
||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const DotenvPlugin = require('dotenv-webpack');
|
||||
const merge = require('webpack-merge');
|
||||
|
||||
const targetDir = path.normalize(path.join(__dirname, 'dist'));
|
||||
|
||||
if (! process.env.NODE_ENV) {
|
||||
console.error('Please set NODE_ENV');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const babelOpts = {
|
||||
presets: [
|
||||
'@babel/preset-env',
|
||||
],
|
||||
};
|
||||
|
||||
let config = {
|
||||
entry: './src/main.ts',
|
||||
output: {
|
||||
filename: '[name].[hash].js',
|
||||
path: targetDir,
|
||||
publicPath: '/',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
// Vue / Babel / Typescript
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: ["vue-loader"],
|
||||
},
|
||||
{
|
||||
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",
|
||||
plugins: loader => {
|
||||
const plugins = [
|
||||
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") {
|
||||
plugins.push(
|
||||
require('postcss-preset-env')(),
|
||||
require('cssnano')(),
|
||||
);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Images/files
|
||||
{
|
||||
test: /\.(png|jpg)$/i,
|
||||
use: ["file-loader"],
|
||||
},
|
||||
|
||||
// HTML
|
||||
{
|
||||
test: /\.html$/,
|
||||
use: [
|
||||
{
|
||||
loader: "html-loader",
|
||||
options: {
|
||||
root: path.resolve(__dirname, 'src'),
|
||||
attrs: ['img:src', 'link:href'],
|
||||
...(process.env.NODE_ENV === 'production' && {
|
||||
minimize: true,
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.html',
|
||||
files: {
|
||||
"css": ["./src/style/initial.pcss"],
|
||||
"js": ["./src/main.ts"],
|
||||
},
|
||||
}),
|
||||
new DotenvPlugin(),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.mjs', '.vue'],
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
config = merge(config, {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
});
|
||||
} else {
|
||||
config = merge(config, {
|
||||
mode: 'development',
|
||||
devtool: 'eval',
|
||||
devServer: {
|
||||
...(process.env.DEV_HOST ? { host: process.env.DEV_HOST } : undefined),
|
||||
historyApiFallback: true,
|
||||
port: 1234,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = config;
|
|
@ -117,7 +117,7 @@ export function initAuth(app: express.Express) {
|
|||
if (req.user && req.user.apiKey) {
|
||||
res.redirect(`${process.env.DASHBOARD_URL}/login-callback/?apiKey=${req.user.apiKey}`);
|
||||
} else {
|
||||
res.redirect(`${process.env.DASHBOARD_URL}/login-callback/?error=noaccess`);
|
||||
res.redirect(`${process.env.DASHBOARD_URL}/login-callback/?error=noAccess`);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue