mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 07:10:09 +01:00
Job logs api are done, still need to build the UI to view logs
This commit is contained in:
parent
48dc8033f5
commit
b65828416f
125
src/dashboard.py
125
src/dashboard.py
@ -83,7 +83,61 @@ class CustomJsonEncoder(DefaultJSONProvider):
|
|||||||
|
|
||||||
app.json = CustomJsonEncoder(app)
|
app.json = CustomJsonEncoder(app)
|
||||||
|
|
||||||
|
class Log:
|
||||||
|
def __init__(self, LogID: str, JobID: str, LogDate: str, Status: str, Message: str):
|
||||||
|
self.LogID = LogID
|
||||||
|
self.JobID = JobID
|
||||||
|
self.LogDate = LogDate
|
||||||
|
self.Status = Status
|
||||||
|
self.Message = Message
|
||||||
|
|
||||||
|
def toJson(self):
|
||||||
|
return {
|
||||||
|
"LogID": self.LogID,
|
||||||
|
"JobID": self.JobID,
|
||||||
|
"LogDate": self.LogDate,
|
||||||
|
"Status": self.Status,
|
||||||
|
"Message": self.Message
|
||||||
|
}
|
||||||
|
class Logger:
|
||||||
|
def __init__(self):
|
||||||
|
self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
|
||||||
|
check_same_thread=False)
|
||||||
|
self.loggerdb.row_factory = sqlite3.Row
|
||||||
|
self.logs:list(Log) = []
|
||||||
|
self.loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
self.__createLogDatabase()
|
||||||
|
|
||||||
|
def __createLogDatabase(self):
|
||||||
|
existingTable = self.loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
|
||||||
|
existingTable = [t['name'] for t in existingTable]
|
||||||
|
|
||||||
|
if "JobLog" not in existingTable:
|
||||||
|
self.loggerdbCursor.execute("CREATE TABLE JobLog (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now')), Status VARCHAR NOT NULL, Message VARCHAR, PRIMARY KEY (LogID))")
|
||||||
|
self.loggerdb.commit()
|
||||||
|
def log(self, JobID: str, Status: bool = True, Message: str = "") -> bool:
|
||||||
|
try:
|
||||||
|
self.loggerdbCursor.execute(f"INSERT INTO JobLog (LogID, JobID, Status, Message) VALUES (?, ?, ?, ?)",
|
||||||
|
(str(uuid.uuid4()), JobID, Status, Message,))
|
||||||
|
self.loggerdb.commit()
|
||||||
|
self.getLogs()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getLogs(self, all: bool = False):
|
||||||
|
try:
|
||||||
|
table = self.loggerdb.execute(f"SELECT * FROM JobLog ORDER BY LogDate DESC {'LIMIT 5' if not all else ''}").fetchall()
|
||||||
|
self.logs.clear()
|
||||||
|
for l in table:
|
||||||
|
self.logs.append(
|
||||||
|
Log(l["LogID"], l["JobID"], l["LogDate"], l["Status"], l["Message"]))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PeerJob:
|
class PeerJob:
|
||||||
def __init__(self, JobID: str, Configuration: str, Peer: str,
|
def __init__(self, JobID: str, Configuration: str, Peer: str,
|
||||||
Field: str, Operator: str, Value: str, CreationDate: datetime, ExpireDate: datetime, Action: str):
|
Field: str, Operator: str, Value: str, CreationDate: datetime, ExpireDate: datetime, Action: str):
|
||||||
@ -146,13 +200,6 @@ class PeerJobs:
|
|||||||
''')
|
''')
|
||||||
self.jobdb.commit()
|
self.jobdb.commit()
|
||||||
|
|
||||||
if "PeerJobs_Logs" not in existingTable:
|
|
||||||
self.jobdbCursor.execute('''
|
|
||||||
CREATE TABLE PeerJobs_Logs (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME, Status VARCHAR NOT NULL,
|
|
||||||
Message VARCHAR NOT NULL, PRIMARY KEY (LogID))
|
|
||||||
''')
|
|
||||||
self.jobdb.commit()
|
|
||||||
|
|
||||||
def toJson(self):
|
def toJson(self):
|
||||||
return [x.toJson() for x in self.Jobs]
|
return [x.toJson() for x in self.Jobs]
|
||||||
|
|
||||||
@ -182,7 +229,7 @@ class PeerJobs:
|
|||||||
if (len(str(Job.CreationDate))) == 0:
|
if (len(str(Job.CreationDate))) == 0:
|
||||||
return False, "Job does not exist"
|
return False, "Job does not exist"
|
||||||
self.jobdbCursor.execute('''
|
self.jobdbCursor.execute('''
|
||||||
DELETE FROM PeerJobs WHERE JobID = ?
|
UPDATE PeerJobs SET ExpireDate = strftime('%Y-%m-%d %H:%M:%S','now') WHERE JobID = ?
|
||||||
''', (Job.JobID,))
|
''', (Job.JobID,))
|
||||||
self.jobdb.commit()
|
self.jobdb.commit()
|
||||||
self.__getJobs()
|
self.__getJobs()
|
||||||
@ -192,23 +239,22 @@ class PeerJobs:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, str(e)
|
return False, str(e)
|
||||||
|
|
||||||
def finishJob(self, Job: PeerJob) -> tuple[bool, list] | tuple[bool, str]:
|
# def finishJob(self, Job: PeerJob) -> tuple[bool, list] | tuple[bool, str]:
|
||||||
try:
|
# try:
|
||||||
if (len(str(Job.CreationDate))) == 0:
|
# if (len(str(Job.CreationDate))) == 0:
|
||||||
return False, "Job does not exist"
|
# return False, "Job does not exist"
|
||||||
self.jobdbCursor.execute('''
|
# self.jobdbCursor.execute('''
|
||||||
UPDATE PeerJobs SET ExpireDate = strftime('%Y-%m-%d %H:%M:%S','now') WHERE JobId = ?
|
# UPDATE PeerJobs SET ExpireDate = strftime('%Y-%m-%d %H:%M:%S','now') WHERE JobId = ?
|
||||||
''', (Job.JobID,))
|
# ''', (Job.JobID,))
|
||||||
self.jobdb.commit()
|
# self.jobdb.commit()
|
||||||
self.__getJobs()
|
# self.__getJobs()
|
||||||
return True, list(
|
# return True, list(
|
||||||
filter(lambda x: x.Configuration == Job.Configuration and x.Peer == Job.Peer and x.JobID == Job.JobID,
|
# filter(lambda x: x.Configuration == Job.Configuration and x.Peer == Job.Peer and x.JobID == Job.JobID,
|
||||||
self.Jobs))
|
# self.Jobs))
|
||||||
except Exception as e:
|
# except Exception as e:
|
||||||
return False, str(e)
|
# return False, str(e)
|
||||||
|
|
||||||
def runJob(self):
|
def runJob(self):
|
||||||
print("======")
|
|
||||||
needToDelete = []
|
needToDelete = []
|
||||||
for job in self.Jobs:
|
for job in self.Jobs:
|
||||||
print(job.toJson())
|
print(job.toJson())
|
||||||
@ -222,22 +268,32 @@ class PeerJobs:
|
|||||||
y: float = float(job.Value)
|
y: float = float(job.Value)
|
||||||
else:
|
else:
|
||||||
x: datetime = datetime.now()
|
x: datetime = datetime.now()
|
||||||
y: datetime = datetime.fromtimestamp(float(job.Value))
|
y: datetime = datetime.strptime(job.Value, "%Y-%m-%dT%H:%M")
|
||||||
|
|
||||||
|
|
||||||
runAction: bool = self.__runJob_Compare(x, y, job.Operator)
|
runAction: bool = self.__runJob_Compare(x, y, job.Operator)
|
||||||
print("Running Job:" + str(runAction) + "\n")
|
print("Running Job:" + str(runAction) + "\n")
|
||||||
if runAction:
|
if runAction:
|
||||||
|
s = False
|
||||||
if job.Action == "restrict":
|
if job.Action == "restrict":
|
||||||
print(str(c.restrictPeers([fp.id]).get_json()))
|
s = c.restrictPeers([fp.id]).get_json()
|
||||||
elif job.Action == "delete":
|
elif job.Action == "delete":
|
||||||
print(str(c.deletePeers([fp.id]).get_json()))
|
s = c.deletePeers([fp.id]).get_json()
|
||||||
needToDelete.append(job)
|
|
||||||
|
if s['status'] is True:
|
||||||
|
JobLogger.log(job.JobID, s["status"],
|
||||||
|
f"Peer {fp.id} from {c.Name} is successfully {job.Action}ed."
|
||||||
|
)
|
||||||
|
needToDelete.append(job)
|
||||||
|
else:
|
||||||
|
JobLogger.log(job.JobID, s["status"],
|
||||||
|
f"Peer {fp.id} from {c.Name} failed {job.Action}ed."
|
||||||
|
)
|
||||||
|
print(f'''[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Peer Job Schedule: Ran {len(needToDelete)} job(s)''')
|
||||||
for j in needToDelete:
|
for j in needToDelete:
|
||||||
self.deleteJob(j)
|
self.deleteJob(j)
|
||||||
|
|
||||||
def __runJob_Compare(self, x: float | datetime, y: float | datetime, operator: str):
|
def __runJob_Compare(self, x: float | datetime, y: float | datetime, operator: str):
|
||||||
print(x, y, operator)
|
|
||||||
if operator == "eq":
|
if operator == "eq":
|
||||||
return x == y
|
return x == y
|
||||||
if operator == "neq":
|
if operator == "neq":
|
||||||
@ -990,6 +1046,7 @@ class DashboardConfig:
|
|||||||
DashboardConfig = DashboardConfig()
|
DashboardConfig = DashboardConfig()
|
||||||
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
|
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
|
||||||
AllPeerJobs: PeerJobs = PeerJobs()
|
AllPeerJobs: PeerJobs = PeerJobs()
|
||||||
|
JobLogger: Logger = Logger()
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Private Functions
|
Private Functions
|
||||||
@ -1678,8 +1735,10 @@ def backGroundThread():
|
|||||||
|
|
||||||
def peerJobScheduleBackgroundThread():
|
def peerJobScheduleBackgroundThread():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
print("-- Peer Schedule: Waiting 5 sec")
|
print(f'''[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Peer Job Schedule: Waiting for 10 Seconds''')
|
||||||
|
time.sleep(10)
|
||||||
while True:
|
while True:
|
||||||
|
print(f'''[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] Peer Job Schedule: Running''')
|
||||||
AllPeerJobs.runJob()
|
AllPeerJobs.runJob()
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
|
|
||||||
@ -1701,9 +1760,9 @@ bgThread = threading.Thread(target=backGroundThread)
|
|||||||
bgThread.daemon = True
|
bgThread.daemon = True
|
||||||
bgThread.start()
|
bgThread.start()
|
||||||
|
|
||||||
bg2Thread = threading.Thread(target=peerJobScheduleBackgroundThread)
|
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread)
|
||||||
bg2Thread.daemon = True
|
scheduleJobThread.daemon = True
|
||||||
bg2Thread.start()
|
scheduleJobThread.start()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host=app_ip, debug=False, port=app_port)
|
app.run(host=app_ip, debug=False, port=app_port)
|
||||||
|
@ -12,9 +12,24 @@ const store = DashboardConfigurationStore();
|
|||||||
</nav>
|
</nav>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade2" mode="out-in">
|
||||||
<Component :is="Component"></Component>
|
<Component :is="Component"></Component>
|
||||||
</Transition>
|
</Transition>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.app-enter-active,
|
||||||
|
.app-leave-active {
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
/*position: absolute;*/
|
||||||
|
/*padding-top: 50px*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-enter-from,
|
||||||
|
.app-leave-to {
|
||||||
|
transform: translateX(-30px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -34,9 +34,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="card shadow-sm rounded-3"
|
<div class="card shadow-sm rounded-3 peerCard bg-transparent"
|
||||||
:class="{'border-warning': Peer.restricted}"
|
:class="{'border-warning': Peer.restricted}">
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<div v-if="!Peer.restricted" class="card-header bg-transparent d-flex align-items-center gap-2 border-0">
|
<div v-if="!Peer.restricted" class="card-header bg-transparent d-flex align-items-center gap-2 border-0">
|
||||||
<div class="dot ms-0" :class="{active: Peer.status === 'running'}"></div>
|
<div class="dot ms-0" :class="{active: Peer.status === 'running'}"></div>
|
||||||
@ -77,8 +76,7 @@ export default {
|
|||||||
<div class="ms-auto px-2 rounded-3 subMenuBtn"
|
<div class="ms-auto px-2 rounded-3 subMenuBtn"
|
||||||
:class="{active: this.subMenuOpened}"
|
:class="{active: this.subMenuOpened}"
|
||||||
>
|
>
|
||||||
<a role="button" class="text-body"
|
<a role="button" class="text-body"
|
||||||
|
|
||||||
@click="this.subMenuOpened = true">
|
@click="this.subMenuOpened = true">
|
||||||
<h5 class="mb-0"><i class="bi bi-three-dots"></i></h5>
|
<h5 class="mb-0"><i class="bi bi-three-dots"></i></h5>
|
||||||
</a>
|
</a>
|
||||||
@ -101,7 +99,7 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.slide-fade-leave-active, .slide-fade-enter-active {
|
.slide-fade-leave-active, .slide-fade-enter-active{
|
||||||
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
|
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,4 +112,12 @@ export default {
|
|||||||
.subMenuBtn.active{
|
.subMenuBtn.active{
|
||||||
background-color: #ffffff20;
|
background-color: #ffffff20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.peerCard{
|
||||||
|
transition: box-shadow 0.1s cubic-bezier(1, 0.5, 0.8, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.peerCard:hover{
|
||||||
|
box-shadow: var(--bs-box-shadow) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -88,7 +88,8 @@ export default {
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -113,11 +114,11 @@ export default {
|
|||||||
|
|
||||||
<TransitionGroup name="schedulePeerJobTransition" tag="div" class="position-relative">
|
<TransitionGroup name="schedulePeerJobTransition" tag="div" class="position-relative">
|
||||||
<SchedulePeerJob
|
<SchedulePeerJob
|
||||||
@refresh="(j) => job = j"
|
@refresh="(j) => this.selectedPeer.jobs[index] = j"
|
||||||
@delete="this.deleteJob(job)"
|
@delete="this.deleteJob(job)"
|
||||||
:dropdowns="this.dropdowns"
|
:dropdowns="this.dropdowns"
|
||||||
:key="job.JobID"
|
:key="job.JobID"
|
||||||
:pjob="job" v-for="(job) in this.selectedPeer.jobs">
|
:pjob="job" v-for="(job, index) in this.selectedPeer.jobs">
|
||||||
</SchedulePeerJob>
|
</SchedulePeerJob>
|
||||||
|
|
||||||
<div class="card" key="none" v-if="this.selectedPeer.jobs.length === 0">
|
<div class="card" key="none" v-if="this.selectedPeer.jobs.length === 0">
|
||||||
|
@ -173,6 +173,19 @@ export default {
|
|||||||
}, (res) => {
|
}, (res) => {
|
||||||
this.configurationInfo = res.data.configurationInfo;
|
this.configurationInfo = res.data.configurationInfo;
|
||||||
this.configurationPeers = res.data.configurationPeers;
|
this.configurationPeers = res.data.configurationPeers;
|
||||||
|
|
||||||
|
// let modals = [this.peerSetting, this.peerScheduleJobs, this.peerQRCode]
|
||||||
|
// modals.forEach(x => {
|
||||||
|
//
|
||||||
|
// if (x.modalOpen && this.configurationPeers.find(p => p.id === x.selectedPeer.id)){
|
||||||
|
// x.selectedPeer = this.configurationPeers.find(p => p.id === x.selectedPeer.id)
|
||||||
|
// console.log(this.configurationPeers.find(p => p.id === x.selectedPeer.id))
|
||||||
|
// }else{
|
||||||
|
// x.modalOpen = false
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
|
||||||
this.configurationPeers.forEach(x => {
|
this.configurationPeers.forEach(x => {
|
||||||
x.restricted = false;
|
x.restricted = false;
|
||||||
})
|
})
|
||||||
|
@ -33,8 +33,9 @@ export default {
|
|||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(newValue){
|
handler(newValue){
|
||||||
console.log(newValue)
|
if (!this.edit){
|
||||||
this.job = JSON.parse(JSON.stringify(newValue))
|
this.job = JSON.parse(JSON.stringify(newValue))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -47,7 +48,8 @@ export default {
|
|||||||
if (res.status){
|
if (res.status){
|
||||||
this.edit = false;
|
this.edit = false;
|
||||||
this.store.newMessage("Server", "Job Saved!", "success")
|
this.store.newMessage("Server", "Job Saved!", "success")
|
||||||
this.$emit("refresh", this.data)
|
console.log(res.data)
|
||||||
|
this.$emit("refresh", res.data[0])
|
||||||
this.newJob = false;
|
this.newJob = false;
|
||||||
}else{
|
}else{
|
||||||
this.store.newMessage("Server", res.message, "danger")
|
this.store.newMessage("Server", res.message, "danger")
|
||||||
|
@ -12,7 +12,7 @@ export default {
|
|||||||
return {store, wireguardConfigurationStore}
|
return {store, wireguardConfigurationStore}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
searchString: String,
|
|
||||||
configuration: Object
|
configuration: Object
|
||||||
},
|
},
|
||||||
data(){
|
data(){
|
||||||
@ -28,10 +28,24 @@ export default {
|
|||||||
'10000': '10 Seconds',
|
'10000': '10 Seconds',
|
||||||
'30000': '30 Seconds',
|
'30000': '30 Seconds',
|
||||||
'60000': '1 Minutes'
|
'60000': '1 Minutes'
|
||||||
}
|
},
|
||||||
|
searchString: "",
|
||||||
|
searchStringTimeout: undefined
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
debounce(){
|
||||||
|
if (!this.searchStringTimeout){
|
||||||
|
this.searchStringTimeout = setTimeout(() => {
|
||||||
|
this.wireguardConfigurationStore.searchString = this.searchString;
|
||||||
|
}, 300)
|
||||||
|
}else{
|
||||||
|
clearTimeout(this.searchStringTimeout)
|
||||||
|
this.searchStringTimeout = setTimeout(() => {
|
||||||
|
this.wireguardConfigurationStore.searchString = this.searchString;
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
},
|
||||||
updateSort(sort){
|
updateSort(sort){
|
||||||
fetchPost("/api/updateDashboardConfigurationItem", {
|
fetchPost("/api/updateDashboardConfigurationItem", {
|
||||||
section: "Server",
|
section: "Server",
|
||||||
@ -68,25 +82,34 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="mb-3">
|
||||||
<div class="d-flex gap-2 mb-3 z-3">
|
<div class="d-flex gap-2 z-3">
|
||||||
<RouterLink
|
<RouterLink
|
||||||
to="create"
|
to="create"
|
||||||
class="text-decoration-none btn btn-primary rounded-3 btn-sm">
|
class="text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle shadow-sm">
|
||||||
<i class="bi bi-plus-lg me-2"></i>Peers
|
<i class="bi bi-plus-lg me-2"></i>Peer
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
||||||
<!-- <RouterLink-->
|
<!-- <RouterLink-->
|
||||||
<!-- to="jobs"-->
|
<!-- to="jobs"-->
|
||||||
<!-- class="text-decoration-none btn btn-primary rounded-3 btn-sm">-->
|
<!-- class="text-decoration-none btn btn-primary rounded-3 btn-sm">-->
|
||||||
<!-- <i class="bi bi-app-indicator me-2"></i>Jobs-->
|
<!-- <i class="bi bi-app-indicator me-2"></i>Jobs-->
|
||||||
<!-- </RouterLink>-->
|
<!-- </RouterLink>-->
|
||||||
<button class="btn btn-sm btn-primary rounded-3" @click="this.downloadAllPeer()">
|
<button class="btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle shadow-sm"
|
||||||
|
@click="this.downloadAllPeer()">
|
||||||
<i class="bi bi-download me-2"></i> Download All
|
<i class="bi bi-download me-2"></i> Download All
|
||||||
</button>
|
</button>
|
||||||
|
<div class="d-flex align-items-center ms-auto">
|
||||||
<div class="dropdown ms-auto">
|
<!-- <label class="d-flex me-2 text-muted" for="searchPeers"><i class="bi bi-search me-1"></i></label>-->
|
||||||
<button class="btn btn-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<input class="form-control rounded-3 bg-secondary-subtle border-1 border-secondary-subtle shadow-sm"
|
||||||
|
placeholder="Search..."
|
||||||
|
id="searchPeers"
|
||||||
|
@keyup="this.debounce()"
|
||||||
|
v-model="this.searchString">
|
||||||
|
</div>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn dropdown-toggle text-secondary-emphasis bg-secondary-subtle rounded-3 border-1 border-secondary-subtle shadow-sm"
|
||||||
|
type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-filter-circle me-2"></i>
|
<i class="bi bi-filter-circle me-2"></i>
|
||||||
Sort
|
Sort
|
||||||
</button>
|
</button>
|
||||||
@ -101,7 +124,8 @@ export default {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button class="btn btn-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="btn dropdown-toggle text-secondary-emphasis bg-secondary-subtle rounded-3 border-1 border-secondary-subtle shadow-sm"
|
||||||
|
type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu shadow mt-2 rounded-3">
|
<ul class="dropdown-menu shadow mt-2 rounded-3">
|
||||||
@ -113,24 +137,7 @@ export default {
|
|||||||
</a></li>
|
</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <button class="btn btn-outline-secondary btn-sm rounded-3" type="button"-->
|
|
||||||
<!-- @click="this.store.Peers.Selecting = !this.store.Peers.Selecting"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- <i class="bi bi-app-indicator me-2"></i>-->
|
|
||||||
<!-- Select-->
|
|
||||||
<!-- </button>-->
|
|
||||||
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<!-- <label class="d-flex me-2 text-muted" for="searchPeers"><i class="bi bi-search me-1"></i></label>-->
|
|
||||||
<input class="form-control form-control-sm rounded-3"
|
|
||||||
placeholder="Search..."
|
|
||||||
id="searchPeers"
|
|
||||||
v-model="this.wireguardConfigurationStore.searchString">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1097,17 +1097,17 @@ pre.index-alert {
|
|||||||
.list-move, /* apply transition to moving elements */
|
.list-move, /* apply transition to moving elements */
|
||||||
.list-enter-active,
|
.list-enter-active,
|
||||||
.list-leave-active {
|
.list-leave-active {
|
||||||
transition: all 0.4s cubic-bezier(0.82, 0.58, 0.17, 0.9);
|
transition: all 0.5s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-leave-active{
|
/*.list-leave-active{*/
|
||||||
position: absolute;
|
/* position: absolute !important;*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
.list-enter-from,
|
.list-enter-from,
|
||||||
.list-leave-to {
|
.list-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(30px);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ensure leaving items are taken out of layout flow so that moving
|
/* ensure leaving items are taken out of layout flow so that moving
|
||||||
|
Loading…
Reference in New Issue
Block a user