diff --git a/src/lib/index.ts b/src/lib/index.ts index 12485ed..46540d3 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,7 +1,4 @@ import type { Cookies } from '@sveltejs/kit' -import { env } from '$env/dynamic/private' -import { get, writable } from 'svelte/store' -import { existsSync, readFileSync } from 'fs' export const scopeCookies = (cookies: Cookies, path: string) => { return { @@ -15,7 +12,4 @@ export const scopeCookies = (cookies: Cookies, path: string) => { cookies.delete(key, { ...props, path }) } } -} - -export const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount` -export const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0')); \ No newline at end of file +} \ No newline at end of file diff --git a/src/lib/visits.ts b/src/lib/visits.ts new file mode 100644 index 0000000..205136c --- /dev/null +++ b/src/lib/visits.ts @@ -0,0 +1,57 @@ +import { env } from "$env/dynamic/private"; +import { scopeCookies } from "$lib"; +import type { Cookies } from "@sveltejs/kit"; +import { existsSync, readFileSync, writeFileSync } from "fs"; +import { get, writable } from "svelte/store"; + +const visitCountFile = `${env.WEBSITE_DATA_DIR}/visitcount` +const visitCount = writable(parseInt(existsSync(visitCountFile) ? readFileSync(visitCountFile).toString() : '0')); + +export const incrementVisitCount = (request: Request, cookies: Cookies) => { + let currentVisitCount = get(visitCount) + // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) + const ua = request.headers.get('user-agent') + const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true + if (!isBot) { + const scopedCookies = scopeCookies(cookies, '/') + // parse the last visit timestamp from cookies if it exists + const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0") + // get unix timestamp + const currentTime = new Date().getTime() + const timeSinceVisit = currentTime - visitedTimestamp + // check if this is the first time a client is visiting or if an hour has passed since they last visited + if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) { + // increment current and write to the store + currentVisitCount += 1; visitCount.set(currentVisitCount) + // update the cookie with the current timestamp + scopedCookies.set('visitedTimestamp', currentTime.toString()) + // write the visit count to a file so we can load it later again + writeFileSync(visitCountFile, currentVisitCount.toString()) + } + } + return currentVisitCount +} + +export const notifyDarkVisitors = (url: URL, request: Request) => { + fetch('https://api.darkvisitors.com/visits', { + method: 'POST', + headers: { + authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`, + 'content-type': 'application/json', + }, + body: JSON.stringify({ + request_path: url.pathname, + request_method: request.method, + request_headers: request.headers, + }) + }).catch((why) => { + console.log("failed sending dark visitors analytics:", why) + return null + }).then(async (resp) => { + if (resp !== null) { + const msg = await resp.json() + const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})` + console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`) + } + }) +} \ No newline at end of file diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 0cc7f91..89a2d1a 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,7 +1,4 @@ -import { env } from '$env/dynamic/private'; -import { scopeCookies, visitCount, visitCountFile } from '$lib'; -import { writeFileSync } from 'fs'; -import { get } from 'svelte/store'; +import { incrementVisitCount, notifyDarkVisitors } from '$lib/visits.js'; export const csr = true; export const ssr = true; @@ -9,54 +6,12 @@ export const prerender = false; export const trailingSlash = 'always'; export async function load({ request, cookies, url, setHeaders, fetch }) { - fetch('https://api.darkvisitors.com/visits', { - method: 'POST', - headers: { - authorization: `Bearer ${env.DARK_VISITORS_TOKEN}`, - 'content-type': 'application/json', - }, - body: JSON.stringify({ - request_path: url.pathname, - request_method: request.method, - request_headers: request.headers, - }) - }).catch((why) => { - console.log("failed sending dark visitors analytics:", why) - return null - }).then(async (resp) => { - if (resp !== null) { - const msg = await resp.json() - const host = `(${request.headers.get('host')} ${request.headers.get('x-real-ip')})` - console.log(`sent visitor analytic to dark visitors: ${resp.statusText}; ${msg.message ?? ''}${host}`) - } - }) - - let currentVisitCount = get(visitCount) - // check whether the request is from a bot or not (this doesnt need to be accurate we just want to filter out honest bots) - const ua = request.headers.get('user-agent') - const isBot = ua ? ua.toLowerCase().match(/(bot|crawl|spider|walk)/) !== null : true - if (!isBot) { - const scopedCookies = scopeCookies(cookies, '/') - // parse the last visit timestamp from cookies if it exists - const visitedTimestamp = parseInt(scopedCookies.get('visitedTimestamp') || "0") - // get unix timestamp - const currentTime = new Date().getTime() - const timeSinceVisit = currentTime - visitedTimestamp - // check if this is the first time a client is visiting or if an hour has passed since they last visited - if (visitedTimestamp === 0 || timeSinceVisit > 1000 * 60 * 60 * 24) { - // increment current and write to the store - currentVisitCount += 1; visitCount.set(currentVisitCount) - // update the cookie with the current timestamp - scopedCookies.set('visitedTimestamp', currentTime.toString()) - // write the visit count to a file so we can load it later again - writeFileSync(visitCountFile, currentVisitCount.toString()) - } - } + notifyDarkVisitors(url, request) // no await so it doesnt block load setHeaders({ 'Cache-Control': 'no-cache' }) return { route: url.pathname, - visitCount: currentVisitCount, + visitCount: incrementVisitCount(request, cookies), } }