Monorepo for Aesthetic.Computer aesthetic.computer
at main 52 lines 1.7 kB view raw
1// Hearts, 26.02.25 2// Toggle and count hearts for any content type. 3// Used by chat-manager (session server) and the chat-heart Netlify function. 4 5export async function ensureIndexes(db) { 6 const collection = db.collection("hearts"); 7 try { 8 await collection.createIndex( 9 { type: 1, for: 1, user: 1 }, 10 { unique: true, background: true, name: "hearts_type_for_user_unique" }, 11 ); 12 await collection.createIndex( 13 { type: 1, for: 1 }, 14 { background: true, name: "hearts_type_for" }, 15 ); 16 } catch { 17 // Indexes already exist — safe to ignore 18 } 19} 20 21// Toggle a heart. Returns { hearted: bool, count: number }. 22export async function toggleHeart(db, { user, type, for: forId }) { 23 const collection = db.collection("hearts"); 24 let hearted; 25 try { 26 await collection.insertOne({ user, type, for: forId, when: new Date() }); 27 hearted = true; 28 } catch (err) { 29 if (err.code === 11000) { 30 await collection.deleteOne({ user, type, for: forId }); 31 hearted = false; 32 } else { 33 throw err; 34 } 35 } 36 const count = await collection.countDocuments({ type, for: forId }); 37 return { hearted, count }; 38} 39 40// Bulk-count hearts for a set of ids within a type. 41// Returns { [forId]: count } — missing ids have count 0. 42export async function countHearts(db, type, forIds) { 43 if (!forIds.length) return {}; 44 const collection = db.collection("hearts"); 45 const results = await collection 46 .aggregate([ 47 { $match: { type, for: { $in: forIds } } }, 48 { $group: { _id: "$for", count: { $sum: 1 } } }, 49 ]) 50 .toArray(); 51 return Object.fromEntries(results.map((r) => [r._id, r.count])); 52}