Import source: backend/src/routes/analytics
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import { HttpError } from '../middleware/errorHandler.js';
|
||||||
|
import { store } from '../services/store.js';
|
||||||
|
import { getSessionId } from '../middleware/session.js';
|
||||||
|
import { getDailyBreakdown, getTopReferrers, recordEvent } from '../services/analytics.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/v1/files/:id/analytics
|
||||||
|
* Retrieve detailed analytics for a specific file owned by the user.
|
||||||
|
*/
|
||||||
|
router.get('/files/:id/analytics', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const sid = getSessionId(req);
|
||||||
|
const db = await store.read();
|
||||||
|
const file = db.files.find((f) => f.id === req.params.id && f.status === 'uploaded');
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
throw new HttpError(404, 'FILE_NOT_FOUND', 'File not found');
|
||||||
|
}
|
||||||
|
if (file.sessionId !== sid) {
|
||||||
|
throw new HttpError(403, 'ACCESS_DENIED', 'Access denied to this file analytics');
|
||||||
|
}
|
||||||
|
|
||||||
|
const dailyBreakdown = await getDailyBreakdown(file.linkId);
|
||||||
|
const topReferrers = await getTopReferrers(file.linkId);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
data: {
|
||||||
|
fileId: file.id,
|
||||||
|
linkId: file.linkId,
|
||||||
|
views: file.views,
|
||||||
|
downloads: file.downloads,
|
||||||
|
dailyBreakdown,
|
||||||
|
topReferrers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/v1/events
|
||||||
|
* Record an event (view or download) on a shared link.
|
||||||
|
*/
|
||||||
|
router.post('/events', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const { linkId, type } = req.body;
|
||||||
|
|
||||||
|
if (!linkId || !type || (type !== 'view' && type !== 'download')) {
|
||||||
|
throw new HttpError(400, 'INVALID_INPUT', 'Missing or invalid linkId or type');
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = await store.read();
|
||||||
|
const file = db.files.find((f) => f.linkId === linkId && f.status === 'uploaded');
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
throw new HttpError(404, 'LINK_NOT_FOUND', 'Active shared link not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const ip = req.ip;
|
||||||
|
const userAgent = req.headers['user-agent'] as string;
|
||||||
|
const referrer = (req.headers.referer || req.headers.referrer) as string;
|
||||||
|
|
||||||
|
await recordEvent(linkId, type, ip, userAgent, referrer);
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
data: {
|
||||||
|
status: 'recorded',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as analyticsRouter };
|
||||||
Reference in New Issue
Block a user