mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-07-28 01:12:22 +00:00
Added generate reset client password link
This commit is contained in:
parent
722cbb6054
commit
674fea7063
@ -1282,6 +1282,17 @@ def API_Clients_AssignedPeers():
|
|||||||
return ResponseObject(False, "Client does not exist")
|
return ResponseObject(False, "Client does not exist")
|
||||||
return ResponseObject(data=d)
|
return ResponseObject(data=d)
|
||||||
|
|
||||||
|
@app.post(f'{APP_PREFIX}/api/clients/generatePasswordResetLink')
|
||||||
|
def API_Clients_GeneratePasswordResetLink():
|
||||||
|
data = request.get_json()
|
||||||
|
clientId = data.get("ClientID")
|
||||||
|
if not clientId:
|
||||||
|
return ResponseObject(False, "Please provide ClientID")
|
||||||
|
|
||||||
|
token = DashboardClients.GenerateClientPasswordResetLink(clientId)
|
||||||
|
if token:
|
||||||
|
return ResponseObject(data=token)
|
||||||
|
return ResponseObject(False, "Failed to generate link")
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Index Page
|
Index Page
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -57,6 +58,18 @@ class DashboardClients:
|
|||||||
db.Column('Name', db.String(500)),
|
db.Column('Name', db.String(500)),
|
||||||
extend_existing=True,
|
extend_existing=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.dashboardClientsPasswordResetLinkTable = db.Table(
|
||||||
|
'DashboardClientsPasswordResetLinks', self.metadata,
|
||||||
|
db.Column('ResetToken', db.String(255), nullable=False, primary_key=True),
|
||||||
|
db.Column('ClientID', db.String(255), nullable=False),
|
||||||
|
db.Column('CreatedDate',
|
||||||
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP),
|
||||||
|
server_default=db.func.now()),
|
||||||
|
db.Column('ExpiryDate',
|
||||||
|
(db.DATETIME if 'sqlite:///' in ConnectionString("wgdashboard") else db.TIMESTAMP)),
|
||||||
|
extend_existing=True
|
||||||
|
)
|
||||||
|
|
||||||
self.metadata.create_all(self.engine)
|
self.metadata.create_all(self.engine)
|
||||||
self.Clients = {}
|
self.Clients = {}
|
||||||
@ -112,7 +125,6 @@ class DashboardClients:
|
|||||||
return self.ClientsRaw
|
return self.ClientsRaw
|
||||||
|
|
||||||
def GetClient(self, ClientID) -> dict[str, str] | None:
|
def GetClient(self, ClientID) -> dict[str, str] | None:
|
||||||
self.__getClients()
|
|
||||||
c = filter(lambda x: x['ClientID'] == ClientID, self.ClientsRaw)
|
c = filter(lambda x: x['ClientID'] == ClientID, self.ClientsRaw)
|
||||||
client = next((dict(client) for client in c), None)
|
client = next((dict(client) for client in c), None)
|
||||||
if client is not None:
|
if client is not None:
|
||||||
@ -322,6 +334,33 @@ class DashboardClients:
|
|||||||
'''
|
'''
|
||||||
For WGDashboard Admin to Manage Clients
|
For WGDashboard Admin to Manage Clients
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def GenerateClientPasswordResetLink(self, ClientID) -> bool | str:
|
||||||
|
c = self.GetClient(ClientID)
|
||||||
|
if c is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
newToken = str(uuid.uuid4())
|
||||||
|
with self.engine.begin() as conn:
|
||||||
|
conn.execute(
|
||||||
|
self.dashboardClientsPasswordResetLinkTable.update().values({
|
||||||
|
"ExpiryDate": db.func.now()
|
||||||
|
}).where(
|
||||||
|
self.dashboardClientsPasswordResetLinkTable.c.ClientID == ClientID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
self.dashboardClientsPasswordResetLinkTable.insert().values({
|
||||||
|
"ResetToken": newToken,
|
||||||
|
"ClientID": ClientID,
|
||||||
|
"ExpiryDate": datetime.datetime.now() + datetime.timedelta(minutes=30)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return newToken
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def GetAssignedPeerClients(self, ConfigurationName, PeerID):
|
def GetAssignedPeerClients(self, ConfigurationName, PeerID):
|
||||||
c = self.DashboardClientsPeerAssignment.GetAssignedClients(ConfigurationName, PeerID)
|
c = self.DashboardClientsPeerAssignment.GetAssignedClients(ConfigurationName, PeerID)
|
||||||
for a in c:
|
for a in c:
|
||||||
|
@ -1,38 +1,65 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
||||||
import LocaleText from "@/components/text/localeText.vue";
|
import LocaleText from "@/components/text/localeText.vue";
|
||||||
|
import { fetchGet, fetchPost } from "@/utilities/fetch.js"
|
||||||
|
import {ref} from "vue";
|
||||||
|
const props = defineProps(['client'])
|
||||||
|
|
||||||
|
const alert = ref(false)
|
||||||
|
const alertStatus = ref(false)
|
||||||
|
const alertMessage = ref(false)
|
||||||
|
|
||||||
|
const sendResetLink = async () => {
|
||||||
|
let smtpReady = false;
|
||||||
|
let token = undefined;
|
||||||
|
await fetchPost('/api/clients/generatePasswordResetLink', {
|
||||||
|
ClientID: props.client.ClientID
|
||||||
|
},(res) => {
|
||||||
|
if (res.status){
|
||||||
|
token = res.data
|
||||||
|
alertStatus.value = true
|
||||||
|
}else{
|
||||||
|
alertStatus.value = false
|
||||||
|
alertMessage.value = res.message
|
||||||
|
alert.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (token){
|
||||||
|
await fetchGet('/api/email/ready', {}, (res) => {
|
||||||
|
smtpReady = res.status
|
||||||
|
});
|
||||||
|
if (smtpReady){
|
||||||
|
await fetchPost('/api/email/send', {
|
||||||
|
"Receiver": props.client.Email,
|
||||||
|
"Body":
|
||||||
|
`Hi${props.client.Name ? ' ' + props.client.Name: ''},\n`
|
||||||
|
}, (res) => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-3">
|
<div class="p-3 d-flex gap-3 flex-column border-bottom">
|
||||||
<h6>
|
<div class="d-flex align-items-center">
|
||||||
<LocaleText t="Reset Password"></LocaleText>
|
<h6 class="mb-0">
|
||||||
</h6>
|
<LocaleText t="Reset Password"></LocaleText>
|
||||||
<div class="row g-2">
|
</h6>
|
||||||
<div class="col-sm-4">
|
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3 ms-auto"
|
||||||
<label class="mb-1">
|
@click="sendResetLink()"
|
||||||
<small class="fw-bold text-muted">
|
>
|
||||||
<LocaleText t="Current Password"></LocaleText>
|
<i class="bi bi-send me-2"></i>
|
||||||
</small>
|
<LocaleText t="Send Password Reset Link"></LocaleText>
|
||||||
</label>
|
</button>
|
||||||
<input type="password" class="form-control form-control-sm rounded-3">
|
</div>
|
||||||
</div>
|
<div class="alert rounded-3 mb-0"
|
||||||
<div class="col-sm-4">
|
:class="[alertStatus ? 'alert-success' : 'alert-danger']"
|
||||||
<label class="mb-1">
|
v-if="alert">
|
||||||
<small class="fw-bold text-muted">
|
{{ alertMessage }}
|
||||||
<LocaleText t="New Password"></LocaleText>
|
|
||||||
</small>
|
|
||||||
</label>
|
|
||||||
<input type="password" class="form-control form-control-sm rounded-3">
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4">
|
|
||||||
<label class="mb-1">
|
|
||||||
<small class="fw-bold text-muted">
|
|
||||||
<LocaleText t="Confirm New Password"></LocaleText>
|
|
||||||
</small>
|
|
||||||
</label>
|
|
||||||
<input type="password" class="form-control form-control-sm rounded-3">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,54 +1,45 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"flag": "🇺🇸",
|
|
||||||
"lang_id": "en-US",
|
"lang_id": "en-US",
|
||||||
"lang_name": "English (United States)",
|
"lang_name": "English (United States)",
|
||||||
"lang_name_localized": "English (United States)"
|
"lang_name_localized": "English (United States)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇸🇦",
|
|
||||||
"lang_id": "ar-SA",
|
"lang_id": "ar-SA",
|
||||||
"lang_name": "Arabic (Saudi Arabia)",
|
"lang_name": "Arabic (Saudi Arabia)",
|
||||||
"lang_name_localized": "العربية (السعودية)"
|
"lang_name_localized": "العربية (السعودية)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇧🇾",
|
|
||||||
"lang_id": "be-BY",
|
"lang_id": "be-BY",
|
||||||
"lang_name": "Belarusian (Belarus)",
|
"lang_name": "Belarusian (Belarus)",
|
||||||
"lang_name_localized": "Беларуская (Беларусь)"
|
"lang_name_localized": "Беларуская (Беларусь)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🏴",
|
|
||||||
"lang_id": "ca-ES",
|
"lang_id": "ca-ES",
|
||||||
"lang_name": "Catalan (Spain)",
|
"lang_name": "Catalan (Spain)",
|
||||||
"lang_name_localized": "Català (Espanya)"
|
"lang_name_localized": "Català (Espanya)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇨🇿",
|
|
||||||
"lang_id": "cs-CZ",
|
"lang_id": "cs-CZ",
|
||||||
"lang_name": "Czech (Czech Republic)",
|
"lang_name": "Czech (Czech Republic)",
|
||||||
"lang_name_localized": "Česky (Česká republika)"
|
"lang_name_localized": "Česky (Česká republika)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇩🇪",
|
|
||||||
"lang_id": "de-DE",
|
"lang_id": "de-DE",
|
||||||
"lang_name": "German (Germany)",
|
"lang_name": "German (Germany)",
|
||||||
"lang_name_localized": "Deutsch (Deutschland)"
|
"lang_name_localized": "Deutsch (Deutschland)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇪🇸",
|
|
||||||
"lang_id": "es-ES",
|
"lang_id": "es-ES",
|
||||||
"lang_name": "Spanish (Spain)",
|
"lang_name": "Spanish (Spain)",
|
||||||
"lang_name_localized": "Español (España)"
|
"lang_name_localized": "Español (España)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇮🇷",
|
|
||||||
"lang_id": "fa-IR",
|
"lang_id": "fa-IR",
|
||||||
"lang_name": "Persian (Iran)",
|
"lang_name": "Persian (Iran)",
|
||||||
"lang_name_localized": "فارسی (ایران)"
|
"lang_name_localized": "فارسی (ایران)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇨🇦",
|
|
||||||
"lang_id": "fr-CA",
|
"lang_id": "fr-CA",
|
||||||
"lang_name": "French (Canada)",
|
"lang_name": "French (Canada)",
|
||||||
"lang_name_localized": "Français (Canada)"
|
"lang_name_localized": "Français (Canada)"
|
||||||
@ -60,91 +51,76 @@
|
|||||||
"lang_name_localized": "Français (France)"
|
"lang_name_localized": "Français (France)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇭🇺",
|
|
||||||
"lang_id": "hu-HU",
|
"lang_id": "hu-HU",
|
||||||
"lang_name": "Hungarian (Hungary)",
|
"lang_name": "Hungarian (Hungary)",
|
||||||
"lang_name_localized": "Magyar (Magyarország)"
|
"lang_name_localized": "Magyar (Magyarország)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇮🇩",
|
|
||||||
"lang_id": "id-ID",
|
"lang_id": "id-ID",
|
||||||
"lang_name": "Indonesian (Indonesia)",
|
"lang_name": "Indonesian (Indonesia)",
|
||||||
"lang_name_localized": "Bahasa Indonesia (Indonesia)"
|
"lang_name_localized": "Bahasa Indonesia (Indonesia)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇮🇹",
|
|
||||||
"lang_id": "it-IT",
|
"lang_id": "it-IT",
|
||||||
"lang_name": "Italian (Italy)",
|
"lang_name": "Italian (Italy)",
|
||||||
"lang_name_localized": "Italiano (Italia)"
|
"lang_name_localized": "Italiano (Italia)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇯🇵",
|
|
||||||
"lang_id": "ja-JP",
|
"lang_id": "ja-JP",
|
||||||
"lang_name": "Japanese (Japan)",
|
"lang_name": "Japanese (Japan)",
|
||||||
"lang_name_localized": "日本語 (日本)"
|
"lang_name_localized": "日本語 (日本)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇰🇷",
|
|
||||||
"lang_id": "ko-KR",
|
"lang_id": "ko-KR",
|
||||||
"lang_name": "Korean (South Korea)",
|
"lang_name": "Korean (South Korea)",
|
||||||
"lang_name_localized": "한국어 (대한민국)"
|
"lang_name_localized": "한국어 (대한민국)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇳🇱",
|
|
||||||
"lang_id": "nl-NL",
|
"lang_id": "nl-NL",
|
||||||
"lang_name": "Dutch (Netherlands)",
|
"lang_name": "Dutch (Netherlands)",
|
||||||
"lang_name_localized": "Nederlands (Nederland)"
|
"lang_name_localized": "Nederlands (Nederland)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇵🇱",
|
|
||||||
"lang_id": "pl-PL",
|
"lang_id": "pl-PL",
|
||||||
"lang_name": "Polish (Poland)",
|
"lang_name": "Polish (Poland)",
|
||||||
"lang_name_localized": "Polski (Polska)"
|
"lang_name_localized": "Polski (Polska)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇵🇹",
|
|
||||||
"lang_id": "pt-BR",
|
"lang_id": "pt-BR",
|
||||||
"lang_name": "Portuguese (Brazil)",
|
"lang_name": "Portuguese (Brazil)",
|
||||||
"lang_name_localized": "Português (Brasil)"
|
"lang_name_localized": "Português (Brasil)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇷🇺",
|
|
||||||
"lang_id": "ru-RU",
|
"lang_id": "ru-RU",
|
||||||
"lang_name": "Russian (Russia)",
|
"lang_name": "Russian (Russia)",
|
||||||
"lang_name_localized": "Русский (Россия)"
|
"lang_name_localized": "Русский (Россия)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇸🇪",
|
|
||||||
"lang_id": "sv-SE",
|
"lang_id": "sv-SE",
|
||||||
"lang_name": "Swedish (Sweden)",
|
"lang_name": "Swedish (Sweden)",
|
||||||
"lang_name_localized": "Svenska (Sverige)"
|
"lang_name_localized": "Svenska (Sverige)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇹🇭",
|
|
||||||
"lang_id": "th-TH",
|
"lang_id": "th-TH",
|
||||||
"lang_name": "Thai (Thailand)",
|
"lang_name": "Thai (Thailand)",
|
||||||
"lang_name_localized": "ภาษาไทย (ประเทศไทย)"
|
"lang_name_localized": "ภาษาไทย (ประเทศไทย)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇹🇷",
|
|
||||||
"lang_id": "tr-TR",
|
"lang_id": "tr-TR",
|
||||||
"lang_name": "Turkish (Turkey)",
|
"lang_name": "Turkish (Turkey)",
|
||||||
"lang_name_localized": "Türkçe (Türkiye)"
|
"lang_name_localized": "Türkçe (Türkiye)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇺🇦",
|
|
||||||
"lang_id": "uk-UA",
|
"lang_id": "uk-UA",
|
||||||
"lang_name": "Ukrainian (Ukraine)",
|
"lang_name": "Ukrainian (Ukraine)",
|
||||||
"lang_name_localized": "Українська (Україна)"
|
"lang_name_localized": "Українська (Україна)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇨🇳",
|
|
||||||
"lang_id": "zh-CN",
|
"lang_id": "zh-CN",
|
||||||
"lang_name": "Chinese (Simplified, China)",
|
"lang_name": "Chinese (Simplified, China)",
|
||||||
"lang_name_localized": "中文(简体,中国)"
|
"lang_name_localized": "中文(简体,中国)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"flag": "🇭🇰",
|
|
||||||
"lang_id": "zh-HK",
|
"lang_id": "zh-HK",
|
||||||
"lang_name": "Chinese (Traditional, Hong Kong)",
|
"lang_name": "Chinese (Traditional, Hong Kong)",
|
||||||
"lang_name_localized": "中文(繁體,香港)"
|
"lang_name_localized": "中文(繁體,香港)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user