initial commit
This commit is contained in:
21
web/src/App.vue
Normal file
21
web/src/App.vue
Normal 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>
|
BIN
web/src/assets/frankenbot.png
Normal file
BIN
web/src/assets/frankenbot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
1
web/src/assets/vue.svg
Normal file
1
web/src/assets/vue.svg
Normal 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 |
33
web/src/components/Dashboard.vue
Normal file
33
web/src/components/Dashboard.vue
Normal 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>
|
63
web/src/components/Home.vue
Normal file
63
web/src/components/Home.vue
Normal 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>
|
72
web/src/components/Login.vue
Normal file
72
web/src/components/Login.vue
Normal 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>
|
74
web/src/components/dashboard/Logs.vue
Normal file
74
web/src/components/dashboard/Logs.vue
Normal 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>
|
217
web/src/components/dashboard/actions.vue
Normal file
217
web/src/components/dashboard/actions.vue
Normal 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>
|
39
web/src/components/dashboard/parts/actions.vue
Normal file
39
web/src/components/dashboard/parts/actions.vue
Normal 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>
|
21
web/src/components/dashboard/parts/log.vue
Normal file
21
web/src/components/dashboard/parts/log.vue
Normal 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>
|
26
web/src/components/dashboard/parts/messenger_message.vue
Normal file
26
web/src/components/dashboard/parts/messenger_message.vue
Normal 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>
|
25
web/src/components/dashboard/parts/support_ticket.vue
Normal file
25
web/src/components/dashboard/parts/support_ticket.vue
Normal 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>
|
25
web/src/components/dashboard/parts/user.vue
Normal file
25
web/src/components/dashboard/parts/user.vue
Normal 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>
|
||||
|
547
web/src/components/dashboard/settings.vue
Normal file
547
web/src/components/dashboard/settings.vue
Normal 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>
|
261
web/src/components/dashboard/support.vue
Normal file
261
web/src/components/dashboard/support.vue
Normal 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>
|
172
web/src/components/dashboard/users.vue
Normal file
172
web/src/components/dashboard/users.vue
Normal 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>
|
35
web/src/components/parts/Footer.vue
Normal file
35
web/src/components/parts/Footer.vue
Normal 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>
|
112
web/src/components/parts/Linechart.vue
Normal file
112
web/src/components/parts/Linechart.vue
Normal 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>
|
62
web/src/components/parts/Navbar.vue
Normal file
62
web/src/components/parts/Navbar.vue
Normal 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>
|
51
web/src/components/parts/sidebar.vue
Normal file
51
web/src/components/parts/sidebar.vue
Normal 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>
|
54
web/src/components/user.vue
Normal file
54
web/src/components/user.vue
Normal 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
39
web/src/main.ts
Normal 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
114
web/src/style.css
Normal 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
7
web/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
Reference in New Issue
Block a user