1
0
mirror of https://github.com/donaldzou/WGDashboard.git synced 2024-11-06 16:00:28 +01:00

I think cross server actually worked

This commit is contained in:
Donald Zou 2024-08-11 01:48:13 -04:00
parent a650e628e5
commit 955839d513
7 changed files with 125 additions and 36 deletions

View File

@ -1096,6 +1096,7 @@ class DashboardConfig:
self.SetConfig(section, key, value, True)
self.__createAPIKeyTable()
self.DashboardAPIKeys = self.__getAPIKeys()
self.APIAccessed = False
def __createAPIKeyTable(self):
existingTable = sqldb.cursor().execute("SELECT name FROM sqlite_master WHERE type='table' AND name = 'DashboardAPIKeys'").fetchall()
@ -1343,6 +1344,7 @@ def _getWireguardConfigurationAvailableIP(configName: str) -> tuple[bool, list[s
return False, None
'''
API Routes
'''
@ -1352,6 +1354,7 @@ def auth_req():
if request.method.lower() == 'options':
return ResponseObject(True)
DashboardConfig.APIAccessed = False
if "api" in request.path:
if str(request.method) == "GET":
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
@ -1368,6 +1371,7 @@ def auth_req():
apiKeyExist = len(list(filter(lambda x : x.Key == apiKey, DashboardConfig.DashboardAPIKeys))) == 1
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {('true' if apiKeyExist else 'false')} - Key: {apiKey}")
if not apiKeyExist:
DashboardConfig.APIAccessed = False
response = Flask.make_response(app, {
"status": False,
"message": "API Key does not exist",
@ -1376,7 +1380,9 @@ def auth_req():
response.content_type = "application/json"
response.status_code = 401
return response
DashboardConfig.APIAccessed = True
else:
DashboardConfig.APIAccessed = False
if ('/static/' not in request.path and "username" not in session and "/" != request.path
and "validateAuthentication" not in request.path and "authenticate" not in request.path
and "getDashboardConfiguration" not in request.path and "getDashboardTheme" not in request.path
@ -1408,6 +1414,17 @@ def API_ValidateAuthentication():
@app.route('/api/authenticate', methods=['POST'])
def API_AuthenticateLogin():
data = request.get_json()
if DashboardConfig.APIAccessed:
authToken = hashlib.sha256(f"{request.headers.get('wg-dashboard-apikey')}{datetime.now()}".encode()).hexdigest()
session['username'] = authToken
resp = ResponseObject(True, DashboardConfig.GetConfig("Other", "welcome_session")[1])
print(data['host'])
resp.set_cookie("authToken", authToken, domain=data['host'])
session.permanent = True
return resp
valid = bcrypt.checkpw(data['password'].encode("utf-8"),
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]

View File

@ -16,8 +16,11 @@ watch(store.CrossServerConfiguration, () => {
<template>
<nav class="navbar bg-dark sticky-top" data-bs-theme="dark">
<div class="container-fluid">
<div class="container-fluid d-flex text-body">
<span class="navbar-brand mb-0 h1">WGDashboard</span>
<span class="ms-auto" v-if="store.getActiveCrossServer() !== undefined">
<i class="bi bi-server me-2"></i>{{store.getActiveCrossServer().host}}
</span>
</div>
</nav>
<Suspense>

View File

@ -10,11 +10,13 @@ export default {
return{
active: false,
startTime: undefined,
endTime: undefined
endTime: undefined,
errorMsg: ""
}
},
methods: {
handshake(){
this.active = false;
if (this.server.host && this.server.apiKey){
this.startTime = undefined;
this.endTime = undefined;
@ -26,15 +28,35 @@ export default {
},
method: "GET",
signal: AbortSignal.timeout(5000)
}).then(res => res.json()).then(res => {
this.active = true;
}).then(res => {
if (res.status === 200){
return res.json()
}
throw new Error(res.statusText)
}).then(() => {
this.endTime = dayjs()
this.active = true;
}).catch((res) => {
this.startTime = undefined;
this.endTime = undefined;
this.active = false;
this.errorMsg = res;
})
}
},
async connect(){
await fetch(`${this.server.host}/api/authenticate`, {
headers: {
"content-type": "application/json",
"wg-dashboard-apikey": this.server.apiKey
},
body: JSON.stringify({
host: window.location.hostname
}),
method: "POST",
signal: AbortSignal.timeout(5000),
}).then(res => res.json()).then(res => {
this.$emit("setActiveServer")
this.$router.push('/')
})
}
},
mounted() {
@ -43,9 +65,9 @@ export default {
computed: {
getHandshakeTime(){
if (this.startTime && this.endTime){
return dayjs().subtract(this.startTime).millisecond()
return `${dayjs().subtract(this.startTime).millisecond()}ms`
}else{
return "N/A"
return this.errorMsg ? this.errorMsg : "N/A"
}
}
}
@ -73,11 +95,13 @@ export default {
<div class="d-flex gap-2">
<button
@click="this.$emit('delete')"
class="ms-auto btn btn-sm bg-danger-subtle text-danger-emphasis border-1 border-danger-subtle">
<i class="bi bi-trash"></i>
</button>
<button class="ms-auto btn btn-sm bg-success-subtle text-success-emphasis border-1 border-success-subtle">
<button
@click="this.connect()"
:class="{disabled: !this.active}"
class="ms-auto btn btn-sm bg-success-subtle text-success-emphasis border-1 border-success-subtle">
<i class="bi bi-arrow-right-circle"></i>
</button>
</div>
@ -85,7 +109,7 @@ export default {
</div>
<div class="card-footer gap-2 d-flex align-items-center">
<span class="dot ms-0 me-2" :class="[this.active ? 'active':'inactive']"></span>
{{this.getHandshakeTime}}
<small>{{this.getHandshakeTime}}</small>
</div>
</div>
</template>

View File

@ -14,7 +14,7 @@ export default {
<template>
<div class="w-100 mt-3">
<div class="d-flex align-items-center">
<div class="d-flex align-items-center mb-3">
<h5 class="mb-0">Server List</h5>
<button
@click="this.store.addCrossServerConfiguration()"
@ -22,10 +22,14 @@ export default {
<i class="bi bi-plus-circle-fill me-2"></i>Server
</button>
</div>
<div class="w-100 py-3 d-flex gap-3 flex-column" style="height: 400px">
<RemoteServer v-for="(server, index) in this.store.CrossServerConfiguration.ServerList"
@delete="this.store.CrossServerConfiguration.ServerList.splice(index, 1)"
<div class="w-100 d-flex gap-3 flex-column p-3 border border-1 border-secondary-subtle rounded-3"
style="height: 400px; overflow-y: scroll">
<RemoteServer v-for="(server, key) in this.store.CrossServerConfiguration.ServerList"
@setActiveServer="this.store.setActiveCrossServer(key)"
@delete="this.store.deleteCrossServerConfiguration(key)"
:server="server"></RemoteServer>
<h6 class="text-muted m-auto" v-if="this.store.CrossServerConfiguration.ServerList.length === 0">
Click<i class="bi bi-plus-circle-fill mx-1"></i>to add your server</h6>
</div>
</div>
</template>

View File

@ -138,19 +138,28 @@ router.beforeEach(async (to, from, next) => {
}
if (to.meta.requiresAuth){
if (cookie.getCookie("authToken") && await checkAuth()){
if (!dashboardConfigurationStore.getActiveCrossServer()){
if (cookie.getCookie("authToken") && await checkAuth()){
await dashboardConfigurationStore.getConfiguration()
if (!wireguardConfigurationsStore.Configurations && to.name !== "Configuration List"){
await wireguardConfigurationsStore.getConfigurations();
}
dashboardConfigurationStore.Redirect = undefined;
next()
}else{
dashboardConfigurationStore.Redirect = to;
next("/signin")
dashboardConfigurationStore.newMessage("WGDashboard", "Session Ended", "warning")
}
}else{
await dashboardConfigurationStore.getConfiguration()
if (!wireguardConfigurationsStore.Configurations && to.name !== "Configuration List"){
await wireguardConfigurationsStore.getConfigurations();
}
dashboardConfigurationStore.Redirect = undefined;
next()
}else{
dashboardConfigurationStore.Redirect = to;
next("/signin")
dashboardConfigurationStore.newMessage("WGDashboard", "Session Ended", "warning")
}
}else {
next();
}
});

View File

@ -13,13 +13,12 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
},
CrossServerConfiguration:{
Enable: false,
ServerList: []
ServerList: {}
}
}),
actions: {
initCrossServerConfiguration(){
const currentConfiguration = localStorage.getItem('CrossServerConfiguration');
if (currentConfiguration === null){
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
}else{
@ -30,9 +29,23 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
},
addCrossServerConfiguration(){
this.CrossServerConfiguration.ServerList.push(
{host: "", apiKey: ""}
)
this.CrossServerConfiguration.ServerList[v4().toString()] = {host: "", apiKey: "", active: false}
},
deleteCrossServerConfiguration(key){
delete this.CrossServerConfiguration.ServerList[key];
},
getActiveCrossServer(){
const key = localStorage.getItem('ActiveCrossServerConfiguration');
if (key !== null){
return this.CrossServerConfiguration.ServerList[key]
}
return undefined
},
setActiveCrossServer(key){
localStorage.setItem('ActiveCrossServerConfiguration', key)
},
removeActiveCrossServer(){
localStorage.removeItem('ActiveCrossServerConfiguration')
},
async getConfiguration(){
@ -49,6 +62,7 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
},
async signOut(){
await fetchGet("/api/signout", {}, (res) => {
this.removeActiveCrossServer();
this.$router.go('/signin')
});
},

View File

@ -1,11 +1,31 @@
import router from "@/router/index.js";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const getHeaders = () => {
let headers = {
"content-type": "application/json"
}
const store = DashboardConfigurationStore();
const apiKey = store.getActiveCrossServer();
if (apiKey){
headers['wg-dashboard-apikey'] = apiKey.apiKey
}
return headers
}
const getUrl = (url) => {
const store = DashboardConfigurationStore();
const apiKey = store.getActiveCrossServer();
if (apiKey){
return `${apiKey.host}${url}`
}
return url
}
export const fetchGet = async (url, params=undefined, callback=undefined) => {
const urlSearchParams = new URLSearchParams(params);
await fetch(`${url}?${urlSearchParams.toString()}`, {
headers: {
"content-type": "application/json"
}
await fetch(`${getUrl(url)}?${urlSearchParams.toString()}`, {
headers: getHeaders()
})
.then((x) => {
const store = DashboardConfigurationStore();
@ -26,10 +46,8 @@ export const fetchGet = async (url, params=undefined, callback=undefined) => {
}
export const fetchPost = async (url, body, callback) => {
await fetch(`${url}`, {
headers: {
"content-type": "application/json"
},
await fetch(`${getUrl(url)}`, {
headers: getHeaders(),
method: "POST",
body: JSON.stringify(body)
}).then((x) => {