docs: add quixzoom-auth-core product to AAMOS

- Product documentation in docs/products/
- Updated MEMORY.md with product info
- quiXzoom Auth Core as AAMOS Identity product
This commit is contained in:
Bernt
2026-07-14 09:58:53 +00:00
parent 58ca4e68db
commit 48ea61cdcc
2730 changed files with 293827 additions and 7213 deletions
+83
View File
@@ -0,0 +1,83 @@
/**
* 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)}`;
}