Skip to content

Commit

Permalink
analytics added for admins
Browse files Browse the repository at this point in the history
  • Loading branch information
bndrmrtn committed Dec 9, 2024
1 parent 26452e3 commit 05f6442
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 8 deletions.
4 changes: 3 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ RUN go mod download

COPY . .

RUN cp /app/config.yaml /app/config.file || cp /app/config.yml /app/config.file

RUN go build -o bin/mycloud

# Run the tests
Expand All @@ -20,6 +22,6 @@ FROM alpine:latest AS run
WORKDIR /app

COPY --from=build /app/bin /app
COPY --from=build /app/config.yaml /app/config.yaml
COPY --from=build /app/config.file /app/config.yaml

CMD ["./mycloud", "-"]
4 changes: 2 additions & 2 deletions backend/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ func NewApiServer(appConf *config.AppConfig, db *gorm.DB, rdb *redis.Client, sto
if conf.Mode == gale.Development {
// Add devtools in development mode
app.Use(gale.NewUIDevtools())
// Register pprof routes
middlewares.RegisterPprof(app.Router())
// Dump the routes in development mode
app.Dump()
}
Expand Down Expand Up @@ -100,6 +98,8 @@ func registerRoutes(r gale.Router, conf *config.AppConfig, db *gorm.DB, rdb *red
if conf.Application.Authorization.UseBlacklist {
admin.Get("/blacklist", handlers.HandleAdminGetBlacklist(db))
}

admin.Get("/analytics", handlers.HandleAdminGetAnalytics(db))
}

func registerValidators(r gale.RouterParamValidator) {
Expand Down
36 changes: 36 additions & 0 deletions backend/database/repository/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package repository

import "gorm.io/gorm"

type ContainerWithSize struct {
Container string `json:"container"`
Size int `json:"size"`
}

func GetOSFilesSeparatedByContainers(db *gorm.DB) ([]*ContainerWithSize, error) {
var files []*ContainerWithSize

result := db.Raw(`select container, file_size as size from os_files group by container`).Scan(&files)
return files, result.Error
}

func GetSizeDiff(db *gorm.DB) (map[string]int64, error) {
var files struct {
TotalFileSize int64 `json:"total_file_size"`
UniqueFileSize int64 `json:"unique_file_size"`
}

err := db.Raw(`
select sum(os_files.file_size) AS total_file_size, sum(DISTINCT os_files.file_size) AS unique_file_size
from files join os_files ON os_files.id = files.os_file_id
`).Scan(&files).Error

if err != nil {
return nil, err
}

return map[string]int64{
"total_file_size": files.TotalFileSize,
"unique_file_size": files.UniqueFileSize,
}, nil
}
12 changes: 12 additions & 0 deletions backend/handlers/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ func HandleAdminGetBlacklist(db *gorm.DB) gale.HandlerFunc {
return c.JSON(data)
}
}

func HandleAdminGetAnalytics(db *gorm.DB) gale.HandlerFunc {
return func(c gale.Ctx) error {
osFilesContainer, _ := repository.GetOSFilesSeparatedByContainers(db)
osFileSizeDiff, _ := repository.GetSizeDiff(db)

return c.JSON(gale.Map{
"os_file_container": osFilesContainer,
"file_difference": osFileSizeDiff,
})
}
}
2 changes: 0 additions & 2 deletions backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"flag"
"log"

_ "net/http/pprof"

"github.com/bndrmrtn/my-cloud/config"
"github.com/bndrmrtn/my-cloud/database"
"github.com/bndrmrtn/my-cloud/implementations"
Expand Down
32 changes: 32 additions & 0 deletions frontend/components/admin/admin-analytics.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<template v-if="d != null">
<h2 class="fredoka text-2xl">Analytics</h2>
<section class="md:grid md:grid-cols-2 md:gap-2">
<div class="p-2 mt-2 rounded-lg bg-widget">
<diff-chart :diff="d?.file_difference" />
</div>
<div class="p-2 mt-2 rounded-lg bg-widget">
<container-chart :containers="d?.os_file_container" />
</div>
</section>
</template>
</template>

<script setup lang="ts">
import DiffChart from "~/components/admin/diff-chart.vue";
import type {Analytics} from "~/types/analytics";
import {fetchAnalytics} from "~/scripts/analytics";
import {useLoaderStore} from "#imports";
import ContainerChart from "~/components/admin/container-chart.vue";
const loader = useLoaderStore()
const d = ref<Analytics|null>(null)
onMounted(async () => {
loader.start()
const res = await fetchAnalytics()
if(!res) return
d.value = res
loader.finish()
})
</script>
47 changes: 47 additions & 0 deletions frontend/components/admin/container-chart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import type {Containers} from "~/types/analytics";
import { Pie } from 'vue-chartjs'
const props = defineProps<{
containers: Containers
}>()
const getLightColor = () => {
let letters = 'BCDEF'.split('');
let color = '#';
for (let i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * letters.length)];
}
return color;
}
const getColors = (n: number): Array<string> => {
let colors = []
for(let i = 0; i < n; i++) {
colors.push(getLightColor())
}
return colors
}
const chartData = ref({
labels: [...props.containers.map(c => c.container)],
datasets: [
{
label: 'Bytes',
backgroundColor: [...getColors(props.containers.length)],
data: [...props.containers.map(c => c.size)],
},
],
})
const chartOptions = ref({
responsive: true,
maintainAspectRatio: false,
})
</script>

<template>
<Pie
:data="chartData"
:options="chartOptions"
/>
</template>
30 changes: 30 additions & 0 deletions frontend/components/admin/diff-chart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script setup lang="ts">
import type {FileDiff} from "~/types/analytics";
import { Bar } from 'vue-chartjs'
const props = defineProps<{
diff: FileDiff
}>()
const chartData = ref({
labels: ['Total', 'Unique'],
datasets: [
{
label: 'Bytes',
backgroundColor: '#847ef7',
data: [props.diff.total_file_size, props.diff.unique_file_size],
},
],
})
const chartOptions = ref({
responsive: true,
maintainAspectRatio: false,
})
</script>

<template>
<Bar
:data="chartData"
:options="chartOptions"
/>
</template>
30 changes: 30 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@codemirror/theme-one-dark": "^6.1.2",
"@pinia/nuxt": "^0.6.1",
"@vueuse/core": "^11.2.0",
"chart.js": "^4.4.7",
"floating-vue": "^5.2.2",
"i": "^0.3.7",
"mime": "^4.0.4",
Expand All @@ -31,6 +32,7 @@
"pretty-bytes": "^6.1.1",
"uuid": "^11.0.2",
"vue": "latest",
"vue-chartjs": "^5.3.2",
"vue-codemirror": "^6.1.1",
"vue-router": "latest",
"vue-tippy": "^6.5.0",
Expand Down
7 changes: 5 additions & 2 deletions frontend/pages/admin/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ onMounted(async () => {
<SpaceLayout>
<h1 class="fredoka text-3xl mb-5">Admin dashboard</h1>
<section v-if="conf">
<div class="grid grid-cols-2 gap-4">
<div class="md:grid md:grid-cols-2 md:gap-4">
<div>
<h2 class="fredoka text-2xl mb-5">Configuration</h2>
<div class="mt-2 bg-widget p-2 rounded-lg">
Expand Down Expand Up @@ -54,7 +54,7 @@ onMounted(async () => {
</p>
</div>
</div>
<div>
<div class="mt-2 md:mt-0">
<h2 class="fredoka text-2xl mb-5">Manage</h2>
<div class="mt-2 p-2">
<ButtonsButtonBluish class="mb-2" to="/admin/users">
Expand All @@ -76,5 +76,8 @@ onMounted(async () => {
</div>
</div>
</section>
<section class="mt-3">
<admin-analytics/>
</section>
</SpaceLayout>
</template>
5 changes: 5 additions & 0 deletions frontend/plugins/chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Chart, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, ArcElement } from 'chart.js'

export default defineNuxtPlugin(() => {
Chart.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ArcElement)
})
16 changes: 16 additions & 0 deletions frontend/scripts/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type {Collaborator} from "~/types/user";
import {newRequest} from "~/scripts/request";
import type {Analytics} from "~/types/analytics";

export const fetchAnalytics = async (): Promise<Analytics | null> => {
try {
const res = await newRequest(`/admin/analytics`);
if (res.status != 200) return null;
const data = await res.json();
return data as Analytics;
} catch (e: unknown) {
console.error(e);
return null;
}
};

1 change: 0 additions & 1 deletion frontend/scripts/collaborators.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type {Space} from "~/types/space";
import {newRequest} from "~/scripts/request";
import type {Collaborator} from "~/types/user";

Expand Down
15 changes: 15 additions & 0 deletions frontend/types/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

export interface FileDiff {
total_file_size: number
unique_file_size: number
}

export type Containers = Array<{
container: string
size: number
}>

export interface Analytics {
file_difference: FileDiff
os_file_container: Containers
}

0 comments on commit 05f6442

Please sign in to comment.