diff --git a/frontend/src/app/adminui/adminui.component.html b/frontend/src/app/adminui/adminui.component.html
index 3429e3a..261f5ad 100644
--- a/frontend/src/app/adminui/adminui.component.html
+++ b/frontend/src/app/adminui/adminui.component.html
@@ -22,7 +22,7 @@
Operational For
-
2 Years
+
{{ statistics.applicationOnlineTime | duration }}
Since launch
@@ -38,7 +38,7 @@
Last Admin Login
-
2 days ago
+
{{ statistics.lastUserLogin | relativeTime }}
Most recent login
diff --git a/frontend/src/app/adminui/adminui.component.ts b/frontend/src/app/adminui/adminui.component.ts
index fd2b4b4..ad8e12c 100644
--- a/frontend/src/app/adminui/adminui.component.ts
+++ b/frontend/src/app/adminui/adminui.component.ts
@@ -6,6 +6,8 @@ import {DevelopmentStore} from "../../store/DevelopmentStore";
import {AuthStore} from "../../store/authStore";
import {Router} from "@angular/router";
import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe";
+import {DurationPipe} from "../duration.pipe";
+import {RelativeTimePipe} from "../relative-time.pipe";
@Component({
selector: 'app-adminui',
@@ -14,7 +16,9 @@ import {FormatFileSizePipePipe} from "../format-file-size-pipe.pipe";
DatePipe,
DecimalPipe,
NgForOf,
- FormatFileSizePipePipe
+ FormatFileSizePipePipe,
+ DurationPipe,
+ RelativeTimePipe
],
templateUrl: './adminui.component.html',
styleUrl: './adminui.component.scss'
@@ -25,9 +29,12 @@ export class AdminuiComponent {
expiredFileUploads: any[] = [];
totalFileSizeOnDisk: number = 0;
totalFileDownloads = 0;
+ statistics: any = {};
constructor(private developmentStore: DevelopmentStore, private authStore: AuthStore, private router: Router) {
- this.verifyToken();
+ setInterval(() => {
+ this.verifyToken();
+ }, 1000);
}
async verifyToken() {
@@ -38,12 +45,14 @@ export class AdminuiComponent {
}
await this.fetchFileUploads();
await this.fetchExpiredFileUploads();
+ await this.fetchStatistics();
await this.calculateStatistics();
}
async calculateStatistics() {
- console.log("Calculating statistics...");
- console.log(this.fileUploads)
+ this.totalFileSizeOnDisk = 0;
+ this.totalFileDownloads = 0;
+
for(let fileUpload of this.fileUploads) {
this.totalFileSizeOnDisk += fileUpload.fileSize;
}
@@ -86,4 +95,20 @@ export class AdminuiComponent {
console.error(error);
}
}
+ async fetchStatistics() {
+ try {
+ const response = await axios({
+ method: 'get',
+ url: this.developmentStore.getBaseUrl() + 'api/v1/secure/statistics',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + await firstValueFrom(this.authStore.token$)
+ }
+ });
+ this.statistics = response.data;
+ console.log(this.statistics)
+ } catch (error) {
+ console.error(error);
+ }
+ }
}
diff --git a/frontend/src/app/duration.pipe.spec.ts b/frontend/src/app/duration.pipe.spec.ts
new file mode 100644
index 0000000..e38345d
--- /dev/null
+++ b/frontend/src/app/duration.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { DurationPipe } from './duration.pipe';
+
+describe('DurationPipe', () => {
+ it('create an instance', () => {
+ const pipe = new DurationPipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/duration.pipe.ts b/frontend/src/app/duration.pipe.ts
new file mode 100644
index 0000000..f94ee9a
--- /dev/null
+++ b/frontend/src/app/duration.pipe.ts
@@ -0,0 +1,29 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'duration',
+ standalone: true
+})
+export class DurationPipe implements PipeTransform {
+
+ transform(value: number): string {
+ if (!value) {
+ return '0m';
+ }
+
+ let milliseconds = value;
+ const days = Math.floor(milliseconds / (24 * 60 * 60 * 1000));
+ milliseconds %= 24 * 60 * 60 * 1000;
+ const hours = Math.floor(milliseconds / (60 * 60 * 1000));
+ milliseconds %= 60 * 60 * 1000;
+ const minutes = Math.floor(milliseconds / (60 * 1000));
+
+ if (days > 0) {
+ return `${days}d ${hours}h ${minutes}m`;
+ } else if (hours > 0) {
+ return `${hours}h ${minutes}m`;
+ } else {
+ return `${minutes}m`;
+ }
+ }
+}
diff --git a/frontend/src/app/relative-time.pipe.spec.ts b/frontend/src/app/relative-time.pipe.spec.ts
new file mode 100644
index 0000000..ba62c09
--- /dev/null
+++ b/frontend/src/app/relative-time.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { RelativeTimePipe } from './relative-time.pipe';
+
+describe('RelativeTimePipe', () => {
+ it('create an instance', () => {
+ const pipe = new RelativeTimePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/relative-time.pipe.ts b/frontend/src/app/relative-time.pipe.ts
new file mode 100644
index 0000000..9016478
--- /dev/null
+++ b/frontend/src/app/relative-time.pipe.ts
@@ -0,0 +1,45 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'relativeTime',
+ standalone: true,
+ pure: false
+})
+export class RelativeTimePipe implements PipeTransform {
+
+ transform(value: string): string {
+ if (!value) {
+ return '';
+ }
+
+ const now = new Date();
+ const past = new Date(value);
+ const elapsed = now.getTime() - past.getTime();
+
+ const msPerSecond = 1000;
+ const msPerMinute = msPerSecond * 60;
+ const msPerHour = msPerMinute * 60;
+ const msPerDay = msPerHour * 24;
+ const msPerWeek = msPerDay * 7;
+ const msPerMonth = msPerDay * 30;
+ const msPerYear = msPerDay * 365;
+
+ if (elapsed < msPerSecond) {
+ return 'just now';
+ } else if (elapsed < msPerMinute) {
+ return `${Math.round(elapsed / msPerSecond)} seconds ago`;
+ } else if (elapsed < msPerHour) {
+ return `${Math.round(elapsed / msPerMinute)} minutes ago`;
+ } else if (elapsed < msPerDay) {
+ return `${Math.round(elapsed / msPerHour)} hours ago`;
+ } else if (elapsed < msPerWeek) {
+ return `${Math.round(elapsed / msPerDay)} days ago`;
+ } else if (elapsed < msPerMonth) {
+ return `${Math.round(elapsed / msPerWeek)} weeks ago`;
+ } else if (elapsed < msPerYear) {
+ return `${Math.round(elapsed / msPerMonth)} months ago`;
+ } else {
+ return `${Math.round(elapsed / msPerYear)} years ago`;
+ }
+ }
+}