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.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
''' '''
@ -1352,6 +1354,7 @@ 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]

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

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

View File

@ -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')
}); });
}, },

View File

@ -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";
export const fetchGet = async (url, params=undefined, callback=undefined) => {
const urlSearchParams = new URLSearchParams(params); const getHeaders = () => {
await fetch(`${url}?${urlSearchParams.toString()}`, { let headers = {
headers: {
"content-type": "application/json" "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(`${getUrl(url)}?${urlSearchParams.toString()}`, {
headers: getHeaders()
}) })
.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) => {