Codex fixes
Some checks failed
Deploy Website / build-and-deploy (push) Has been cancelled

This commit is contained in:
2026-05-25 09:49:40 -04:00
parent 78ec3d58e3
commit 014b1836c0
101 changed files with 1048 additions and 7327 deletions

View File

@@ -2,39 +2,86 @@ import Database from 'better-sqlite3';
import type BetterSqlite3 from 'better-sqlite3';
import path from 'path';
import fs from 'fs';
import crypto from 'crypto';
let db: BetterSqlite3.Database | null = null;
let insertStmt: BetterSqlite3.Statement | null = null;
const runtimeAnalyticsSalt = crypto.randomBytes(32).toString('hex');
function getAnalyticsSalt() {
return process.env.ANALYTICS_SALT || runtimeAnalyticsSalt;
}
function getDbPath() {
if (process.env.VISIT_DB_PATH) {
return process.env.VISIT_DB_PATH;
}
const serverStorage = '/server_storage';
try {
fs.mkdirSync(serverStorage, { recursive: true, mode: 0o700 });
fs.accessSync(serverStorage, fs.constants.W_OK);
return path.join(serverStorage, 'visitors.db');
} catch {
const localStorage = path.join(process.cwd(), '.data');
fs.mkdirSync(localStorage, { recursive: true, mode: 0o700 });
return path.join(localStorage, 'visitors.db');
}
}
function createVisitorId(clientAddress: string, userAgent: string) {
const day = new Date().toISOString().slice(0, 10);
return crypto
.createHmac('sha256', getAnalyticsSalt())
.update(`${day}:${clientAddress}:${userAgent}`)
.digest('hex')
.slice(0, 32);
}
function ensureSchema(database: BetterSqlite3.Database) {
database.exec(`
CREATE TABLE IF NOT EXISTS visits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
visitor_id TEXT NOT NULL,
path TEXT NOT NULL,
visited_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);
const columns = database
.prepare("PRAGMA table_info('visits')")
.all() as { name: string; notnull: number }[];
const columnNames = new Set(columns.map((column) => column.name));
if (!columnNames.has('visitor_id')) {
database.exec("ALTER TABLE visits ADD COLUMN visitor_id TEXT NOT NULL DEFAULT 'legacy'");
columnNames.add('visitor_id');
}
if (!columnNames.has('path')) {
throw new Error('visits table is missing required path column');
}
return {
hasLegacyIpAddress: columnNames.has('ip_address'),
};
}
function getDb() {
if (db) return db;
try {
const SERVER_STORAGE = '/server_storage';
let dbDir: string;
try {
fs.accessSync(SERVER_STORAGE, fs.constants.W_OK);
dbDir = SERVER_STORAGE;
} catch {
dbDir = process.cwd();
}
const dbPath = path.join(dbDir, 'visitors.db');
const dbPath = getDbPath();
db = new Database(dbPath);
db.pragma('busy_timeout = 5000');
db.pragma('journal_mode = WAL');
db.pragma('synchronous = NORMAL');
db.exec(`
CREATE TABLE IF NOT EXISTS visits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip_address TEXT NOT NULL,
path TEXT NOT NULL,
visited_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`);
const schema = ensureSchema(db);
insertStmt = db.prepare(
'INSERT INTO visits (ip_address, path) VALUES (?, ?)'
);
insertStmt = schema.hasLegacyIpAddress
? db.prepare("INSERT INTO visits (ip_address, visitor_id, path) VALUES ('redacted', ?, ?)")
: db.prepare('INSERT INTO visits (visitor_id, path) VALUES (?, ?)');
return db;
} catch (e) {
@@ -45,8 +92,11 @@ function getDb() {
}
}
export function logVisit(ip: string, visitPath: string) {
export function logVisit(clientAddress: string, userAgent: string, visitPath: string) {
const database = getDb();
if (!database || !insertStmt) return;
insertStmt.run(ip, visitPath);
if (!database || !insertStmt) return null;
const visitorId = createVisitorId(clientAddress, userAgent);
insertStmt.run(visitorId, visitPath);
return visitorId;
}

10
lib/format.ts Normal file
View File

@@ -0,0 +1,10 @@
const postDateFormatter = new Intl.DateTimeFormat('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
timeZone: 'UTC',
});
export function formatPostDate(date: string) {
return postDateFormatter.format(new Date(date));
}

View File

@@ -17,11 +17,38 @@ export type Post = {
content: string;
};
function readPostMetadata(data: Record<string, unknown>, slug: string): PostMetadata {
if (
typeof data.title !== 'string' ||
typeof data.date !== 'string' ||
typeof data.description !== 'string'
) {
throw new Error(`Invalid frontmatter for post: ${slug}`);
}
if (!Number.isFinite(Date.parse(data.date))) {
throw new Error(`Invalid date for post: ${slug}`);
}
return {
title: data.title,
date: data.date,
description: data.description,
slug,
tags: Array.isArray(data.tags)
? data.tags.filter((tag): tag is string => typeof tag === 'string')
: undefined,
};
}
export function getPostSlugs() {
if (!fs.existsSync(postsDirectory)) {
return [];
}
return fs.readdirSync(postsDirectory);
return fs
.readdirSync(postsDirectory, { withFileTypes: true })
.filter((entry) => entry.isFile())
.map((entry) => entry.name);
}
export function getPostBySlug(slug: string): Post {
@@ -43,10 +70,7 @@ export function getPostBySlug(slug: string): Post {
const { data, content } = matter(fileContents);
return {
metadata: {
...data,
slug: realSlug,
} as PostMetadata,
metadata: readPostMetadata(data, realSlug),
content,
};
}
@@ -57,6 +81,6 @@ export function getAllPosts(): PostMetadata[] {
.filter((slug) => slug.endsWith('.mdx'))
.map((slug) => getPostBySlug(slug).metadata)
// Sort posts by date in descending order
.sort((post1, post2) => (post1.date > post2.date ? -1 : 1));
.sort((post1, post2) => new Date(post2.date).getTime() - new Date(post1.date).getTime());
return posts;
}

80
lib/request.ts Normal file
View File

@@ -0,0 +1,80 @@
const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
function sanitizeHeaderValue(value: string, maxLength: number) {
return value.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, maxLength) || 'unknown';
}
export function getClientAddress(headers: Headers) {
const forwarded = headers.get('x-forwarded-for');
if (forwarded) {
const addresses = forwarded
.split(',')
.map((value) => value.trim())
.filter(Boolean);
if (addresses.length > 0) {
// Use the nearest address. This avoids trusting a spoofed left-most value
// when Next or a reverse proxy appends the real peer address.
return sanitizeHeaderValue(addresses[addresses.length - 1], 128);
}
}
const realIp = headers.get('x-real-ip');
if (realIp) return sanitizeHeaderValue(realIp, 128);
return 'unknown';
}
export function getUserAgent(headers: Headers) {
return sanitizeHeaderValue(headers.get('user-agent') || 'unknown', 256);
}
export function normalizeVisitPath(value: unknown) {
if (typeof value !== 'string') return null;
const input = value.trim();
if (
input.length === 0 ||
input.length > 2048 ||
!input.startsWith('/') ||
input.startsWith('//') ||
CONTROL_CHARS.test(input)
) {
return null;
}
try {
const parsed = new URL(input, 'https://site.local');
if (parsed.origin !== 'https://site.local') return null;
return `${parsed.pathname}${parsed.search}`;
} catch {
return null;
}
}
export function isSameOriginRequest(request: Request) {
const host = request.headers.get('host');
if (!host) return false;
const requestHost = host.toLowerCase();
const origin = request.headers.get('origin');
if (origin) {
try {
return new URL(origin).host.toLowerCase() === requestHost;
} catch {
return false;
}
}
const referer = request.headers.get('referer');
if (referer) {
try {
return new URL(referer).host.toLowerCase() === requestHost;
} catch {
return false;
}
}
const fetchSite = request.headers.get('sec-fetch-site');
return fetchSite === 'same-origin' || fetchSite === 'none';
}