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(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
|
||||
|
@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
import uuid
|
||||
|
||||
@ -58,6 +59,18 @@ class DashboardClients:
|
||||
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.Clients = {}
|
||||
self.ClientsRaw = []
|
||||
@ -112,7 +125,6 @@ class DashboardClients:
|
||||
return self.ClientsRaw
|
||||
|
||||
def GetClient(self, ClientID) -> dict[str, str] | None:
|
||||
self.__getClients()
|
||||
c = filter(lambda x: x['ClientID'] == ClientID, self.ClientsRaw)
|
||||
client = next((dict(client) for client in c), None)
|
||||
if client is not None:
|
||||
@ -322,6 +334,33 @@ class DashboardClients:
|
||||
'''
|
||||
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):
|
||||
c = self.DashboardClientsPeerAssignment.GetAssignedClients(ConfigurationName, PeerID)
|
||||
for a in c:
|
||||
|
@ -1,38 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
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>
|
||||
|
||||
<template>
|
||||
<div class="p-3">
|
||||
<h6>
|
||||
<LocaleText t="Reset Password"></LocaleText>
|
||||
</h6>
|
||||
<div class="row g-2">
|
||||
<div class="col-sm-4">
|
||||
<label class="mb-1">
|
||||
<small class="fw-bold text-muted">
|
||||
<LocaleText t="Current 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="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 class="p-3 d-flex gap-3 flex-column border-bottom">
|
||||
<div class="d-flex align-items-center">
|
||||
<h6 class="mb-0">
|
||||
<LocaleText t="Reset Password"></LocaleText>
|
||||
</h6>
|
||||
<button class="btn btn-sm bg-primary-subtle text-primary-emphasis rounded-3 ms-auto"
|
||||
@click="sendResetLink()"
|
||||
>
|
||||
<i class="bi bi-send me-2"></i>
|
||||
<LocaleText t="Send Password Reset Link"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
<div class="alert rounded-3 mb-0"
|
||||
:class="[alertStatus ? 'alert-success' : 'alert-danger']"
|
||||
v-if="alert">
|
||||
{{ alertMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,54 +1,45 @@
|
||||
[
|
||||
{
|
||||
"flag": "🇺🇸",
|
||||
"lang_id": "en-US",
|
||||
"lang_name": "English (United States)",
|
||||
"lang_name_localized": "English (United States)"
|
||||
},
|
||||
{
|
||||
"flag": "🇸🇦",
|
||||
"lang_id": "ar-SA",
|
||||
"lang_name": "Arabic (Saudi Arabia)",
|
||||
"lang_name_localized": "العربية (السعودية)"
|
||||
},
|
||||
{
|
||||
"flag": "🇧🇾",
|
||||
"lang_id": "be-BY",
|
||||
"lang_name": "Belarusian (Belarus)",
|
||||
"lang_name_localized": "Беларуская (Беларусь)"
|
||||
},
|
||||
{
|
||||
"flag": "🏴",
|
||||
"lang_id": "ca-ES",
|
||||
"lang_name": "Catalan (Spain)",
|
||||
"lang_name_localized": "Català (Espanya)"
|
||||
},
|
||||
{
|
||||
"flag": "🇨🇿",
|
||||
"lang_id": "cs-CZ",
|
||||
"lang_name": "Czech (Czech Republic)",
|
||||
"lang_name_localized": "Česky (Česká republika)"
|
||||
},
|
||||
{
|
||||
"flag": "🇩🇪",
|
||||
"lang_id": "de-DE",
|
||||
"lang_name": "German (Germany)",
|
||||
"lang_name_localized": "Deutsch (Deutschland)"
|
||||
},
|
||||
{
|
||||
"flag": "🇪🇸",
|
||||
"lang_id": "es-ES",
|
||||
"lang_name": "Spanish (Spain)",
|
||||
"lang_name_localized": "Español (España)"
|
||||
},
|
||||
{
|
||||
"flag": "🇮🇷",
|
||||
"lang_id": "fa-IR",
|
||||
"lang_name": "Persian (Iran)",
|
||||
"lang_name_localized": "فارسی (ایران)"
|
||||
},
|
||||
{
|
||||
"flag": "🇨🇦",
|
||||
"lang_id": "fr-CA",
|
||||
"lang_name": "French (Canada)",
|
||||
"lang_name_localized": "Français (Canada)"
|
||||
@ -60,91 +51,76 @@
|
||||
"lang_name_localized": "Français (France)"
|
||||
},
|
||||
{
|
||||
"flag": "🇭🇺",
|
||||
"lang_id": "hu-HU",
|
||||
"lang_name": "Hungarian (Hungary)",
|
||||
"lang_name_localized": "Magyar (Magyarország)"
|
||||
},
|
||||
{
|
||||
"flag": "🇮🇩",
|
||||
"lang_id": "id-ID",
|
||||
"lang_name": "Indonesian (Indonesia)",
|
||||
"lang_name_localized": "Bahasa Indonesia (Indonesia)"
|
||||
},
|
||||
{
|
||||
"flag": "🇮🇹",
|
||||
"lang_id": "it-IT",
|
||||
"lang_name": "Italian (Italy)",
|
||||
"lang_name_localized": "Italiano (Italia)"
|
||||
},
|
||||
{
|
||||
"flag": "🇯🇵",
|
||||
"lang_id": "ja-JP",
|
||||
"lang_name": "Japanese (Japan)",
|
||||
"lang_name_localized": "日本語 (日本)"
|
||||
},
|
||||
{
|
||||
"flag": "🇰🇷",
|
||||
"lang_id": "ko-KR",
|
||||
"lang_name": "Korean (South Korea)",
|
||||
"lang_name_localized": "한국어 (대한민국)"
|
||||
},
|
||||
{
|
||||
"flag": "🇳🇱",
|
||||
"lang_id": "nl-NL",
|
||||
"lang_name": "Dutch (Netherlands)",
|
||||
"lang_name_localized": "Nederlands (Nederland)"
|
||||
},
|
||||
{
|
||||
"flag": "🇵🇱",
|
||||
"lang_id": "pl-PL",
|
||||
"lang_name": "Polish (Poland)",
|
||||
"lang_name_localized": "Polski (Polska)"
|
||||
},
|
||||
{
|
||||
"flag": "🇵🇹",
|
||||
"lang_id": "pt-BR",
|
||||
"lang_name": "Portuguese (Brazil)",
|
||||
"lang_name_localized": "Português (Brasil)"
|
||||
},
|
||||
{
|
||||
"flag": "🇷🇺",
|
||||
"lang_id": "ru-RU",
|
||||
"lang_name": "Russian (Russia)",
|
||||
"lang_name_localized": "Русский (Россия)"
|
||||
},
|
||||
{
|
||||
"flag": "🇸🇪",
|
||||
"lang_id": "sv-SE",
|
||||
"lang_name": "Swedish (Sweden)",
|
||||
"lang_name_localized": "Svenska (Sverige)"
|
||||
},
|
||||
{
|
||||
"flag": "🇹🇭",
|
||||
"lang_id": "th-TH",
|
||||
"lang_name": "Thai (Thailand)",
|
||||
"lang_name_localized": "ภาษาไทย (ประเทศไทย)"
|
||||
},
|
||||
{
|
||||
"flag": "🇹🇷",
|
||||
"lang_id": "tr-TR",
|
||||
"lang_name": "Turkish (Turkey)",
|
||||
"lang_name_localized": "Türkçe (Türkiye)"
|
||||
},
|
||||
{
|
||||
"flag": "🇺🇦",
|
||||
"lang_id": "uk-UA",
|
||||
"lang_name": "Ukrainian (Ukraine)",
|
||||
"lang_name_localized": "Українська (Україна)"
|
||||
},
|
||||
{
|
||||
"flag": "🇨🇳",
|
||||
"lang_id": "zh-CN",
|
||||
"lang_name": "Chinese (Simplified, China)",
|
||||
"lang_name_localized": "中文(简体,中国)"
|
||||
},
|
||||
{
|
||||
"flag": "🇭🇰",
|
||||
"lang_id": "zh-HK",
|
||||
"lang_name": "Chinese (Traditional, Hong Kong)",
|
||||
"lang_name_localized": "中文(繁體,香港)"
|
||||
|
Loading…
x
Reference in New Issue
Block a user