···11-import { env } from "$env/dynamic/private";
22-import { scopeCookies } from "$lib";
33-import type { Cookies } from "@sveltejs/kit";
44-import { existsSync, readFileSync, writeFileSync } from "fs";
55-import { nanoid } from "nanoid";
66-import { get, writable } from "svelte/store";
11+import { env } from '$env/dynamic/private';
22+import { scopeCookies } from '$lib';
33+import type { Cookies } from '@sveltejs/kit';
44+import { existsSync, readFileSync, writeFileSync } from 'fs';
55+import { nanoid } from 'nanoid';
66+import { get, writable } from 'svelte/store';
7788-const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`
99-const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0'))
88+const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount`;
99+const visitCount = writable(
1010+ parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0')
1111+);
10121111-type Visitor = { visits: number[] }
1212-const lastVisitors = writable<Map<string, Visitor>>(new Map())
1313-const VISITOR_EXPIRY_SECONDS = 60 * 60 // an hour seems reasonable
1313+type Visitor = { visits: number[] };
1414+const lastVisitors = writable<Map<string, Visitor>>(new Map());
1515+const VISITOR_EXPIRY_SECONDS = 60 * 60; // an hour seems reasonable
14161517export const incrementVisitCount = (request: Request, cookies: Cookies) => {
1616- let currentVisitCount = get(visitCount)
1717- // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
1818- if (isBot(request)) { return currentVisitCount }
1919- const scopedCookies = scopeCookies(cookies, '/')
2020- // parse the last visit timestamp from cookies if it exists
2121- const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0")
2222- // get unix timestamp
2323- const currentTime = Date.now()
2424- const timeSinceVisit = currentTime - visitedTimestamp
2525- // check if this is the first time a client is visiting or if an hour has passed since they last visited
2626- if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
2727- // increment current and write to the store
2828- currentVisitCount += 1; visitCount.set(currentVisitCount)
2929- // update the cookie with the current timestamp
3030- scopedCookies.set('visitedTimestamp', currentTime.toString())
3131- // write the visit count to a file so we can load it later again
3232- writeFileSync(visitCountFile, currentVisitCount.toString())
3333- }
3434- return currentVisitCount
3535-}
1818+ let currentVisitCount = get(visitCount);
1919+ // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
2020+ if (isBot(request)) {
2121+ return currentVisitCount;
2222+ }
2323+ const scopedCookies = scopeCookies(cookies, '/');
2424+ // parse the last visit timestamp from cookies if it exists
2525+ const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || '0');
2626+ // get unix timestamp
2727+ const currentTime = Date.now();
2828+ const timeSinceVisit = currentTime - visitedTimestamp;
2929+ // check if this is the first time a client is visiting or if an hour has passed since they last visited
3030+ if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) {
3131+ // increment current and write to the store
3232+ currentVisitCount += 1;
3333+ visitCount.set(currentVisitCount);
3434+ // update the cookie with the current timestamp
3535+ scopedCookies.set('visitedTimestamp', currentTime.toString());
3636+ // write the visit count to a file so we can load it later again
3737+ writeFileSync(visitCountFile, currentVisitCount.toString());
3838+ }
3939+ return currentVisitCount;
4040+};
36413742export const addLastVisitor = (request: Request, cookies: Cookies) => {
3838- let visitors = get(lastVisitors)
3939- visitors = _addLastVisitor(visitors, request, cookies)
4040- lastVisitors.set(visitors)
4141- return visitors
4242-}
4343+ let visitors = get(lastVisitors);
4444+ visitors = _addLastVisitor(visitors, request, cookies);
4545+ lastVisitors.set(visitors);
4646+ return visitors;
4747+};
43484449export const getVisitorId = (cookies: Cookies) => {
4545- const scopedCookies = scopeCookies(cookies, '/')
4646- // parse the last visit timestamp from cookies if it exists
4747- return scopedCookies.get('visitorId')
4848-}
5050+ const scopedCookies = scopeCookies(cookies, '/');
5151+ // parse the last visit timestamp from cookies if it exists
5252+ return scopedCookies.get('visitorId');
5353+};
49545055// 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)
5156const _addLastVisitor = (visitors: Map<string, Visitor>, request: Request, cookies: Cookies) => {
5252- const currentTime = Date.now()
5353- // filter out old entries
5454- visitors.forEach((visitor, id, map) => {
5555- if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS)
5656- map.delete(id)
5757- else {
5858- visitor.visits = visitor.visits.filter((since) => {
5959- return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS
6060- })
6161- map.set(id, visitor)
6262- }
6363- })
6464- // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
6565- if (isBot(request)) { return visitors }
6666- const scopedCookies = scopeCookies(cookies, '/')
6767- // parse the last visit timestamp from cookies if it exists
6868- let visitorId = scopedCookies.get('visitorId') || ""
6969- // if no such id exists, create one and assign it to the client
7070- if (! visitors.has(visitorId)) {
7171- visitorId = nanoid()
7272- scopedCookies.set('visitorId', visitorId)
7373- console.log(`new client visitor id ${visitorId}`)
7474- }
7575- // update the entry
7676- let visitorEntry = visitors.get(visitorId) || {visits: []}
7777- // put new visit in the front
7878- visitorEntry.visits = [currentTime].concat(visitorEntry.visits)
7979- visitors.set(visitorId, visitorEntry);
8080- return visitors
8181-}
5757+ const currentTime = Date.now();
5858+ // filter out old entries
5959+ visitors.forEach((visitor, id, map) => {
6060+ if (currentTime - visitor.visits[0] > 1000 * VISITOR_EXPIRY_SECONDS) map.delete(id);
6161+ else {
6262+ visitor.visits = visitor.visits.filter((since) => {
6363+ return currentTime - since < 1000 * VISITOR_EXPIRY_SECONDS;
6464+ });
6565+ map.set(id, visitor);
6666+ }
6767+ });
6868+ // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots)
6969+ if (isBot(request)) {
7070+ return visitors;
7171+ }
7272+ const scopedCookies = scopeCookies(cookies, '/');
7373+ // parse the last visit timestamp from cookies if it exists
7474+ let visitorId = scopedCookies.get('visitorId') || '';
7575+ // if no such id exists, create one and assign it to the client
7676+ if (!visitors.has(visitorId)) {
7777+ visitorId = nanoid();
7878+ scopedCookies.set('visitorId', visitorId);
7979+ console.log(`new client visitor id ${visitorId}`);
8080+ }
8181+ // update the entry
8282+ const visitorEntry = visitors.get(visitorId) || { visits: [] };
8383+ // put new visit in the front
8484+ visitorEntry.visits = [currentTime].concat(visitorEntry.visits);
8585+ visitors.set(visitorId, visitorEntry);
8686+ return visitors;
8787+};
82888389const isBot = (request: Request) => {
8484- const ua = request.headers.get('user-agent')
8585- return ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null : true
8686-}
9090+ const ua = request.headers.get('user-agent');
9191+ return ua
9292+ ? ua.toLowerCase().match(/(bot|crawl|spider|walk|fetch|scrap|proxy|image)/) !== null
9393+ : true;
9494+};
87958896export const notifyDarkVisitors = (url: URL, request: Request) => {
8989- fetch('https://api.darkvisitors.com/visits', {
9090- method: 'POST',
9191- headers: {
9292- authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
9393- 'content-type': 'application/json',
9494- },
9595- body: JSON.stringify({
9696- request_path: url.pathname,
9797- request_method: request.method,
9898- request_headers: request.headers,
9999- })
100100- }).catch((why) => {
101101- console.log("failed sending dark visitors analytics:", why)
102102- return null
103103- }).then(async (resp) => {
104104- if (resp !== null) {
105105- const msg = await resp.json()
106106- const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`
107107- console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`)
108108- }
109109- })
110110-}9797+ fetch('https://api.darkvisitors.com/visits', {
9898+ method: 'POST',
9999+ headers: {
100100+ authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`,
101101+ 'content-type': 'application/json'
102102+ },
103103+ body: JSON.stringify({
104104+ request_path: url.pathname,
105105+ request_method: request.method,
106106+ request_headers: request.headers
107107+ })
108108+ })
109109+ .catch((why) => {
110110+ console.log('failed sending dark visitors analytics:', why);
111111+ return null;
112112+ })
113113+ .then(async (resp) => {
114114+ if (resp !== null) {
115115+ const msg = await resp.json();
116116+ const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})`;
117117+ console.log(
118118+ `sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`
119119+ );
120120+ }
121121+ });
122122+};