Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154// Piece Hit, 2025.12.31// Track and retrieve piece analytics - hits, unique users, top fans.// POST: Record a hit (with optional auth for per-user tracking)// GET: Retrieve stats for a piece or top pieces overall
import { authorize } from "../../backend/authorization.mjs";import { connect } from "../../backend/database.mjs";import { respond } from "../../backend/http.mjs";
export async function handler(event) { let database; try { database = await connect(); } catch (err) { console.error("❌ Database connection failed:", err); return respond(500, { error: "Database connection failed" }); }
try { // GET: Return stats for a piece or all pieces if (event.httpMethod === "GET") { const { piece, top, users } = event.queryStringParameters || {}; const hitsCol = database.db.collection("piece-hits"); const userHitsCol = database.db.collection("piece-user-hits"); const handlesCol = database.db.collection("@handles");
if (piece) { const stats = await hitsCol.findOne({ piece });
// Optionally include top users for this piece let topUsers = []; if (users) { const userStats = await userHitsCol .find({ piece, user: { $ne: "anonymous" } }) .sort({ hits: -1 }) .limit(10) .toArray();
// Resolve handles from subs for (const u of userStats) { const handleDoc = await handlesCol.findOne( { user: u.user }, { projection: { _id: 1, handle: 1 } }, // never pull secret fields into memory ); topUsers.push({ handle: handleDoc?._id || null, hits: u.hits, lastHit: u.lastHit, }); } }
await database.disconnect(); return respond(200, { piece, hits: stats?.hits || 0, uniqueUsers: stats?.uniqueUsers || 0, firstHit: stats?.firstHit || null, lastHit: stats?.lastHit || null, topUsers: users ? topUsers : undefined, }); }
// Return top pieces overall const pieces = await hitsCol .find({}) .sort({ hits: -1 }) .limit(parseInt(top) || 50) .toArray();
await database.disconnect(); return respond(200, { pieces }); }
// POST: Record a hit if (event.httpMethod === "POST") { const body = JSON.parse(event.body || "{}"); const { piece, type = "system" } = body;
if (!piece) { await database.disconnect(); return respond(400, { error: "piece required" }); }
// Try to get user from auth header (silent fail for anonymous) let user = null; try { user = await authorize(event.headers); } catch (e) { /* anonymous hit */ }
const now = new Date(); const today = now.toISOString().split("T")[0]; const hitsCol = database.db.collection("piece-hits"); const userHitsCol = database.db.collection("piece-user-hits");
// Ensure indexes exist (safe to call multiple times) try { await hitsCol.createIndex({ piece: 1 }, { unique: true }); await hitsCol.createIndex({ hits: -1 }); await userHitsCol.createIndex({ piece: 1, user: 1 }, { unique: true }); await userHitsCol.createIndex({ piece: 1, hits: -1 }); } catch (indexErr) { // Indexes already exist - that's fine }
// 1. Update aggregate stats await hitsCol.updateOne( { piece }, { $inc: { hits: 1, [`daily.${today}.hits`]: 1, }, $set: { lastHit: now, type }, $setOnInsert: { firstHit: now, uniqueUsers: 0 }, }, { upsert: true }, );
// 2. Update per-user stats const userKey = user?.sub || "anonymous"; const userResult = await userHitsCol.updateOne( { piece, user: userKey }, { $inc: { hits: 1 }, $set: { lastHit: now }, $setOnInsert: { firstHit: now }, }, { upsert: true }, );
// If this was a new user for this piece, increment uniqueUsers if (userResult.upsertedCount > 0 && userKey !== "anonymous") { await hitsCol.updateOne( { piece }, { $inc: { uniqueUsers: 1, [`daily.${today}.unique`]: 1 } }, ); }
await database.disconnect(); return respond(200, { success: true }); }
await database.disconnect(); return respond(405, { error: "Method not allowed" }); } catch (err) { console.error("❌ piece-hit error:", err); if (database) await database.disconnect(); return respond(500, { error: "Internal server error" }); }}