mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
I think cross server actually worked
This commit is contained in:
parent
a650e628e5
commit
955839d513
@ -1096,6 +1096,7 @@ class DashboardConfig:
|
|||||||
self.SetConfig(section, key, value, True)
|
self.SetConfig(section, key, value, True)
|
||||||
self.__createAPIKeyTable()
|
self.__createAPIKeyTable()
|
||||||
self.DashboardAPIKeys = self.__getAPIKeys()
|
self.DashboardAPIKeys = self.__getAPIKeys()
|
||||||
|
self.APIAccessed = False
|
||||||
|
|
||||||
def __createAPIKeyTable(self):
|
def __createAPIKeyTable(self):
|
||||||
existingTable = sqldb.cursor().execute("SELECT name FROM sqlite_master WHERE type='table' AND name = 'DashboardAPIKeys'").fetchall()
|
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
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
API Routes
|
API Routes
|
||||||
'''
|
'''
|
||||||
@ -1351,7 +1353,8 @@ API Routes
|
|||||||
def auth_req():
|
def auth_req():
|
||||||
if request.method.lower() == 'options':
|
if request.method.lower() == 'options':
|
||||||
return ResponseObject(True)
|
return ResponseObject(True)
|
||||||
|
|
||||||
|
DashboardConfig.APIAccessed = False
|
||||||
if "api" in request.path:
|
if "api" in request.path:
|
||||||
if str(request.method) == "GET":
|
if str(request.method) == "GET":
|
||||||
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=str(request.args))
|
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
|
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}")
|
DashboardLogger.log(str(request.url), str(request.remote_addr), Message=f"API Key Access: {('true' if apiKeyExist else 'false')} - Key: {apiKey}")
|
||||||
if not apiKeyExist:
|
if not apiKeyExist:
|
||||||
|
DashboardConfig.APIAccessed = False
|
||||||
response = Flask.make_response(app, {
|
response = Flask.make_response(app, {
|
||||||
"status": False,
|
"status": False,
|
||||||
"message": "API Key does not exist",
|
"message": "API Key does not exist",
|
||||||
@ -1376,7 +1380,9 @@ def auth_req():
|
|||||||
response.content_type = "application/json"
|
response.content_type = "application/json"
|
||||||
response.status_code = 401
|
response.status_code = 401
|
||||||
return response
|
return response
|
||||||
|
DashboardConfig.APIAccessed = True
|
||||||
else:
|
else:
|
||||||
|
DashboardConfig.APIAccessed = False
|
||||||
if ('/static/' not in request.path and "username" not in session and "/" != request.path
|
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 "validateAuthentication" not in request.path and "authenticate" not in request.path
|
||||||
and "getDashboardConfiguration" not in request.path and "getDashboardTheme" 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'])
|
@app.route('/api/authenticate', methods=['POST'])
|
||||||
def API_AuthenticateLogin():
|
def API_AuthenticateLogin():
|
||||||
data = request.get_json()
|
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"),
|
valid = bcrypt.checkpw(data['password'].encode("utf-8"),
|
||||||
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
DashboardConfig.GetConfig("Account", "password")[1].encode("utf-8"))
|
||||||
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
totpEnabled = DashboardConfig.GetConfig("Account", "enable_totp")[1]
|
||||||
|
@ -16,8 +16,11 @@ watch(store.CrossServerConfiguration, () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav class="navbar bg-dark sticky-top" data-bs-theme="dark">
|
<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="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>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
|
@ -10,11 +10,13 @@ export default {
|
|||||||
return{
|
return{
|
||||||
active: false,
|
active: false,
|
||||||
startTime: undefined,
|
startTime: undefined,
|
||||||
endTime: undefined
|
endTime: undefined,
|
||||||
|
errorMsg: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handshake(){
|
handshake(){
|
||||||
|
this.active = false;
|
||||||
if (this.server.host && this.server.apiKey){
|
if (this.server.host && this.server.apiKey){
|
||||||
this.startTime = undefined;
|
this.startTime = undefined;
|
||||||
this.endTime = undefined;
|
this.endTime = undefined;
|
||||||
@ -26,15 +28,35 @@ export default {
|
|||||||
},
|
},
|
||||||
method: "GET",
|
method: "GET",
|
||||||
signal: AbortSignal.timeout(5000)
|
signal: AbortSignal.timeout(5000)
|
||||||
}).then(res => res.json()).then(res => {
|
}).then(res => {
|
||||||
this.active = true;
|
if (res.status === 200){
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
throw new Error(res.statusText)
|
||||||
|
}).then(() => {
|
||||||
this.endTime = dayjs()
|
this.endTime = dayjs()
|
||||||
|
this.active = true;
|
||||||
}).catch((res) => {
|
}).catch((res) => {
|
||||||
this.startTime = undefined;
|
this.active = false;
|
||||||
this.endTime = undefined;
|
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() {
|
mounted() {
|
||||||
@ -43,9 +65,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
getHandshakeTime(){
|
getHandshakeTime(){
|
||||||
if (this.startTime && this.endTime){
|
if (this.startTime && this.endTime){
|
||||||
return dayjs().subtract(this.startTime).millisecond()
|
return `${dayjs().subtract(this.startTime).millisecond()}ms`
|
||||||
}else{
|
}else{
|
||||||
return "N/A"
|
return this.errorMsg ? this.errorMsg : "N/A"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,11 +95,13 @@ export default {
|
|||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button
|
<button
|
||||||
@click="this.$emit('delete')"
|
@click="this.$emit('delete')"
|
||||||
|
|
||||||
class="ms-auto btn btn-sm bg-danger-subtle text-danger-emphasis border-1 border-danger-subtle">
|
class="ms-auto btn btn-sm bg-danger-subtle text-danger-emphasis border-1 border-danger-subtle">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
</button>
|
</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>
|
<i class="bi bi-arrow-right-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -85,7 +109,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-footer gap-2 d-flex align-items-center">
|
<div class="card-footer gap-2 d-flex align-items-center">
|
||||||
<span class="dot ms-0 me-2" :class="[this.active ? 'active':'inactive']"></span>
|
<span class="dot ms-0 me-2" :class="[this.active ? 'active':'inactive']"></span>
|
||||||
{{this.getHandshakeTime}}
|
<small>{{this.getHandshakeTime}}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -14,7 +14,7 @@ export default {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-100 mt-3">
|
<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>
|
<h5 class="mb-0">Server List</h5>
|
||||||
<button
|
<button
|
||||||
@click="this.store.addCrossServerConfiguration()"
|
@click="this.store.addCrossServerConfiguration()"
|
||||||
@ -22,10 +22,14 @@ export default {
|
|||||||
<i class="bi bi-plus-circle-fill me-2"></i>Server
|
<i class="bi bi-plus-circle-fill me-2"></i>Server
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-100 py-3 d-flex gap-3 flex-column" style="height: 400px">
|
<div class="w-100 d-flex gap-3 flex-column p-3 border border-1 border-secondary-subtle rounded-3"
|
||||||
<RemoteServer v-for="(server, index) in this.store.CrossServerConfiguration.ServerList"
|
style="height: 400px; overflow-y: scroll">
|
||||||
@delete="this.store.CrossServerConfiguration.ServerList.splice(index, 1)"
|
<RemoteServer v-for="(server, key) in this.store.CrossServerConfiguration.ServerList"
|
||||||
|
@setActiveServer="this.store.setActiveCrossServer(key)"
|
||||||
|
@delete="this.store.deleteCrossServerConfiguration(key)"
|
||||||
:server="server"></RemoteServer>
|
: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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -138,19 +138,28 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (to.meta.requiresAuth){
|
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()
|
await dashboardConfigurationStore.getConfiguration()
|
||||||
if (!wireguardConfigurationsStore.Configurations && to.name !== "Configuration List"){
|
if (!wireguardConfigurationsStore.Configurations && to.name !== "Configuration List"){
|
||||||
await wireguardConfigurationsStore.getConfigurations();
|
await wireguardConfigurationsStore.getConfigurations();
|
||||||
}
|
}
|
||||||
dashboardConfigurationStore.Redirect = undefined;
|
|
||||||
next()
|
next()
|
||||||
}else{
|
|
||||||
dashboardConfigurationStore.Redirect = to;
|
|
||||||
next("/signin")
|
|
||||||
dashboardConfigurationStore.newMessage("WGDashboard", "Session Ended", "warning")
|
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -13,13 +13,12 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||||||
},
|
},
|
||||||
CrossServerConfiguration:{
|
CrossServerConfiguration:{
|
||||||
Enable: false,
|
Enable: false,
|
||||||
ServerList: []
|
ServerList: {}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
initCrossServerConfiguration(){
|
initCrossServerConfiguration(){
|
||||||
const currentConfiguration = localStorage.getItem('CrossServerConfiguration');
|
const currentConfiguration = localStorage.getItem('CrossServerConfiguration');
|
||||||
|
|
||||||
if (currentConfiguration === null){
|
if (currentConfiguration === null){
|
||||||
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
|
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
|
||||||
}else{
|
}else{
|
||||||
@ -30,9 +29,23 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||||||
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
|
localStorage.setItem('CrossServerConfiguration', JSON.stringify(this.CrossServerConfiguration))
|
||||||
},
|
},
|
||||||
addCrossServerConfiguration(){
|
addCrossServerConfiguration(){
|
||||||
this.CrossServerConfiguration.ServerList.push(
|
this.CrossServerConfiguration.ServerList[v4().toString()] = {host: "", apiKey: "", active: false}
|
||||||
{host: "", apiKey: ""}
|
},
|
||||||
)
|
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(){
|
async getConfiguration(){
|
||||||
@ -49,6 +62,7 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||||||
},
|
},
|
||||||
async signOut(){
|
async signOut(){
|
||||||
await fetchGet("/api/signout", {}, (res) => {
|
await fetchGet("/api/signout", {}, (res) => {
|
||||||
|
this.removeActiveCrossServer();
|
||||||
this.$router.go('/signin')
|
this.$router.go('/signin')
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1,11 +1,31 @@
|
|||||||
import router from "@/router/index.js";
|
import router from "@/router/index.js";
|
||||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.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) => {
|
export const fetchGet = async (url, params=undefined, callback=undefined) => {
|
||||||
const urlSearchParams = new URLSearchParams(params);
|
const urlSearchParams = new URLSearchParams(params);
|
||||||
await fetch(`${url}?${urlSearchParams.toString()}`, {
|
await fetch(`${getUrl(url)}?${urlSearchParams.toString()}`, {
|
||||||
headers: {
|
headers: getHeaders()
|
||||||
"content-type": "application/json"
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then((x) => {
|
.then((x) => {
|
||||||
const store = DashboardConfigurationStore();
|
const store = DashboardConfigurationStore();
|
||||||
@ -26,10 +46,8 @@ export const fetchGet = async (url, params=undefined, callback=undefined) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const fetchPost = async (url, body, callback) => {
|
export const fetchPost = async (url, body, callback) => {
|
||||||
await fetch(`${url}`, {
|
await fetch(`${getUrl(url)}`, {
|
||||||
headers: {
|
headers: getHeaders(),
|
||||||
"content-type": "application/json"
|
|
||||||
},
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
}).then((x) => {
|
}).then((x) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user