mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-20 08:43:37 +02:00
✨ feat(stats): add user statistics to Prometheus metrics and Grafana
- Add user metrics to mana-core-auth MetricsService: - auth_users_total: Total registered users - auth_users_verified: Email-verified users - auth_users_created_today/this_week/this_month - Create Grafana user-statistics dashboard with: - User overview stats (total, verified, verification rate, new today) - Registration period breakdown (today/week/month) - User growth trends over time - Enhance telegram-stats-bot /users command: - Add yesterday comparison with trends - Add week-over-week comparison - Add mini bar chart for last 7 days registration - Include user stats in daily Telegram report
This commit is contained in:
parent
9fedb7cfdd
commit
0cd2bc858a
6 changed files with 798 additions and 13 deletions
|
|
@ -1,9 +1,10 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { UmamiModule } from '../umami/umami.module';
|
||||
import { UsersModule } from '../users/users.module';
|
||||
import { AnalyticsService } from './analytics.service';
|
||||
|
||||
@Module({
|
||||
imports: [UmamiModule],
|
||||
imports: [UmamiModule, UsersModule],
|
||||
providers: [AnalyticsService],
|
||||
exports: [AnalyticsService],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { UmamiService, UmamiStats } from '../umami/umami.service';
|
||||
import { UsersService, UserStats } from '../users/users.service';
|
||||
import {
|
||||
formatDailyReport,
|
||||
formatWeeklyReport,
|
||||
formatRealtimeReport,
|
||||
formatStatsOverview,
|
||||
formatUsersReportCompact,
|
||||
} from './formatters';
|
||||
|
||||
@Injectable()
|
||||
export class AnalyticsService {
|
||||
private readonly logger = new Logger(AnalyticsService.name);
|
||||
|
||||
constructor(private readonly umamiService: UmamiService) {}
|
||||
constructor(
|
||||
private readonly umamiService: UmamiService,
|
||||
private readonly usersService: UsersService
|
||||
) {}
|
||||
|
||||
private getStartOfDay(date: Date = new Date()): Date {
|
||||
const start = new Date(date);
|
||||
|
|
@ -75,7 +80,15 @@ export class AnalyticsService {
|
|||
async generateDailyReport(): Promise<string> {
|
||||
try {
|
||||
const stats = await this.getTodayStats();
|
||||
return formatDailyReport(stats, new Date());
|
||||
let report = formatDailyReport(stats, new Date());
|
||||
|
||||
// Add user stats to daily report
|
||||
const userStats = await this.usersService.getUserStats();
|
||||
if (userStats) {
|
||||
report += formatUsersReportCompact(userStats);
|
||||
}
|
||||
|
||||
return report;
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to generate daily report:', error);
|
||||
return '❌ Fehler beim Erstellen des Daily Reports';
|
||||
|
|
|
|||
|
|
@ -203,27 +203,103 @@ Verfügbare Befehle:
|
|||
• Weekly: Jeden Montag um 9:00`;
|
||||
}
|
||||
|
||||
export interface DailyRegistration {
|
||||
date: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface UserStats {
|
||||
totalUsers: number;
|
||||
verifiedUsers: number;
|
||||
todayNewUsers: number;
|
||||
yesterdayNewUsers: number;
|
||||
weekNewUsers: number;
|
||||
lastWeekNewUsers: number;
|
||||
monthNewUsers: number;
|
||||
dailyRegistrations: DailyRegistration[];
|
||||
}
|
||||
|
||||
function createMiniBarChart(dailyRegistrations: DailyRegistration[]): string[] {
|
||||
if (dailyRegistrations.length === 0) return [];
|
||||
|
||||
const maxCount = Math.max(...dailyRegistrations.map((d) => d.count), 1);
|
||||
const barChars = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
||||
|
||||
// Fill in missing days and sort
|
||||
const last7Days: DailyRegistration[] = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const found = dailyRegistrations.find((d) => d.date === dateStr);
|
||||
last7Days.push({ date: dateStr, count: found?.count || 0 });
|
||||
}
|
||||
|
||||
const bars = last7Days.map((d) => {
|
||||
const index = Math.floor((d.count / maxCount) * (barChars.length - 1));
|
||||
return barChars[Math.max(0, index)];
|
||||
});
|
||||
|
||||
const dayLabels = last7Days.map((d) => {
|
||||
const date = new Date(d.date);
|
||||
return ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'][date.getDay()];
|
||||
});
|
||||
|
||||
return [`<code>${bars.join('')}</code>`, `<code>${dayLabels.join('')}</code>`];
|
||||
}
|
||||
|
||||
export function formatUsersReport(stats: UserStats): string {
|
||||
const verificationRate =
|
||||
stats.totalUsers > 0 ? Math.round((stats.verifiedUsers / stats.totalUsers) * 100) : 0;
|
||||
|
||||
// Calculate trends
|
||||
const dailyTrend =
|
||||
stats.yesterdayNewUsers > 0
|
||||
? ((stats.todayNewUsers - stats.yesterdayNewUsers) / stats.yesterdayNewUsers) * 100
|
||||
: stats.todayNewUsers > 0
|
||||
? 100
|
||||
: 0;
|
||||
|
||||
const weeklyTrend =
|
||||
stats.lastWeekNewUsers > 0
|
||||
? ((stats.weekNewUsers - stats.lastWeekNewUsers) / stats.lastWeekNewUsers) * 100
|
||||
: stats.weekNewUsers > 0
|
||||
? 100
|
||||
: 0;
|
||||
|
||||
const lines: string[] = [
|
||||
'👥 <b>ManaCore User Statistics</b>',
|
||||
'━━━━━━━━━━━━━━━━━━━━',
|
||||
'',
|
||||
`👤 <b>Gesamt:</b> ${formatNumber(stats.totalUsers)}`,
|
||||
`✅ <b>Verifiziert:</b> ${formatNumber(stats.verifiedUsers)}`,
|
||||
'<b>📊 Übersicht</b>',
|
||||
` 👤 Gesamt: <b>${formatNumber(stats.totalUsers)}</b>`,
|
||||
` ✅ Verifiziert: ${formatNumber(stats.verifiedUsers)} (${verificationRate}%)`,
|
||||
'',
|
||||
'<b>📊 Neue Registrierungen:</b>',
|
||||
` Heute: +${formatNumber(stats.todayNewUsers)}`,
|
||||
` Diese Woche: +${formatNumber(stats.weekNewUsers)}`,
|
||||
'<b>📈 Neue Registrierungen</b>',
|
||||
` Heute: <b>+${formatNumber(stats.todayNewUsers)}</b> ${formatChangeEmoji(dailyTrend)}`,
|
||||
` Gestern: +${formatNumber(stats.yesterdayNewUsers)}`,
|
||||
` Diese Woche: +${formatNumber(stats.weekNewUsers)} ${formatChange(weeklyTrend)} ${formatChangeEmoji(weeklyTrend)}`,
|
||||
` Dieser Monat: +${formatNumber(stats.monthNewUsers)}`,
|
||||
];
|
||||
|
||||
// Add mini bar chart for last 7 days
|
||||
if (stats.dailyRegistrations.length > 0) {
|
||||
lines.push('');
|
||||
lines.push('<b>📅 Letzte 7 Tage</b>');
|
||||
lines.push(...createMiniBarChart(stats.dailyRegistrations));
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export function formatUsersReportCompact(stats: UserStats): string {
|
||||
const verificationRate =
|
||||
stats.totalUsers > 0 ? Math.round((stats.verifiedUsers / stats.totalUsers) * 100) : 0;
|
||||
|
||||
return [
|
||||
'',
|
||||
'<b>👥 Registrierte User</b>',
|
||||
` Gesamt: <b>${formatNumber(stats.totalUsers)}</b> (${verificationRate}% verifiziert)`,
|
||||
` Heute: +${formatNumber(stats.todayNewUsers)} | Woche: +${formatNumber(stats.weekNewUsers)} | Monat: +${formatNumber(stats.monthNewUsers)}`,
|
||||
].join('\n');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue