import { query } from '../db/client.js' import type { ShortLinkCreateInput } from '../types/repository/inputs.js' import type { ShortLinkRow } from '../types/repository/rows.js' export async function createShortLink(input: ShortLinkCreateInput): Promise { const result = await query( ` INSERT INTO short_links ( code, target_url, target_url_hash, source, task_id, expires_at, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (source, task_id, target_url_hash) DO UPDATE SET target_url = EXCLUDED.target_url, expires_at = EXCLUDED.expires_at, updated_at = EXCLUDED.updated_at RETURNING * `, [ input.code, input.targetUrl, input.targetUrlHash, input.source || '', normalizeNullableId(input.taskId), input.expiresAt || null, input.createdAt, input.updatedAt, ], ) return result.rows[0] || null } export async function findShortLinkByCode(code: string): Promise { const normalizedCode = String(code || '').trim() if (!normalizedCode) { return null } const result = await query( ` SELECT * FROM short_links WHERE code = $1 LIMIT 1 `, [normalizedCode], ) return result.rows[0] || null } export async function markShortLinkVisited( code: string, visitedAt: string, ): Promise { const normalizedCode = String(code || '').trim() if (!normalizedCode) { return null } const result = await query( ` UPDATE short_links SET visit_count = visit_count + 1, last_visited_at = $2, updated_at = $2 WHERE code = $1 RETURNING * `, [normalizedCode, visitedAt], ) return result.rows[0] || null } function normalizeNullableId(value: unknown): number | null { if (value === null || value === undefined || value === '') { return null } const parsed = Number(value) return Number.isFinite(parsed) && parsed > 0 ? parsed : null }