data endpoint for entity 90008 (aka. a website)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at svelte 91 lines 3.2 kB view raw
1import { scopeCookies } from '$lib/index.ts'; 2import type { Cookies } from '@sveltejs/kit'; 3import { nanoid } from 'nanoid'; 4import { get, writable } from 'svelte/store'; 5import { darkVisitors } from './darkvisitors.ts'; 6 7export type Visitor = { visits: number[] }; 8export const lastVisitors = writable<Map<string, Visitor>>(new Map()); 9const VISITOR_EXPIRY_SECONDS = 60 * 60; // an hour seems reasonable 10 11export const removeLastVisitor = (id: string) => { 12 const visitors = get(lastVisitors); 13 if (visitors.has(id)) { 14 const visitor = visitors.get(id) ?? { visits: [] }; 15 visitor?.visits.shift(); 16 // if not enough visits remove 17 if (visitor?.visits.length === 0) { 18 visitors.delete(id); 19 } else { 20 visitors.set(id, visitor); 21 } 22 } 23 lastVisitors.set(visitors); 24}; 25 26export const addLastVisitor = (request: Request, cookies: Cookies) => { 27 const { visitors, visitorId } = _addLastVisitor(get(lastVisitors), request, cookies); 28 lastVisitors.set(visitors); 29 return visitorId; 30}; 31 32export const getVisitorId = (cookies: Cookies) => { 33 const scopedCookies = scopeCookies(cookies, '/'); 34 // parse the last visit timestamp from cookies if it exists 35 return scopedCookies.get('visitorId'); 36}; 37 38// why not use this for incrementVisitCount? cuz i wanna have separate visit counts (one per hour and one per day, per hour being recent visitors) 39const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => { 40 const currentTime = Date.now(); 41 // filter out old entries 42 visitors.forEach((visitor, id, map) => { 43 if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS) map.delete(id); 44 else { 45 visitor.visits = visitor.visits.filter((since) => { 46 return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS; 47 }); 48 map.set(id, visitor); 49 } 50 }); 51 // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) 52 if (isBot(request)) return { visitors, visitorId: null }; 53 const scopedCookies = scopeCookies(cookies, '/'); 54 // parse the last visit timestamp from cookies if it exists 55 let visitorId = scopedCookies.get('visitorId') || ''; 56 // if no such id exists, create one and assign it to the client 57 if (!visitors.has(visitorId)) { 58 visitorId = nanoid(); 59 scopedCookies.set('visitorId', visitorId); 60 console.log(`new client visitor id ${visitorId}`); 61 } 62 // update the entry 63 const visitorEntry = visitors.get(visitorId) || { visits: [] }; 64 // put new visit in the front 65 visitorEntry.visits = [currentTime].concat(visitorEntry.visits); 66 visitors.set(visitorId, visitorEntry); 67 return { 68 visitors, 69 visitorId 70 }; 71}; 72 73export const isBot = (request: Request) => { 74 const ua = request.headers.get('user-agent'); 75 return ua 76 ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null 77 : true; 78}; 79 80export const notifyDarkVisitors = (url: URL, request: Request) => { 81 const headers = Object.fromEntries(request.headers.entries()); 82 try { 83 darkVisitors.trackVisit({ 84 path: url.pathname, 85 method: request.method, 86 headers: headers 87 }); 88 } catch (error) { 89 console.error('failed to notify dark visitors:', error); 90 } 91};