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

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
}