add new statistic page
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"backend/env"
|
||||
"backend/models"
|
||||
"backend/server"
|
||||
"backend/stats"
|
||||
"backend/utils"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -119,6 +120,10 @@ func main() {
|
||||
}))
|
||||
|
||||
//set logger for AuthMiddleware
|
||||
s.Routes.Use(func(c *gin.Context) {
|
||||
c.Set("logger", logger)
|
||||
c.Next()
|
||||
})
|
||||
accessHandler.SetMiddlewareLogger(s.Routes)
|
||||
api := s.Routes.Group("/api")
|
||||
//set routes
|
||||
@@ -136,6 +141,7 @@ func main() {
|
||||
role.GET("/members", dbHandler.GetMember)
|
||||
auth.GET("/events", dbHandler.GetEvent)
|
||||
auth.GET("/groups", dbHandler.GetGroup)
|
||||
auth.POST("/stats", stats.GetStats)
|
||||
|
||||
auth.GET("/users", accessHandler.GetUser)
|
||||
auth.GET("/roles", accessHandler.GetRole)
|
||||
|
||||
54
backend/stats/stats.go
Normal file
54
backend/stats/stats.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"backend/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"gitea.tecamino.com/paadi/tecamino-logger/logging"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Stats struct {
|
||||
Database string `json:"database,omitempty"`
|
||||
DatabaseSize int64 `json:"databaseSize,omitempty"`
|
||||
}
|
||||
|
||||
func GetStats(c *gin.Context) {
|
||||
middlewareData, err := utils.GetMiddlewareData(c, "logger")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
logger, ok := middlewareData.(*logging.Logger)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "middleware logger for state not defined",
|
||||
})
|
||||
return
|
||||
}
|
||||
var stats Stats
|
||||
err = c.BindJSON(&stats)
|
||||
if err != nil {
|
||||
logger.Error("GetStats", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Stat(stats.Database)
|
||||
if err != nil {
|
||||
logger.Error("GetStats", err)
|
||||
c.JSON(http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var returnStats = Stats{
|
||||
DatabaseSize: f.Size(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"data": returnStats,
|
||||
})
|
||||
}
|
||||
28
backend/utils/middleware.go
Normal file
28
backend/utils/middleware.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetMiddlewareData(r *gin.Engine, key string, data any) {
|
||||
//set logger for middleware
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set(key, data)
|
||||
c.Next()
|
||||
})
|
||||
}
|
||||
|
||||
func GetMiddlewareData(c *gin.Context, key string) (any, error) {
|
||||
// Retrieve logger from Gin context
|
||||
data, ok := c.Get("logger")
|
||||
if !ok {
|
||||
log.Fatal("middleware logger not set — use SetMiddlewareLogger first")
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, http.StatusInternalServerError)
|
||||
return nil, fmt.Errorf("middleware key '%s'not set ", key)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -63,6 +63,22 @@
|
||||
>
|
||||
<q-item-section>{{ $t('groups') }}</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="!autorized" to="/login" exact clickable v-ripple @click="closeDrawer">
|
||||
<q-item-section>{{ $t('login') }}</q-item-section>
|
||||
</q-item>
|
||||
<!-- <q-item
|
||||
v-if="autorized || user.isPermittedTo('members', 'read')"
|
||||
to="/report"
|
||||
exact
|
||||
clickable
|
||||
v-ripple
|
||||
@click="closeDrawer"
|
||||
>
|
||||
<q-item-section> {{ $t('report') }}</q-item-section>
|
||||
</q-item> -->
|
||||
<q-item v-if="autorized" to="/stats" exact clickable v-ripple @click="closeDrawer">
|
||||
<q-item-section> {{ $t('stats') }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
|
||||
116
src/pages/StatsPage.vue
Normal file
116
src/pages/StatsPage.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<site-title :title="$t('stats')" />
|
||||
<q-card class="q-ma-md">
|
||||
<q-card-section>
|
||||
<div class="row q-ma-md q-gutter-md">
|
||||
<q-card class="row col-auto">
|
||||
<div class="col">
|
||||
<h5 class="text-primary q-ma-md">{{ i18n.global.t('database') }}</h5>
|
||||
<q-card-section>
|
||||
<div class="column q-gutter-y-sm">
|
||||
<div class="row items-center">
|
||||
<p class="text-bold" style="min-width: 180px">
|
||||
{{ i18n.global.t('databaseSize') }}:
|
||||
</p>
|
||||
<p class="q-ml-md">{{ stats }}</p>
|
||||
</div>
|
||||
<div class="row items-center">
|
||||
<p class="text-bold" style="min-width: 180px">
|
||||
{{ i18n.global.t('numberOfMembers') }}:
|
||||
</p>
|
||||
<p class="q-ml-md">{{ amounts?.members }}</p>
|
||||
</div>
|
||||
<div class="row items-center">
|
||||
<p class="text-bold" style="min-width: 180px">
|
||||
{{ i18n.global.t('numberOfEvents') }}:
|
||||
</p>
|
||||
<p class="q-ml-md">{{ amounts?.events }}</p>
|
||||
</div>
|
||||
<div class="row items-center">
|
||||
<p class="text-bold" style="min-width: 180px">
|
||||
{{ i18n.global.t('numberOfResponsibles') }}:
|
||||
</p>
|
||||
<p class="q-ml-md">{{ amounts?.responsibles }}</p>
|
||||
</div>
|
||||
<div class="row items-center">
|
||||
<p class="text-bold" style="min-width: 180px">
|
||||
{{ i18n.global.t('numberOfGroups') }}:
|
||||
</p>
|
||||
<p class="q-ml-md">{{ amounts?.groups }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { appApi } from 'src/boot/axios';
|
||||
import { i18n } from 'src/boot/lang';
|
||||
import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
|
||||
import { useNotify } from 'src/vueLib/general/useNotify';
|
||||
import { databaseName } from 'src/vueLib/models/settings';
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
const stats = ref();
|
||||
const amounts = ref<{
|
||||
members: number;
|
||||
events: number;
|
||||
responsibles: number;
|
||||
groups: number;
|
||||
}>({
|
||||
members: 0,
|
||||
events: 0,
|
||||
responsibles: 0,
|
||||
groups: 0,
|
||||
});
|
||||
|
||||
const { NotifyResponse } = useNotify();
|
||||
|
||||
onMounted(async () => {
|
||||
stats.value = await appApi
|
||||
.post('/stats', { database: databaseName.value })
|
||||
.then((resp) => {
|
||||
if ((resp.data.databaseSize as number) >= 1000000000) {
|
||||
return (resp.data.data.databaseSize / 1000000000).toFixed(2) + ' GB';
|
||||
} else if ((resp.data.data.databaseSize as number) >= 1000000) {
|
||||
return (resp.data.data.databaseSize / 1000000).toFixed(2) + ' MB';
|
||||
} else if ((resp.data.data.databaseSize as number) >= 1000) {
|
||||
return (resp.data.data.databaseSize / 1000).toFixed(2) + ' kB';
|
||||
}
|
||||
return resp.data.data.databaseSize + ' B';
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
|
||||
amounts.value.members = await appApi
|
||||
.get('/members')
|
||||
.then((resp) => {
|
||||
return resp.data.length;
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
|
||||
amounts.value.events = await appApi
|
||||
.get('/events')
|
||||
.then((resp) => {
|
||||
return resp.data.length;
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
|
||||
amounts.value.responsibles = await appApi
|
||||
.get('responsible')
|
||||
.then((resp) => {
|
||||
return resp.data.length;
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
|
||||
amounts.value.groups = await appApi
|
||||
.get('/groups')
|
||||
.then((resp) => {
|
||||
return resp.data.length;
|
||||
})
|
||||
.catch((err) => NotifyResponse(err, 'error'));
|
||||
});
|
||||
</script>
|
||||
@@ -40,6 +40,7 @@ export default defineRouter(function (/* { store, ssrContext } */) {
|
||||
|
||||
Router.beforeEach((to, from, next) => {
|
||||
const userStore = useUserStore();
|
||||
|
||||
const isLoggedIn = userStore.isAuthenticated;
|
||||
if (!userStore.$state.firstLogin && to.path === '/firstlogin') {
|
||||
next('/login');
|
||||
@@ -51,7 +52,11 @@ export default defineRouter(function (/* { store, ssrContext } */) {
|
||||
to.meta.requiresAdmin &&
|
||||
!userStore.isPermittedTo(to.path.replace('/', ''), 'read')
|
||||
) {
|
||||
if (to.meta.noBackendAdmin) {
|
||||
next();
|
||||
} else {
|
||||
next('/');
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -37,6 +37,16 @@ const routes: RouteRecordRaw[] = [
|
||||
component: () => import('src/pages/GroupTable.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'report',
|
||||
component: () => import('src/pages/ReportPage.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: false },
|
||||
},
|
||||
{
|
||||
path: 'stats',
|
||||
component: () => import('src/pages/StatsPage.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true, noBackendAdmin: true },
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: () => import('pages/SettingsPage.vue'),
|
||||
|
||||
Reference in New Issue
Block a user