/** * AAMOS API v1 — Shared utilities * Bildhantering, DB, auth helpers */ import { createHash } from 'crypto'; import { Pool } from 'pg'; // ── PostgreSQL pool ────────────────────────────────────────── const DB_URL = process.env.AMOS_DB_URL || process.env.DATABASE_URL; export const dbPool = new Pool({ connectionString: DB_URL, ssl: false, max: 5, idleTimeoutMillis: 30000, }); // ── Ensure aamos_api_results table exists ─────────────────── export async function ensureTable() { await dbPool.query(` CREATE TABLE IF NOT EXISTS aamos_api_results ( id SERIAL PRIMARY KEY, endpoint VARCHAR(32) NOT NULL, request_id VARCHAR(64) NOT NULL, input_hash VARCHAR(64), result JSONB NOT NULL, confidence NUMERIC(5,4), metadata JSONB, created_at TIMESTAMPTZ DEFAULT NOW() ) `); } // ── Save result to DB ──────────────────────────────────────── export async function saveResult(endpoint, requestId, inputHash, result, confidence, metadata = {}) { await ensureTable(); await dbPool.query( `INSERT INTO aamos_api_results (endpoint, request_id, input_hash, result, confidence, metadata) VALUES ($1,$2,$3,$4,$5,$6)`, [endpoint, requestId, inputHash, JSON.stringify(result), confidence, JSON.stringify(metadata)] ); } // ── Fetch image from URL or base64 ─────────────────────────── export async function fetchImage(input) { if (!input) throw new Error('No image input provided'); if (input.image_base64) { const buf = Buffer.from(input.image_base64.replace(/^data:image\/\w+;base64,/, ''), 'base64'); return { buffer: buf, source: 'base64' }; } if (input.image_url) { const r = await fetch(input.image_url, { signal: AbortSignal.timeout(15000) }); if (!r.ok) throw new Error(`Failed to fetch image: ${r.status}`); const buf = Buffer.from(await r.arrayBuffer()); return { buffer: buf, source: 'url' }; } throw new Error('image_url or image_base64 required'); } // ── Simple hash for input dedup ────────────────────────────── export function hashInput(buf) { return createHash('sha256').update(buf).digest('hex'); } // ── JWT auth middleware (Bearer token) ─────────────────────── export function requireAuth(req, res, next) { const auth = req.headers.authorization || ''; const token = auth.replace(/^Bearer\s+/i, ''); if (!token || token.length < 20) { return res.status(401).json({ error: 'Unauthorized', message: 'Bearer token required' }); } // Validate JWT structure (3 parts) const parts = token.split('.'); if (parts.length !== 3) { return res.status(401).json({ error: 'Unauthorized', message: 'Invalid token format' }); } req.token = token; next(); } // ── Request ID generator ───────────────────────────────────── export function genReqId() { return `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; }