initial commit

This commit is contained in:
maurice fletgen
2022-09-08 09:18:04 +02:00
commit 447b2fb51d
163 changed files with 47569 additions and 0 deletions

1
web/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

16
web/README.md Normal file
View File

@ -0,0 +1,16 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
## Type Support For `.vue` Imports in TS
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).

13
web/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/frankenbot.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Frankenbot</title>
</head>
<body class="min-h-screen">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

2776
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
web/package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "my-vue-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"chart.js": "^3.9.1",
"daisyui": "^2.24.0",
"vue": "^3.2.37",
"vue-chartjs": "^4.1.1",
"vue-router": "^4.1.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.3",
"autoprefixer": "^10.4.8",
"postcss": "^8.4.16",
"tailwindcss": "^3.1.8",
"typescript": "^4.6.4",
"vite": "^3.0.7",
"vue-tsc": "^0.39.5"
}
}

6
web/postcss.config.cjs Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

BIN
web/public/frankenbot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

1
web/public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

21
web/src/App.vue Normal file
View File

@ -0,0 +1,21 @@
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import { ref, computed } from 'vue';
import Navbar from './components/parts/Navbar.vue'
import Footer from './components/parts/Footer.vue';
// check if a jwt is expired with date time
// huge issue: users can chenge durations of their tokens manually
</script>
<template>
<Navbar></Navbar>
<router-view></router-view>
<Footer></Footer>
</template>
<style scoped>
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

1
web/src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1,33 @@
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import Linechart from './parts/Linechart.vue';
import Sidebar from './parts/sidebar.vue';
export default defineComponent ({
setup() {
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
return {};
},
components: { Linechart, Sidebar }
})
</script>
<template>
<div class="row">
<div class="col-2">
<Sidebar></Sidebar>
</div>
<div class="col-10"></div>
</div>
</template>
<style scoped>
input {
margin-top: 10px;
width: 100%;
}
</style>

View File

@ -0,0 +1,63 @@
<script lang="ts">
</script>
<template>
<div class="hero bg-neutral text-neutral-content">
<div class="hero-content flex-col lg:flex-row">
<img src="../assets/frankenbot.png" class="max-w-sm rounded-lg" />
<div>
<h1 class="text-5xl font-bold">Awake your Servers Potential</h1>
<p class="py-6">Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.</p>
<button class="btn btn-primary">Get Started</button>
</div>
</div>
</div>
<br>
<div class="hero ">
<div class="hero-content flex-col lg:flex-row">
<img src="../assets/frankenbot.png" class="max-w-sm rounded-lg shadow-2xl" />
<div>
<h1 class="text-5xl font-bold">Box Office News!</h1>
<p class="py-6">Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.</p>
<button class="btn btn-primary">Get Started</button>
</div>
</div>
</div>
<br>
<div class="hero">
<div class="hero-content flex-col lg:flex-row-reverse">
<img src="../assets/frankenbot.png" class="max-w-sm rounded-lg shadow-2xl" />
<div>
<h1 class="text-5xl font-bold">Box Office News!</h1>
<p class="py-6">Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.</p>
<button class="btn btn-primary">Get Started</button>
</div>
</div>
</div>
<br>
<div class="hero">
<div class="hero-content flex-col lg:flex-row">
<img src="../assets/frankenbot.png" class="max-w-sm rounded-lg shadow-2xl" />
<div>
<h1 class="text-5xl font-bold">Box Office News!</h1>
<p class="py-6">Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.</p>
<button class="btn btn-primary">Get Started</button>
</div>
</div>
</div>
<br>
<div class="hero">
<div class="hero-content flex-col lg:flex-row-reverse">
<img src="../assets/frankenbot.png" class="max-w-sm rounded-lg shadow-2xl" />
<div>
<h1 class="text-5xl font-bold">Box Office News!</h1>
<p class="py-6">Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.</p>
<button class="btn btn-primary">Get Started</button>
</div>
</div>
</div>
<br>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,72 @@
<script lang="ts">
import { defineComponent, reactive } from 'vue';
export default defineComponent ({
setup() {
const data = reactive({username: "", password:""})
const error = reactive({html: ""});
function login(){
console.log(data.username);
console.log(data.password);
const requestOptions = {
method: "POST",
body: JSON.stringify({username:data.username, password:data.password})
}
fetch("http://localhost:3000/server/auth", requestOptions)
.then(response => {
response.json().then(values => {
console.log(values)
let data = JSON.parse(values)
if(data.type == "success") {
localStorage.setItem("bearer", "bearer " + data.token);
document.location.href = "/dash";
}
})
})
.then(data => {console.log(data)})
}
return {
data,
login,
}
}
})
</script>
<template>
<br>
<div class="centered">
<div class="card flex-shrink-0 w-full max-w-sm shadow-2xl bg-base-100">
<div class="card-body">
<div class="form-control">
<label class="label">
<span style="color: white;" class="label-text">Username</span>
</label>
<input type="text" placeholder="username" class="input input-bordered w-full max-w-xs" v-model="data.username" />
</div>
<div class="form-control">
<label class="label">
<span style="color: white;" class="label-text">Password</span>
</label>
<input type="password" placeholder="password" class="input input-bordered w-full max-w-xs" v-model="data.password" />
<label class="label">
<a href="#" style="color: white;" class="label-text-alt link link-hover">Forgot password?</a>
</label>
</div>
<div class="form-control mt-6">
<button @click="login" class="btn btn-primary">Login</button>
</div>
</div>
</div>
</div>
<br>
</template>
<style scoped>
input {
margin-top: 10px;
width: 100%;
}
</style>

View File

@ -0,0 +1,74 @@
<script lang="ts">
import { defineComponent, onMounted, reactive, Ref, ref } from "vue";
import Sidebar from "../parts/sidebar.vue";
import Log from "./parts/log.vue";
export default defineComponent({
setup() {
const requestHeaders: HeadersInit = new Headers();
const bearer = localStorage.getItem("bearer");
if (bearer !== null) {
requestHeaders.set("Authorization", bearer);
}
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
type Log = {
id: number,
type: string,
commiter: string,
description: string
}
const values:Ref<Log[]> = ref([]);
onMounted(async () => {
let resp = await fetch("http://localhost:3000/logger?serverid="+localStorage.getItem("server")+"&commiterid=steev", {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
let data = await resp.json();
data.forEach((element: any) => {
let topush = element;
values.value.push(topush)
});
});
return {values};
},
components: { Sidebar, Log}
});
</script>
<template>
<div class="row">
<div class="col-2" >
<Sidebar></Sidebar>
</div>
<div class="col-10" style="padding: 1rem;">
<h1>Server Logs for </h1>
<div class="overflow-x-auto" style="height: 45rem; overflow-y: scroll; width: 100%" >
<table class="table table-compact w-full">
<thead>
<tr>
<th>Type</th>
<th>Commiter</th>
<th>Description</th>
</tr>
</thead>
<tbody id="logBody">
<Log v-for="log in values" :key="log.id" :type="log.type" :commiter="log.commiter" :description="log.description" ></Log>
</tbody>
</table>
</div>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,217 @@
<script lang="ts">
import { defineComponent, onMounted, ref, Ref } from "vue";
import Sidebar from "../parts/sidebar.vue";
import actionnode from "./parts/actions.vue";
export default defineComponent({
setup() {
const requestHeaders: HeadersInit = new Headers();
const bearer:string | null = localStorage.getItem("bearer");
const display:Ref<boolean> = ref(false);
if (bearer !== null) {
requestHeaders.set("Authorization", bearer);
}
requestHeaders.set("Content-Type", "application/json");
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
type Action = {
refid: string,
target: string,
type: string,
duration: string,
commiter: string,
reason: string,
temp: boolean
}
const values:Ref<Action[]> = ref([]);
const rid:Ref<string> = ref("");
const editorAction:Ref<Action> = ref();
function showEditor(action:Action) {
console.log(action.refid);
editorAction.value = action;
console.log(editorAction.value);
//TODO: PUT data back to API once edited
display.value=true;
}
function updateAction(){
let requestData:BodyInit = JSON.stringify(editorAction.value);
console.log(requestData);
let resp = fetch("http://localhost:3000/server/actions", {
method: "PUT",
credentials: "same-origin",
headers: requestHeaders,
body: requestData
});
}
function deleteAction(){
let data = {
serverid: "test",
comiterid: "steev",
refid: editorAction.value.refid,
}
let resp = fetch("http://localhost:3000/server/actions", {
method: "DELETE",
credentials: "same-origin",
headers: requestHeaders,
body: JSON.stringify(data)
});
}
async function fetchActions(){
let resp = await fetch("http://localhost:3000/server/actions?serverid="+localStorage.getItem("server")+"&commiterid=steev", {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
values.value = [];
let data = await resp.json();
data.forEach((element: { refid: string; target: string; type: string; duration: string; commiter: string; reason: string; temp: boolean}) => {
let data = element;
data["temp"] = (element.duration != "infinite");
values.value.push(data)
});
}
onMounted(() => {
fetchActions();
})
async function refLookup(){
console.log(rid.value)
let resp = await fetch("http://localhost:3000/server/actions/search?serverid="+localStorage.getItem("server")+"&commiterid=steev&refid="+rid.value, {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
values.value = [];
let data = await resp.json();
data.forEach((element: { refid: string; target: string; type: string; duration: string; commiter: string; reason: string; temp:boolean; }) => {
let data = element;
data["temp"] = (element.duration != "infinite");
values.value.push(data)
});
}
return {values, refLookup, fetchActions, showEditor, updateAction, deleteAction, rid, display, editorAction};
},
components: { Sidebar,actionnode }
});
</script>
<template>
<div class="row">
<div class="col-2" >
<Sidebar></Sidebar>
</div>
<div class="col-10">
<!-- Action Editor -->
<div class="row" v-if="display">
<div class="col-2"></div>
<div class="col-8">
<div class="card-body items-center text-center">
<h2 class="card-title">Action Editor!</h2>
<div class="card-actions justify-end">
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Action RefID</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" disabled v-model="editorAction.refid"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Action Type</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorAction.type"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Action Target</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorAction.target"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Action Duration</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorAction.duration"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Creator</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" disabled v-model="editorAction.commiter"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Reason</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorAction.reason"/>
</div>
<button class="btn btn-success" @click="updateAction">Accept</button>
<button class="btn btn-secondary" @click="deleteAction">Delete</button>
<button class="btn btn-primary" @click="display=false;">Hide</button>
</div>
</div>
</div>
<div class="col-2"></div>
</div>
<div style="padding: 1rem">
<div class="overflow-x-auto" style="height: 50rem; overflow-y: scroll; width: 100%">
<div class="form-control">
<div class="input-group">
<input type="text" placeholder="RefID here" class="input input-bordered" v-model="rid"/>
<button class="btn btn-square" @click="refLookup">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
</button>
<button @click="fetchActions" style="color:white;" class="btn btn-info">Reload</button>
</div>
</div>
<hr style="margin: 10px 0 10px;">
<table class="table table-compact w-full">
<thead>
<tr>
<th>refid</th>
<th>target</th>
<th>type</th>
<th>duration</th>
<th>Commiter</th>
<th>reason</th>
<th>temporary</th>
</tr>
</thead>
<tbody id="logBody">
<actionnode v-for="action in values" :key="action.refid" @click="showEditor(action)"
:refid="action?.refid"
:type="action?.type"
:target="action?.target"
:duration="action?.duration"
:commiter="action?.commiter"
:reason="action?.reason"
:istemp="action?.temp"
></actionnode>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>
<style scoped> </style>

View File

@ -0,0 +1,39 @@
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
const display = false;
name: 'actionnode'
return {display};
},
props:['refid', 'type', 'target','duration','commiter','reason','istemp'],
});
</script>
<template>
<tr>
<td>{{refid}}</td>
<td>{{target}}</td>
<td>{{type}}</td>
<td>{{duration}}</td>
<td>{{commiter}}</td>
<td>{{reason}}</td>
<td>
<div class="form-control">
<label class="cursor-pointer label">
<span v-if="istemp">
<input type="checkbox" disabled checked class="checkbox checkbox-accent" />
</span>
<span v-else>
<input type="checkbox" disabled class="checkbox checkbox-accent" />
</span>
</label>
</div>
</td>
</tr>
</template>
<style scoped></style>

View File

@ -0,0 +1,21 @@
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
name: 'lognode'
return {};
},
props:['type', 'commiter','description'],
});
</script>
<template>
<tr>
<td>{{type}}</td>
<td>{{commiter}}</td>
<td>{{description}}</td>
</tr>
</template>
<style scoped></style>

View File

@ -0,0 +1,26 @@
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
name: 'usernode'
return {};
},
props:['userid', 'writer', 'message'],
});
</script>
<template>
<li class="flex justify-start" v-if="userid!=writer">
<div class="bg-success relative max-w-xl px-4 py-2 text-gray-700 rounded shadow">
<span class="block">{{message}}</span>
</div>
</li>
<li class="flex justify-end" v-else>
<div class=" bg-primary relative max-w-xl px-4 py-2 text-gray-700 rounded shadow">
<span style="color: white;" class="block">{{message}}</span>
</div>
</li>
</template>
<style scoped></style>

View File

@ -0,0 +1,25 @@
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
const display = false;
name: 'supportticket'
return {display};
},
props:['refid', 'creator', 'request','status'],
});
</script>
<template>
<tr>
<td>{{refid}}</td>
<td>{{creator}}</td>
<td>{{request}}</td>
<td>{{status}}</td>
</tr>
</template>
<style scoped></style>

View File

@ -0,0 +1,25 @@
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
setup() {
name: 'usernode'
return {};
},
props:['userid', 'username', 'joined','level','xp','msgs'],
});
</script>
<template>
<tr>
<td>{{userid}}</td>
<td>{{username}}</td>
<td>{{joined}}</td>
<td>{{level}}</td>
<td>{{xp}}</td>
<td>{{msgs}}</td>
</tr>
</template>
<style scoped></style>

View File

@ -0,0 +1,547 @@
<script lang="ts">
import { defineComponent, onMounted, onUpdated, ref, Ref } from "vue";
import Sidebar from "../parts/sidebar.vue";
import actionnode from "./parts/actions.vue";
export default defineComponent({
setup() {
const requestHeaders: HeadersInit = new Headers();
const bearer = localStorage.getItem("bearer");
if (bearer !== null) {
requestHeaders.set("Authorization", bearer);
}
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
type Settings = {
serverid: string,
servername: string,
ownerid: string,
commiterid: string,
apitoken: string | undefined,
loggerchannel: string | undefined,
spamchannel: string | undefined,
greetingchannel: string | undefined,
announcechannel: string | undefined,
statspagemode: string,
greetingtype: string,
greetings: boolean,
stats: boolean,
statspage: boolean,
statsprivate: boolean,
music: boolean,
twitchannounce: boolean,
twitteranounce: boolean,
moderation: boolean,
automod: boolean,
logger: boolean,
spamprotect: boolean,
linkprotect: boolean,
blacklist: boolean,
apitoggle: boolean
}
const settingsData: Ref<Settings> = ref({});
async function updateSettings(){
if (settingsData.value["commiterid"] == undefined) settingsData.value["commiterid"] = "steev";
if (settingsData.value["statsprivate"] == undefined) settingsData.value["statsprivate"] = false;
if (settingsData.value["greetingchannel"] == undefined) settingsData.value["greetingchannel"] = "undefined";
if (settingsData.value["spamchannel"] == undefined) settingsData.value["spamchannel"] = "undefined";
if (settingsData.value["announcechannel"] == undefined) settingsData.value["announcechannel"] = "undefined";
if (settingsData.value["apitoggle"] == undefined) settingsData.value["apitoggle"] = false;
console.log(settingsData.value)
let resp = await fetch("http://localhost:3000/server/settings", {
method: "PUT",
credentials: "same-origin",
headers: requestHeaders,
body: JSON.stringify(settingsData.value)
});
}
onMounted(async () => {
// TODO: Fetch settings from database
// Load data into variable
let resp = await fetch("http://localhost:3000/server/settings?serverid="+localStorage.getItem("server")+"&commiterid=steev", {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
let data = await resp.json();
console.log(data[0]);
if(data[0]["serverid"] == undefined) {settingsData.value.serverid = "";} else {settingsData.value.serverid = data[0]["serverid"];}
if(data[0]["servername"] == undefined) {settingsData.value.servername = "";} else {settingsData.value.servername = data[0]["servername"];}
if(data[0]["ownerid"] == undefined) {settingsData.value.ownerid = "";} else {settingsData.value.ownerid = data[0]["ownerid"];}
if(data[0]["apitoken"] == undefined) {settingsData.value.apitoken = "";} else {settingsData.value.apitoken = data[0]["apitoken"];}
if(data[0]["loggerchannel"] == undefined) {settingsData.value.loggerchannel = "";} else {settingsData.value.loggerchannel = data[0]["loggerchannel"];}
if(data[0]["spamchannel"] == undefined) {settingsData.value.spamchannel = "";} else {settingsData.value.spamchannel = data[0]["spamchannel"];}
if(data[0]["announcechannel"] == undefined) {settingsData.value.announcechannel = "";} else {settingsData.value.announcechannel = data[0]["announcechannel"];}
if(data[0]["statspagemode"] == undefined) {settingsData.value.statspagemode = "";} else {settingsData.value.statspagemode = data[0]["statspagemode"];}
if(data[0]["greetermode"] == undefined) {settingsData.value.greetingtype = "";} else {settingsData.value.greetingtype = data[0]["greetermode"];}
if(data[0]["greetings"] == undefined) {settingsData.value.greetings = false;} else {settingsData.value.greetings = data[0]["greetings"];}
if(data[0]["stats"] == undefined) {settingsData.value.stats = false;} else {settingsData.value.stats = data[0]["stats"];}
if(data[0]["statspage"] == undefined) {settingsData.value.statspage = false;} else {settingsData.value.statspage = data[0]["statspage"];}
if(data[0]["music"] == undefined) {settingsData.value.music = false;} else {settingsData.value.music = data[0]["music"];}
if(data[0]["twitchannounce"] == undefined) {settingsData.value.twitchannounce = false;} else {settingsData.value.twitchannounce = data[0]["twitchannounce"];}
if(data[0]["twitterannounce"] == undefined) {settingsData.value.twitteranounce = false;} else {settingsData.value.twitteranounce = data[0]["twitterannounce"];}
if(data[0]["moderation"] == undefined) {settingsData.value.moderation = false;} else {settingsData.value.moderation = data[0]["moderation"];}
if(data[0]["automatedmoderation"] == undefined) {settingsData.value.automod = false;} else {settingsData.value.automod = data[0]["automatedmoderation"];}
if(data[0]["logger"] == undefined) {settingsData.value.logger = false;} else {settingsData.value.logger = data[0]["logger"];}
if(data[0]["spamprotection"] == undefined) {settingsData.value.spamprotect = false;} else {settingsData.value.spamprotect = data[0]["spamprotection"];}
if(data[0]["linkprotection"] == undefined) {settingsData.value.linkprotect = false;} else {settingsData.value.linkprotect = data[0]["linkprotection"];}
if(data[0]["wordfilter"] == undefined) {settingsData.value.blacklist = false;} else {settingsData.value.blacklist = data[0]["wordfilter"];}
if(data[0]["apitoggle"] == undefined) {settingsData.value.apitoggle = false;} else {settingsData.value.apitoggle = data[0]["apitoggle"];}
if(data[0]["greeterchannel"] == undefined) {settingsData.value.greetingchannel = "";} else {settingsData.value.greetingchannel = data[0]["greeterchannel"];}
});
return {settingsData, updateSettings};
},
components: { Sidebar,actionnode }
});
</script>
<template>
<div class="row">
<div class="col-2" >
<Sidebar></Sidebar>
</div>
<div class="col-10">
<div class="row">
<div class="col-1"></div>
<div class="col-10">
<br>
<h1 class="text-center">General Input</h1>
<div class="flex">
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">ServerID</span>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.serverid" disabled/>
</div>
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Server Name</span>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.servername" disabled/>
</div>
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">OwnerID</span>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.ownerid" disabled/>
</div>
</div>
<div class="flex">
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">API Token</span>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.apitoken" disabled/>
</div>
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Logger Channel</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Logger Channel?</h2>
<p>This Setting sets where the bot will send Event logs on your Discord Server<br><br>
type a Channel name to set a channel
</p>
</div>
</div>
</div>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.loggerchannel" />
</div>
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Spam Channel</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Spam Channel?</h2>
<p>This Setting sets where the bot will allow commands and send his spam message<br><br>
type a Channel name to set a channel
</p>
</div>
</div>
</div>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.spamchannel" />
</div>
</div>
<div class="flex">
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Greet Channel </span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Greet Channel?</h2>
<p>This Setting sets where the bot will send his hello message<br><br>
type Private if you want the message to be send as dm to the user or a Channel name to set a channel
</p>
</div>
</div>
</div>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.greetingchannel" />
</div>
<div style="margin-right: 15px;" class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Announcement Channel</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Announcement Channel?</h2>
<p>This Setting sets where the bot will Announce things<br><br>
type a Channel name to set a channel
</p>
</div>
</div>
</div>
</label>
<input v-on:change="updateSettings" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="settingsData.announcechannel" />
</div>
</div>
<br>
<br>
<h1 class="text-center">Mode Selection</h1>
<div class="flex">
<label style="margin-right: 15px;">
<span>Statspage Mode </span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Statspage Mode?</h2>
<p>This Setting Sets the behaviour of your Server own Stats Page if enabled</p>
</div>
</div>
</div>
<select v-on:change="updateSettings" v-model="settingsData.statspagemode" class="select w-full max-w-xs">
<option value="" disabled selected>Statspage Mode</option>
<option value="default">Default</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</label>
<label style="margin-right: 15px;">
<span>Greeting Mode </span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Grettings Mode?</h2>
<p>This Setting Sets the behaviour of how the Bot will say hello to new users</p>
</div>
</div>
</div>
<select v-on:change="updateSettings" v-model="settingsData.greetingtype" class="select w-full max-w-xs">
<option value="" disabled selected>Gretting Mode</option>
<option value="image">Image</option>
<option value="message">Message</option>
<option value="both">Both</option>
</select>
</label>
</div>
<br>
<br>
<h1 class="text-center">Toggles</h1>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">User Greeting</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">User Grettings?</h2>
<p>This Setting Toggles if the Bot say hello to new users or not</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.greetings" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent"/>
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">User Statistics</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">User Statistics?</h2>
<p>This Setting toggles if the bot should keep track of certain user statistics or not<br><br>
this will enable [prefix]stats, [prefix]topten and the statspage
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.stats" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Stats Page</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Stats Page?</h2>
<p>This Setting toggles if the User statistics are showed on your servers specific stats page<br><br>
The statspage only works properly if stats are enabled
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.statspage" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Music Player</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Music Player?</h2>
<p>This Setting toggles if the can play music in a voice channel</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.music" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Twitch Announcement</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Twitch Announcement?</h2>
<p>This Setting toggles if the Anounces added twitch streams<br><br>
this settings only gets effective if twitch streamers are assigned on the <a href="">socialmedia page</a>
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.twitchannounce" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Twitter Announcement</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Twitter Announcement?</h2>
<p>This Setting toggles if the Anounces added twitter posts<br><br>
this settings only gets effective if twitter accounts are assigned on the <a href="">socialmedia page</a>
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.twitteranounce" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Moderation</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Moderation?</h2>
<p>This Setting toggles if the Moeration features for your Server<br><br>
this settings enables: Kick, Ban, Warn, Mute all of those as well temporary and is a dependency for automated moderation
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.moderation" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Automated Moderation</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Automated Moderation?</h2>
<p>This Setting allows Frankenbot to perform automated actions based on warnamounts</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.automod" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Server Logger</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Server Logger?</h2>
<p>This Settings toggles server based event logs <br><br>
this setting wont affect system based logs on the website and will only disable event coming from a discord server
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.logger" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Spam Protection</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Spampprotection?</h2>
<p>This Settings toggles if the bot should block duplicating messages<br><br> as of now no delay can be set for this</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.spamprotect" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Link Protection</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Linkprotection?</h2>
<p>This Settings toggles if the bot should block links<br><br> as of now there is no whitelist for this setting</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.linkprotect" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Word Filter</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">Wordfilter?</h2>
<p>This Settings toggles if the bot should block blacklisted words <br><br>
Frankenbot will as of now delete the entire message if this setting is enabled
</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.blacklist" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<div class="flex">
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">API Toggle</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-circle btn-ghost btn-xs text-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="w-4 h-4 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
</label>
<div tabindex="0" class="card compact dropdown-content shadow bg-base-100 rounded-box w-64">
<div class="card-body">
<h2 class="card-title">API Toggle?</h2>
<p>Enables or Disables an API</p>
</div>
</div>
</div>
<input v-on:change="updateSettings" v-model="settingsData.apitoggle" style="margin-left: 15px;" type="checkbox" class="toggle toggle-accent" />
</label>
</div>
</div>
<br>
</div>
<div class="col-1"></div>
</div>
</div>
</div>
</template>
<style scoped> </style>

View File

@ -0,0 +1,261 @@
<script lang="ts">
import { defineComponent, onMounted, ref, Ref } from "vue";
import Sidebar from "../parts/sidebar.vue";
import Supportticket from "./parts/support_ticket.vue";
import Messenger_message from "./parts/messenger_message.vue";
export default defineComponent({
setup() {
const devData = { userid: "steev", username: "steev" }
const requestHeaders: HeadersInit = new Headers();
const bearer = localStorage.getItem("bearer");
const message:Ref<String> = ref("");
if (bearer !== null) {
requestHeaders.set("Authorization", bearer);
}
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
type Ticket = {
refid: string,
serverid: string,
creator: string,
title: string,
description:string,
status:string
}
type Message = {
id: number,
refid: string,
writer: string,
message: string,
}
const tickets:Ref<Ticket[]> = ref([])
const Messages:Ref<Message[]> = ref([])
const activeTicket: Ref<Ticket> = ref({});
const readerDisplay:Ref<boolean> = ref(false);
const messengerDisplay:Ref<boolean> = ref(false);
const ws:Ref<WebSocket> = ref();
function readTicket(ticket:Ticket) {
activeTicket.value = ticket;
readerDisplay.value = true;
}
function closeAll(){
if (messengerDisplay.value) {
showMessenger();
}
readerDisplay.value = false;
}
// Open And Close Websocket Connection + fetch messages from API
async function showMessenger(){
if (messengerDisplay.value) {
ws.value.close();
messengerDisplay.value = false;
} else {
messengerDisplay.value = true;
let resp = await fetch("http://localhost:3000/server/support/message?refid=" + activeTicket.value.refid + "&commiterid=" + devData.userid, {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
ws.value = new WebSocket("ws://localhost:3000/ws?userid=" + devData.userid + "&channel=" + activeTicket.value.refid);
Messages.value = await resp.json();
// event Handling
ws.value.onmessage = msg => {
console.log(typeof(msg.data));
console.log(msg.data);
let msgData = JSON.parse(msg.data);
console.log(msgData["payload"]["userid"])
Messages.value.push({refid: activeTicket.value.refid, writer: msgData["payload"]["userid"], message: msgData["payload"]["msg"]})
}
}
}
async function postMessage(){
// Send Message to API
let resp = await fetch("http://localhost:3000/server/support/message?refid=" + activeTicket.value.refid + "&commiterid=" + devData.userid, {
method: "POST",
credentials: "same-origin",
headers: requestHeaders,
body: JSON.stringify({
refid: activeTicket.value.refid,
message: message.value,
writer: devData.userid,
rtcchannel: activeTicket.value.refid,
isread: false,
type: "website"
})
});
Messages.value.push({refid: activeTicket.value.refid, writer: devData.userid, message: message.value})
// Send Message to RTC
ws.value.send(JSON.stringify({
command:"msg",
payload:{
name: devData.username,
context: "ticket-" + activeTicket.value.refid,
type: "default",
userid: devData.userid,
msg: message.value
}
}));
}
async function closeTicket(){
activeTicket.value.status = "closed";
activeTicket.value.commiterid = devData.userid;
let resp = await fetch("http://localhost:3000/server/support", {
method: "PUT",
credentials: "same-origin",
headers: requestHeaders,
body: JSON.stringify(activeTicket.value)
});
}
onMounted(async () => {
let resp = await fetch("http://localhost:3000/server/support?serverid=" + localStorage.getItem("server") + "&commiterid=" + devData.userid, {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
tickets.value = await resp.json();
});
return {tickets, readTicket, message, devData, activeTicket, readerDisplay, messengerDisplay, showMessenger, Messages, postMessage, closeAll, closeTicket};
},
components: { Sidebar, Supportticket, Messenger_message }
});
</script>
<template>
<div class="row">
<div class="col-2" >
<Sidebar></Sidebar>
</div>
<div class="col-10">
<!-- Action Editor -->
<div class="row">
<div class="col-1"></div>
<div class="col-10">
<div class="card-body items-center" v-if="readerDisplay">
<h2 class="card-title">Ticket Reader!</h2>
<div class="card-actions">
<div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">RefID</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="activeTicket.refid" disabled />
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Creator</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="activeTicket.creator" disabled />
</div>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Ticket Request</span>
</label>
<textarea class="textarea textarea-ghost" placeholder="Ticket Content..." disabled rows="4">{{activeTicket.description}}</textarea>
</div>
<div>
<button style="margin-right: 15px;" @click="showMessenger" class="btn btn-success" >Reply</button>
<button style="margin-right: 15px;" @click="closeAll" class="btn btn-secondary" >Close</button>
<button style="margin-right: 15px;" @click="closeTicket" class="btn btn-danger" >Close Ticket</button>
</div>
</div>
</div>
<div class="card" v-if="messengerDisplay">
<br>
<h1 class="card-title">Ticket Reader!</h1>
<div class="alert alert-info shadow-lg">
<div>
<span><u><b>{{activeTicket.title}}</b></u><br>{{activeTicket.description}}</span>
</div>
</div>
<br>
<hr>
<br>
<div class="relative w-full p-6 overflow-y-auto h-[20rem]" style="overflow-y: scroll;">
<ul class="space-y-2">
<!-- add message component here -->
<Messenger_message v-for="message in Messages" :key="message.id"
:userid="devData.userid"
:writer="message.writer"
:message="message.message"
></Messenger_message>
</ul>
</div>
<div class="flex items-center justify-between w-full p-3 border-t border-gray-300">
<input type="text" placeholder="Message" v-model="message"
class="block w-full py-2 pl-4 mx-3 bg-gray-100 rounded-full outline-none focus:text-gray-700"
name="message" required />
<button type="submit" @click="postMessage" >
<svg class="w-5 h-5 text-gray-500 origin-center transform rotate-90" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
</svg>
</button>
</div>
</div>
</div>
<div class="col-1"></div>
</div>
<div style="padding: 1rem">
<div class="overflow-x-auto" style="height: 50rem; overflow-y: scroll; width: 100%">
<div class="form-control">
<div class="input-group">
<input type="text" placeholder="RefID here" class="input input-bordered" />
<button class="btn btn-square" >
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
</button>
<button style="color:white;" class="btn btn-info">Reload</button>
</div>
</div>
<hr style="margin: 10px 0 10px;">
<table class="table table-compact w-full">
<thead>
<tr>
<th>refid</th>
<th>Creator</th>
<th>Request</th>
<th>Status</th>
</tr>
</thead>
<tbody id="logBody">
<Supportticket
v-for="ticket in tickets" :key="ticket.refid"
@click="readTicket(ticket)"
:refid="ticket.refid"
:creator="ticket.creator"
:request="ticket.title"
:status="ticket.status"
></Supportticket>
</tbody>
</table>
</div>
</div>
</div>
</div>
</template>
<style scoped> </style>

View File

@ -0,0 +1,172 @@
<script lang="ts">
import { defineComponent, onMounted, reactive, ref, Ref } from "vue";
import Sidebar from "../parts/sidebar.vue";
import usernode from './parts/user.vue';
export default defineComponent({
setup() {
const requestHeaders: HeadersInit = new Headers();
const bearer:string | null = localStorage.getItem("bearer");
const display:Ref<boolean> = ref(false);
const editorUser:Ref<User> = ref();
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
type User = {
userid: string,
username: string,
joined: string,
level: number,
created: string,
xp: number,
msgs: number
}
const userValues:Ref<User[]> = ref([]);
if (bearer !== null) {
requestHeaders.set("Authorization", bearer);
}
requestHeaders.set("Content-Type", "application/json");
onMounted(async () => {
console.log("mounted")
let resp = await fetch("http://localhost:3000/stats/server?serverid="+localStorage.getItem("server")+"&commiterid=steev", {
method: "GET",
credentials: "same-origin",
headers: requestHeaders
});
let data = await resp.json();
console.log(data)
data.forEach((element: any) => {
let data = element;
if (data.xp == undefined) {data["xp"] = 0};
if (data.msgs == undefined) {data["msgs"] = 0};
userValues.value.push(data);
});
});
function showEditor(user:User) {
display.value = true
editorUser.value = user;
}
async function searchUser(){
}
function updateUser(){
let data = {
serverid: "test",
committerid: "steev",
userid: editorUser.value.userid,
username: editorUser.value.username,
joined: editorUser.value.joined,
level: editorUser.value.level,
xp: editorUser.value.xp,
msgs: editorUser.value.msgs
}
let resp = fetch("http://localhost:3000/stats/user", {
method: "PUT",
credentials: "same-origin",
headers: requestHeaders,
body: JSON.stringify(data)
});
}
return {searchUser, showEditor, updateUser, userValues, display, editorUser};
},
components: { Sidebar,usernode }
});
</script>
<template>
<div class="row">
<div class="col-2"><Sidebar></Sidebar></div>
<div class="col-10">
<div class="row" v-if="display">
<div class="col-2"></div>
<div class="col-8">
<div class="card-body items-center text-center">
<h2 class="card-title">User Editor!</h2>
<div class="card-actions justify-end">
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Userid</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorUser.userid" disabled/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Username</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorUser.username" disabled/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Joined</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" v-model="editorUser.created" disabled/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Level</span>
</label>
<input type="number" class="input input-bordered w-full max-w-xs" v-model="editorUser.level" />
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">XP</span>
</label>
<input type="number" class="input input-bordered w-full max-w-xs" v-model="editorUser.xp"/>
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">MSGS</span>
</label>
<input type="number" class="input input-bordered w-full max-w-xs" v-model="editorUser.msgs"/>
</div>
<button class="btn btn-success" @click="updateUser">Accept</button>
<button class="btn btn-primary" @click="display=false;">Hide</button>
</div>
</div>
</div>
<div class="col-2"></div>
</div>
<div class="overflow-x-auto" style="height: 50rem; overflow-y: scroll; width: 100%; padding: 1rem;" >
<input type="text" placeholder="Userid here" class="input w-full max-w-xs"/>
<button style="margin-left: 15px" class="btn btn-accent">lookup</button>
<table style="margin-top: 15px;" class="table table-compact w-full">
<thead>
<tr>
<th>userid</th>
<th>username</th>
<th>joined</th>
<th>Level</th>
<th>xp</th>
<th>msgs</th>
</tr>
</thead>
<tbody id="logBody">
<usernode v-for="user in userValues" :key="user.userid" @click="showEditor(user)"
:userid="user.userid"
:username="user.username"
:joined="user.created"
:level="user.level"
:xp="user.xp"
:msgs="user.msgs"
></usernode>
</tbody>
</table>
</div>
</div>
</div>
</template>
<style scoped> </style>

View File

@ -0,0 +1,35 @@
<script lang="ts">
import { def } from '@vue/shared';
import { defineComponent } from 'vue';
export default defineComponent({
setup(){
return {}
}
})
</script>
<template>
<footer class="footer p-10 bg-neutral text-neutral-content h-full">
<div>
<img src="../../assets/frankenbot.png" width="32">
<p>ACME Industries Ltd.<br>Providing reliable tech since 1992</p>
</div>
<div>
<span class="footer-title">Social</span>
<div class="grid grid-flow-col gap-4">
<a><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="fill-current"><path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"></path></svg></a>
<a><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="fill-current"><path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"></path></svg></a>
<a><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="fill-current"><path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"></path></svg></a>
</div>
</div>
</footer>
</template>
<style scoped>
input {
margin-top: 10px;
width: 100%;
}
</style>

View File

@ -0,0 +1,112 @@
<script lang="ts">
import { def } from '@vue/shared';
import { defineComponent, h, PropType } from 'vue';
import { Line } from 'vue-chartjs'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
Plugin
} from 'chart.js'
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale
)
export default defineComponent({
name:'Linechart',
components: {Line},
props: {
chartId: {
type: String,
default: 'line-chart'
},
width: {
type: Number,
default: 600
},
height: {
type: Number,
default: 190
},
cssClasses: {
default: '',
type: String
},
styles: {
type: Object as PropType<Partial<CSSStyleDeclaration>>,
default: () => {}
},
plugins: {
type: Array as PropType<Plugin<'line'>[]>,
default: () => []
},
},
setup(props){
const chartData = {
labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Satturday', 'Sunday'],
datasets: [
{
label: 'Bans',
backgroundColor: '#c0392b',
data: [40, 39, 10, 40, 39, 80, 40]
},
{
label: 'Warns',
backgroundColor: '#2980b9',
data: [4, 50, 2, 100, 30, 0, 1]
},
{
label: 'Mutes',
backgroundColor: '#e67e22',
data: [4, 5, 2, 10, 20, 5, 7]
},
{
label: 'Kicks',
backgroundColor: '#8e44ad',
data: [100, 120, 10, 50, 4, 8, 60]
},
{
label: 'Messages',
backgroundColor: '#27ae60',
data: [1000, 1200, 100, 500, 40, 80, 600]
}
]
}
const chartOptions = {
responsive: true,
maintainAspectRatio: true
}
return () =>
h(Line, {
chartData,
chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins
})
}
})
</script>
<template>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,62 @@
<script lang="ts">
import { def } from '@vue/shared';
import { defineComponent, reactive } from 'vue';
export default defineComponent({
setup(){
const isLoggedIn = reactive({"loggedIn": localStorage.getItem("bearer") != undefined})
console.log(isLoggedIn.loggedIn)
function logout(){
localStorage.clear();
isLoggedIn.loggedIn = false;
document.location.href = "/";
}
return {
isLoggedIn,
logout,
}
}
})
</script>
<template>
<div class="navbar bg-neutral text-neutral-content">
<div class="flex-1">
<a style="color:white;" class="btn btn-ghost normal-case text-xl" href="/"><img src="../../assets/frankenbot.png" width="32px" style="margin-right: 10px;">FRANKENBOT</a>
<ul class="menu menu-horizontal p-0">
<li><a href="/">Home</a></li>
<li><a href="#">About</a></li>
</ul>
</div>
<div class="flex-none" v-if="isLoggedIn.loggedIn">
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img src="../../assets/frankenbot.png" />
</div>
</label>
<ul tabindex="0" class="menu menu-compact dropdown-content mt-3 p-2 shadow bg-neutral text-neutral-content w-52">
<li><a href="/settings">Settings</a></li>
<li><a href="/dash">dashboard</a></li>
<li><a @click="logout()">Logout</a></li>
</ul>
</div>
</div>
<div v-else class="flex-none">
<div class="btn-group">
<a class="btn btn-active" href="/login">Login</a>
<button class="btn">Register</button>
</div>
</div>
</div>
</template>
<style scoped>
input {
margin-top: 10px;
width: 100%;
}
</style>

View File

@ -0,0 +1,51 @@
<script lang="ts">
import { defineComponent, onMounted, reactive } from "vue";
export default defineComponent({
setup() {
// Simulate servers
const servers = [
{id:"test", name:"Steevs Server"},
{id:"testid2", name:"Steevs Dev Server"},
{id:"testid3", name:"FrankenServer"},
{id:"testid4", name:"Discord discord"},
]
function changeServer(event:any){
console.log(event.target.value)
localStorage.setItem("server", event.target.value)
}
onMounted( () => {
if (localStorage.getItem("server") == undefined) {
localStorage.setItem
}
})
const foo ="bar";
name: 'sidebar'
return {foo, changeServer, servers};
},
});
</script>
<template>
<ul class="h-full menu bg-neutral text-neutral-content">
<li class="menu-title text-neutral-content" style="padding: 0.5rem;">Navigation</li>
<li>
<select class="select w-full" style="border-radius: 0;" @change="changeServer($event)">
<option v-for="server in servers" :key="server.id" value="{{server.id}}">{{server.name}}</option>
</select>
</li>
<hr style="margin: 10px 0 10px;">
<li><a href="/dash">Overview</a></li>
<li><a href="/dash/logs">Logs</a></li>
<li><a href="/dash/actions">Actions</a></li>
<li><a href="/dash/users">Users</a></li>
<li><a href="/dash/support">Support Tickets</a></li>
<li><a href="/dash/serversettings">Server Settings</a></li>
</ul>
</template>
<style scoped></style>

View File

@ -0,0 +1,54 @@
<script lang="ts">
import { defineComponent, reactive } from 'vue';
import Linechart from './parts/Linechart.vue';
import Sidebar from './parts/sidebar.vue';
export default defineComponent ({
setup() {
if (localStorage.getItem("bearer") == null || undefined) {
console.log("error no bearer -> moving to login page with error code");
document.location.href = "/login?error=not_logged_id";
}
return {};
},
components: { Linechart, Sidebar }
})
</script>
<template>
<div class="row">
<div class="col-2">
<Sidebar></Sidebar>
</div>
<div class="col-10">
<div class="row">
<div class="col-1"></div>
<div class="col-10">
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Username</span>
</label>
<input type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
</div>
<div class="form-control w-full max-w-xs">
<label class="label">
<span class="label-text">Password</span>
</label>
<input type="password" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
</div>
<br>
<button class="btn btn-success"> Submit </button>
</div>
<div class="col-1"></div>
</div>
</div>
</div>
</template>
<style scoped>
input {
margin-top: 10px;
width: 100%;
}
</style>

39
web/src/main.ts Normal file
View File

@ -0,0 +1,39 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import Login from './components/Login.vue';
import User from './components/user.vue';
import Dashboard from './components/Dashboard.vue';
import Logs from './components/dashboard/Logs.vue';
import Actions from './components/dashboard/actions.vue';
import Users from './components/dashboard/users.vue';
import Support from './components/dashboard/support.vue';
import Settings from './components/dashboard/settings.vue';
import Home from './components/Home.vue';
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
// 4. Provide the history implementation to use. We are using the hash history for simplicity here.
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/dash', component: Dashboard },
{ path: '/settings', component: User },
{ path: '/dash/logs', component: Logs },
{ path: '/dash/actions', component: Actions },
{ path: '/dash/users', component: Users },
{ path: '/dash/support', component: Support },
{ path: '/dash/serversettings', component: Settings },
{ path: '/login', component: Login },
],
})
// 5. Create and mount the root instance.
const app = createApp(App)
// Make sure to _use_ the router instance to make the
// whole app router-aware.
app.use(router)
app.mount('#app')

114
web/src/style.css Normal file
View File

@ -0,0 +1,114 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
background-color: #1e272e;
}
.centered {
display: flex;
justify-content: center;
align-items: center;
}
.row {
display: flex;
flex-wrap: wrap
}
.col-1 {
flex: 0 0 8.33333%
}
.col-offset-1 {
margin-left: 8.33333%
}
.col-2 {
flex: 0 0 16.66667%
}
.col-offset-2 {
margin-left: 16.66667%
}
.col-3 {
flex: 0 0 25%
}
.col-offset-3 {
margin-left: 25%
}
.col-4 {
flex: 0 0 33.33333%
}
.col-offset-4 {
margin-left: 33.33333%
}
.col-5 {
flex: 0 0 41.66667%
}
.col-offset-5 {
margin-left: 41.66667%
}
.col-6 {
flex: 0 0 50%
}
.col-offset-6 {
margin-left: 50%
}
.col-7 {
flex: 0 0 58.33333%
}
.col-offset-7 {
margin-left: 58.33333%
}
.col-8 {
flex: 0 0 66.66667%
}
.col-offset-8 {
margin-left: 66.66667%
}
.col-9 {
flex: 0 0 75%
}
.col-offset-9 {
margin-left: 75%
}
.col-10 {
flex: 0 0 83.33333%
}
.col-offset-10 {
margin-left: 83.33333%
}
.col-11 {
flex: 0 0 91.66667%
}
.col-offset-11 {
margin-left: 91.66667%
}
.col-12 {
flex: 0 0 100%
}
.col-offset-12 {
margin-left: 100%
}

7
web/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

11
web/tailwind.config.cjs Normal file
View File

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [require("daisyui")],
}

18
web/tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue","src/**/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

9
web/tsconfig.node.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

7
web/vite.config.ts Normal file
View File

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})