Milestone 3: NATS wait (gate 9), hash lookup, zappier timestamp, NS1 tunnel
Wait-via-NATS returns completed or pending+jobId (GATE 9). Mock SHA256
idempotent register + GET /hashes/{sha256}. Zappier commercial edge has
POST /v1/timestamp and hash-lookup. GATE 12 smoke (signup + wait) passes.
NS1 NATS is 127.0.0.1:4222 on 70.88.205.138; SSH tunnel :14222. Local
nats-server -js used for isolated tests. Activate app adds Echo Text.
Learned: JetStream on NS1 is loopback-only; do not bind 4222 public.
This commit is contained in:
parent
10c663cc0c
commit
51ae79b75f
75 changed files with 1114 additions and 123 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import path from 'path';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { createHash, randomUUID } from 'crypto';
|
||||
import express, { Express, NextFunction, Request, RequestHandler, Response } from 'express';
|
||||
import * as OpenApiValidator from 'express-openapi-validator';
|
||||
import swaggerUi from 'swagger-ui-express';
|
||||
|
|
@ -97,6 +97,10 @@ export function buildApp(deps: AppDeps = {}): {
|
|||
},
|
||||
};
|
||||
const items: StoredItem[] = [];
|
||||
const hashIndex = new Map<
|
||||
string,
|
||||
{ jobId: string; sha256: string; data?: string; timestamp: string }
|
||||
>();
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
|
@ -157,6 +161,36 @@ export function buildApp(deps: AppDeps = {}): {
|
|||
res.json({ output: text.toUpperCase(), quote: res.locals.quote });
|
||||
});
|
||||
|
||||
app.post('/v1/timestamp', meter('timestamp', usage, pricing), (req, res) => {
|
||||
const data = req.body?.data != null ? String(req.body.data) : '';
|
||||
const sha256 =
|
||||
(req.body?.sha256 && String(req.body.sha256).toLowerCase()) ||
|
||||
(data ? createHash('sha256').update(data, 'utf8').digest('hex') : '');
|
||||
if (!sha256) {
|
||||
res.status(400).json({ error: 'data or sha256 is required' });
|
||||
return;
|
||||
}
|
||||
const existing = hashIndex.get(sha256);
|
||||
if (existing) {
|
||||
res.status(202).json({ jobId: existing.jobId, sha256, existing: true, timestamp: existing.timestamp });
|
||||
return;
|
||||
}
|
||||
const jobId = randomUUID();
|
||||
const timestamp = new Date().toISOString();
|
||||
hashIndex.set(sha256, { jobId, sha256, data, timestamp });
|
||||
res.status(202).json({ jobId, sha256, existing: false, timestamp });
|
||||
});
|
||||
|
||||
app.get('/v1/hashes/:sha256', meter('hash-lookup', usage, pricing), (req, res) => {
|
||||
const sha256 = String(req.params.sha256 || '').toLowerCase();
|
||||
const rec = hashIndex.get(sha256);
|
||||
if (!rec) {
|
||||
res.status(404).json({ error: 'Hash not found' });
|
||||
return;
|
||||
}
|
||||
res.json({ exists: true, ...rec });
|
||||
});
|
||||
|
||||
app.post('/v1/add', meter('add', usage, pricing), (req, res) => {
|
||||
const number1 = Number(req.body?.number1);
|
||||
const number2 = Number(req.body?.number2);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ export const DEFAULT_RATE_CARD: RateCard = {
|
|||
'storage-list': { kind: 'free' },
|
||||
transform: { kind: 'fixed', fixedCents: 4 },
|
||||
add: { kind: 'free' },
|
||||
timestamp: { kind: 'fixed', fixedCents: 4 },
|
||||
'hash-lookup': { kind: 'free' },
|
||||
storage: { kind: 'variable', baseCents: 10, perKbCents: 1, perMbCents: 50 },
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue