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:
@@ -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)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user