delete checkins when service is deleted, search in logs on UI, added UI alerts when tables are empty

pull/760/head
hunterlong 2020-07-22 13:15:35 -07:00
parent d16c2aad72
commit 4728a82006
8 changed files with 91 additions and 18 deletions

View File

@ -1,4 +1,4 @@
# 0.90.61 (07-19-2020) # 0.90.61 (07-22-2020)
- Modified sass layouts, organized and split up sections - Modified sass layouts, organized and split up sections
- Modified Checkins to seconds rather than milliseconds (for cronjob) - Modified Checkins to seconds rather than milliseconds (for cronjob)
- Modified Service View page to show data inside cards - Modified Service View page to show data inside cards
@ -10,6 +10,7 @@
- Added additional testing - Added additional testing
- Modified node version from 10.x to 12.18.2 - Modified node version from 10.x to 12.18.2
- Modified Notifier's struct values to be NullString and NullInt to allow empty values - Modified Notifier's struct values to be NullString and NullInt to allow empty values
- Added Search ability to Logs in UI
# 0.90.60 (07-15-2020) # 0.90.60 (07-15-2020)
- Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL - Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL

View File

@ -16,6 +16,15 @@
</div> </div>
</div> </div>
<div class="col-12" v-if="services.length === 0">
<div class="alert alert-dark d-block">
You currently don't have any services!
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-success float-right">
<font-awesome-icon icon="plus"/> Create
</router-link>
</div>
</div>
<div v-for="(service, index) in services" class="service_block" v-bind:key="index"> <div v-for="(service, index) in services" class="service_block" v-bind:key="index">
<ServiceInfo :service=service /> <ServiceInfo :service=service />
</div> </div>

View File

@ -3,7 +3,14 @@
<div class="card contain-card mb-4"> <div class="card contain-card mb-4">
<div class="card-header">{{ $t('top_nav.announcements') }}</div> <div class="card-header">{{ $t('top_nav.announcements') }}</div>
<div class="card-body pt-0"> <div class="card-body pt-0">
<table class="table table-striped">
<div v-if="messages.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any Announcements! Create one using the form below.
</div>
</div>
<table v-else class="table table-striped">
<thead> <thead>
<tr> <tr>
<th scope="col">{{ $t('dashboard.title') }}</th> <th scope="col">{{ $t('dashboard.title') }}</th>
@ -13,7 +20,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="message in $store.getters.messages" v-bind:key="message.id"> <tr v-for="message in messages" v-bind:key="message.id">
<td>{{message.title}}</td> <td>{{message.title}}</td>
<td class="d-none d-md-table-cell"> <td class="d-none d-md-table-cell">
<router-link :to="serviceLink(service(message.service))">{{serviceName(service(message.service))}}</router-link> <router-link :to="serviceLink(service(message.service))">{{serviceName(service(message.service))}}</router-link>
@ -51,6 +58,11 @@
message: {} message: {}
} }
}, },
computed: {
messages() {
return this.$store.getters.messages
}
},
methods: { methods: {
goto(to) { goto(to) {
this.$router.push(to) this.$router.push(to)

View File

@ -2,7 +2,7 @@
<div class="col-12"> <div class="col-12">
<div class="card contain-card mb-4"> <div class="card contain-card mb-4">
<div class="card-header">{{ $t('top_nav.services') }} <div class="card-header">{{ $t('top_nav.services') }}
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-outline-success float-right"> <router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-success float-right">
<font-awesome-icon icon="plus"/> Create <font-awesome-icon icon="plus"/> Create
</router-link> </router-link>
</div> </div>
@ -14,7 +14,14 @@
<div class="card contain-card mb-4"> <div class="card contain-card mb-4">
<div class="card-header">{{ $t('top_nav.groups') }}</div> <div class="card-header">{{ $t('top_nav.groups') }}</div>
<div class="card-body pt-0"> <div class="card-body pt-0">
<table class="table">
<div v-if="groupsList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any groups! Create one using the form below.
</div>
</div>
<table v-else class="table">
<thead> <thead>
<tr> <tr>
<th scope="col">{{ $t('dashboard.name') }}</th> <th scope="col">{{ $t('dashboard.name') }}</th>

View File

@ -1,5 +1,11 @@
<template> <template>
<table class="table"> <div>
<div v-if="servicesList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any services!
</div>
</div>
<table v-else class="table">
<thead> <thead>
<tr> <tr>
<th scope="col">Name</th> <th scope="col">Name</th>
@ -42,6 +48,7 @@
</tr> </tr>
</draggable> </draggable>
</table> </table>
</div>
</template> </template>
<script> <script>

View File

@ -1,9 +1,25 @@
<template> <template>
<div class="col-12 p-4"> <div class="col-12">
<p v-if="logs.length === 0" class="text-monospace sm"> <div class="card contain-card mb-4">
Loading Logs... <div class="card-header">
</p> Logs
<p v-for="(log, index) in logs" class="text-monospace sm">{{log}}</p> <div class="input-group input-group-sm float-right col-6">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-sm">Search</span>
</div>
<input v-model="search" type="text" class="form-control">
</div>
</div>
<div class="card-body">
<p v-if="logs.length === 0" class="text-monospace sm">
Loading Logs...
</p>
<div v-for="(log, index) in logs">
<span class="badge badge-secondary small mr-2">{{log.time}}</span>
<span class="text-monospace small">{{log.message}}</span>
</div>
</div>
</div>
</div> </div>
</template> </template>
@ -14,40 +30,58 @@ export default {
name: 'Logs', name: 'Logs',
data() { data() {
return { return {
logs: [], logs_record: [],
last: "", last: "",
search: "",
t: null t: null
} }
}, },
computed: {
logs() {
if (this.search) {
return this.logs_record.filter(o => o.message.includes(this.search));
} else {
return this.logs_record
}
}
},
async created() { async created() {
await this.getLogs() await this.getLogs()
if (!this.t) { if (!this.t) {
this.t = setInterval(async () => { this.t = setInterval(async () => {
await this.lastLog() await this.lastLog()
}, 650) }, 1000)
} }
}, },
beforeDestroy() { beforeDestroy() {
clearInterval(this.t) clearInterval(this.t)
}, },
methods: { methods: {
parseLog(data) {
const ts = data.match(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/gm)
return {
time: ts[0],
message: data.split(ts+": ")[1]
}
},
cleanLog(l) { cleanLog(l) {
const splitLog = l.split(": ") const splitLog = l.split(": ")
const last = splitLog.slice(1); const last = splitLog.slice(1);
return last.join(": ") return last.join(": ")
}, },
async getLogs() { async getLogs() {
const logs = await Api.logs() const l = await Api.logs()
const l = logs.reverse() l.forEach((d) => {
this.logs_record.push(this.parseLog(d))
})
this.last = this.cleanLog(l[l.length - 1]) this.last = this.cleanLog(l[l.length - 1])
this.logs = l
}, },
async lastLog() { async lastLog() {
const log = await Api.logs_last() const log = await Api.logs_last()
const cleanLast = this.cleanLog(log) const cleanLast = this.cleanLog(log)
if (this.last !== cleanLast) { if (this.last !== cleanLast) {
this.last = cleanLast this.last = cleanLast
this.logs.reverse().push(log) this.logs_record.unshift(this.parseLog(log))
} }
} }
} }

View File

@ -47,7 +47,7 @@ func TestPushoverNotifier(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Pushover.Author) assert.Equal(t, "Hunter Long", Pushover.Author)
assert.Equal(t, PUSHOVER_TOKEN, Pushover.ApiKey) assert.Equal(t, PUSHOVER_TOKEN, Pushover.ApiKey.String)
}) })
t.Run("Pushover Within Limits", func(t *testing.T) { t.Run("Pushover Within Limits", func(t *testing.T) {

View File

@ -99,6 +99,9 @@ func (s *Service) Delete() error {
if err := s.DeleteHits(); err != nil { if err := s.DeleteHits(); err != nil {
return err return err
} }
if err := s.DeleteCheckins(); err != nil {
return err
}
delete(allServices, s.Id) delete(allServices, s.Id)
q := db.Model(&Service{}).Delete(s) q := db.Model(&Service{}).Delete(s)
return q.Error() return q.Error()