Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
660
marketing-agent/services/analytics/src/routes/index.js
Normal file
660
marketing-agent/services/analytics/src/routes/index.js
Normal file
@@ -0,0 +1,660 @@
|
||||
import Joi from '@hapi/joi';
|
||||
import { EventCollector } from '../services/EventCollector.js';
|
||||
import { MetricsProcessor } from '../services/MetricsProcessor.js';
|
||||
import { RealtimeAnalytics } from '../services/RealtimeAnalytics.js';
|
||||
import { ReportGenerator } from '../services/ReportGenerator.js';
|
||||
import { AlertManager } from '../services/AlertManager.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import dashboardRoutes from './dashboard.js';
|
||||
|
||||
const eventCollector = EventCollector.getInstance();
|
||||
const metricsProcessor = MetricsProcessor.getInstance();
|
||||
const realtimeAnalytics = RealtimeAnalytics.getInstance();
|
||||
const reportGenerator = ReportGenerator.getInstance();
|
||||
const alertManager = AlertManager.getInstance();
|
||||
|
||||
export default [
|
||||
...dashboardRoutes,
|
||||
// Event Collection Routes
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/events',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
type: Joi.string().required(),
|
||||
accountId: Joi.string().required(),
|
||||
userId: Joi.string().optional(),
|
||||
sessionId: Joi.string().optional(),
|
||||
action: Joi.string().required(),
|
||||
target: Joi.string().optional(),
|
||||
value: Joi.number().optional(),
|
||||
metadata: Joi.object().optional(),
|
||||
properties: Joi.object().optional()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await eventCollector.collectEvent(request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Event collection error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/events/bulk',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
events: Joi.array().items(
|
||||
Joi.object({
|
||||
type: Joi.string().required(),
|
||||
accountId: Joi.string().required(),
|
||||
userId: Joi.string().optional(),
|
||||
sessionId: Joi.string().optional(),
|
||||
action: Joi.string().required(),
|
||||
target: Joi.string().optional(),
|
||||
value: Joi.number().optional(),
|
||||
metadata: Joi.object().optional(),
|
||||
properties: Joi.object().optional()
|
||||
})
|
||||
).required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { events } = request.payload;
|
||||
const result = await eventCollector.collectBulkEvents(events);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Bulk event collection error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/events',
|
||||
options: {
|
||||
validate: {
|
||||
query: Joi.object({
|
||||
type: Joi.string().optional(),
|
||||
accountId: Joi.string().optional(),
|
||||
userId: Joi.string().optional(),
|
||||
startTime: Joi.date().iso().optional(),
|
||||
endTime: Joi.date().iso().optional(),
|
||||
limit: Joi.number().min(1).max(1000).default(100),
|
||||
offset: Joi.number().min(0).default(0),
|
||||
aggregation: Joi.string().valid('hourly', 'daily', 'by_type').optional()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await eventCollector.queryEvents(request.query);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Event query error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Metrics Routes
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/metrics/{metricId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
metricId: Joi.string().required()
|
||||
}),
|
||||
query: Joi.object({
|
||||
startTime: Joi.date().iso().optional(),
|
||||
endTime: Joi.date().iso().optional(),
|
||||
dimensions: Joi.object().optional(),
|
||||
limit: Joi.number().min(1).max(10000).default(1000)
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { metricId } = request.params;
|
||||
const result = await metricsProcessor.getMetric(metricId, request.query);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Metric query error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/metrics/{metricId}/summary',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
metricId: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { metricId } = request.params;
|
||||
const result = await metricsProcessor.getMetricSummary(metricId);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Metric summary error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/api/v1/metrics/{metricId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
metricId: Joi.string().required()
|
||||
}),
|
||||
payload: Joi.object({
|
||||
name: Joi.string(),
|
||||
description: Joi.string(),
|
||||
formula: Joi.string(),
|
||||
dimensions: Joi.array().items(Joi.string()),
|
||||
aggregations: Joi.array().items(Joi.string()),
|
||||
refreshInterval: Joi.number(),
|
||||
retentionDays: Joi.number(),
|
||||
active: Joi.boolean()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { metricId } = request.params;
|
||||
const result = await metricsProcessor.updateMetricDefinition(metricId, request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Metric update error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Real-time Analytics Routes
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/realtime/dashboard/{accountId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
accountId: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { accountId } = request.params;
|
||||
const result = await realtimeAnalytics.getDashboard(accountId);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Dashboard error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/realtime/subscribe',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
accountId: Joi.string().required(),
|
||||
metrics: Joi.array().items(Joi.string()).required(),
|
||||
filters: Joi.object().optional()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { accountId, metrics, filters } = request.payload;
|
||||
const subscriptionId = await realtimeAnalytics.subscribe(accountId, metrics, filters);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: { subscriptionId }
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Subscription error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/api/v1/realtime/subscribe/{subscriptionId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
subscriptionId: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { subscriptionId } = request.params;
|
||||
const result = realtimeAnalytics.unsubscribe(subscriptionId);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: { unsubscribed: result }
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Unsubscribe error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Report Generation Routes
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/reports/generate',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
accountId: Joi.string().required(),
|
||||
type: Joi.string().valid(
|
||||
'campaign_performance',
|
||||
'user_analytics',
|
||||
'ab_test'
|
||||
).required(),
|
||||
period: Joi.string().valid(
|
||||
'today', 'yesterday', 'this_week', 'last_week',
|
||||
'this_month', 'last_month', 'last_30_days', 'last_90_days'
|
||||
).optional(),
|
||||
startDate: Joi.date().iso().optional(),
|
||||
endDate: Joi.date().iso().optional(),
|
||||
filters: Joi.object().optional(),
|
||||
format: Joi.string().valid('json', 'pdf', 'excel', 'csv').default('json')
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await reportGenerator.generateReport(request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Report generation error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/reports/schedule',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
accountId: Joi.string().required(),
|
||||
type: Joi.string().required(),
|
||||
schedule: Joi.string().required(), // cron expression
|
||||
recipients: Joi.array().items(Joi.string()).required(),
|
||||
format: Joi.string().valid('pdf', 'excel', 'csv').default('pdf')
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await reportGenerator.scheduleReport(request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Report scheduling error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/reports/history',
|
||||
options: {
|
||||
validate: {
|
||||
query: Joi.object({
|
||||
accountId: Joi.string().required(),
|
||||
limit: Joi.number().min(1).max(100).default(20)
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { accountId, limit } = request.query;
|
||||
const result = await reportGenerator.getReportHistory(accountId, limit);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Report history error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Alert Management Routes
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/alerts/rules',
|
||||
options: {
|
||||
validate: {
|
||||
payload: Joi.object({
|
||||
ruleId: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
description: Joi.string().optional(),
|
||||
metric: Joi.string().required(),
|
||||
condition: Joi.object({
|
||||
operator: Joi.string().valid('>', '>=', '<', '<=', '=', '==', '!=').required(),
|
||||
threshold: Joi.number().required(),
|
||||
duration: Joi.number().min(0).default(300)
|
||||
}).required(),
|
||||
severity: Joi.string().valid('low', 'medium', 'high', 'critical').required(),
|
||||
channels: Joi.array().items(Joi.string()).required(),
|
||||
cooldown: Joi.number().min(0).default(1800),
|
||||
accountId: Joi.string().optional(),
|
||||
active: Joi.boolean().default(true)
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await alertManager.createAlertRule(request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert rule creation error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/api/v1/alerts/rules/{ruleId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
ruleId: Joi.string().required()
|
||||
}),
|
||||
payload: Joi.object({
|
||||
name: Joi.string(),
|
||||
description: Joi.string(),
|
||||
condition: Joi.object(),
|
||||
severity: Joi.string().valid('low', 'medium', 'high', 'critical'),
|
||||
channels: Joi.array().items(Joi.string()),
|
||||
cooldown: Joi.number(),
|
||||
active: Joi.boolean()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { ruleId } = request.params;
|
||||
const result = await alertManager.updateAlertRule(ruleId, request.payload);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert rule update error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/api/v1/alerts/rules/{ruleId}',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
ruleId: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { ruleId } = request.params;
|
||||
const result = await alertManager.deleteAlertRule(ruleId);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert rule deletion error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/alerts/history',
|
||||
options: {
|
||||
validate: {
|
||||
query: Joi.object({
|
||||
ruleId: Joi.string().optional(),
|
||||
severity: Joi.string().optional(),
|
||||
startTime: Joi.date().iso().optional(),
|
||||
endTime: Joi.date().iso().optional(),
|
||||
limit: Joi.number().min(1).max(1000).default(100)
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const result = await alertManager.getAlertHistory(request.query);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert history error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/alerts/{alertId}/acknowledge',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
alertId: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { alertId } = request.params;
|
||||
const result = await alertManager.acknowledgeAlert(alertId);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert acknowledge error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/api/v1/alerts/{alertId}/resolve',
|
||||
options: {
|
||||
validate: {
|
||||
params: Joi.object({
|
||||
alertId: Joi.string().required()
|
||||
}),
|
||||
payload: Joi.object({
|
||||
resolution: Joi.string().required()
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { alertId } = request.params;
|
||||
const { resolution } = request.payload;
|
||||
const result = await alertManager.resolveAlert(alertId, resolution);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert resolve error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/api/v1/alerts/stats',
|
||||
options: {
|
||||
validate: {
|
||||
query: Joi.object({
|
||||
period: Joi.string().valid('1h', '24h', '7d').default('24h')
|
||||
})
|
||||
},
|
||||
handler: async (request, h) => {
|
||||
try {
|
||||
const { period } = request.query;
|
||||
const result = await alertManager.getAlertStats(period);
|
||||
|
||||
return h.response({
|
||||
success: true,
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Alert stats error:', error);
|
||||
return h.response({
|
||||
success: false,
|
||||
error: error.message
|
||||
}).code(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
Reference in New Issue
Block a user