[gemini] Implement POST /api/relay/policy endpoint (#46) #99
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
|
||||
import router from "./routes/index.js";
|
||||
import bootstrapRouter from "./routes/bootstrap.js"; // New: Bootstrap routes
|
||||
import adminRelayPanelRouter from "./routes/admin-relay-panel.js";
|
||||
import relayPolicyRouter from "./routes/relay-policy.js";
|
||||
import { requestIdMiddleware } from "./middlewares/request-id.js";
|
||||
import { responseTimeMiddleware } from "./middlewares/response-time.js";
|
||||
|
||||
@@ -57,6 +58,7 @@ app.use(responseTimeMiddleware);
|
||||
|
||||
app.use("/api", router);
|
||||
app.use("/api", bootstrapRouter); // New: Mount bootstrap routes
|
||||
app.use("/api", relayPolicyRouter);
|
||||
|
||||
// ── Relay admin panel at /admin/relay ────────────────────────────────────────
|
||||
// Served outside /api so the URL is clean: /admin/relay (not /api/admin/relay).
|
||||
|
||||
79
artifacts/api-server/src/routes/relay-policy.ts
Normal file
79
artifacts/api-server/src/routes/relay-policy.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { type Express, Router } from "express";
|
||||
import { z } from "zod";
|
||||
import { Status } from "../lib/http.js";
|
||||
import { rootLogger } from "../lib/logger.js";
|
||||
|
||||
const router = Router();
|
||||
const log = rootLogger.child({ service: "relay-policy" });
|
||||
|
||||
// ── Auth ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
const RELAY_POLICY_SECRET = process.env["RELAY_POLICY_SECRET"] ?? "";
|
||||
|
||||
if (!RELAY_POLICY_SECRET) {
|
||||
log.warn("RELAY_POLICY_SECRET is not set — /api/relay/policy will be unauthenticated!");
|
||||
}
|
||||
|
||||
function isAuthenticated(req: Express.Request): boolean {
|
||||
if (!RELAY_POLICY_SECRET) {
|
||||
return true; // No secret configured, so no auth.
|
||||
}
|
||||
const authz = req.headers["authorization"];
|
||||
if (!authz) {
|
||||
return false;
|
||||
}
|
||||
const [scheme, token] = authz.split(" ");
|
||||
if (scheme !== "Bearer" || token !== RELAY_POLICY_SECRET) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ── POST /api/relay/policy ────────────────────────────────────────────────────
|
||||
|
||||
const relayPolicyRequestSchema = z.object({
|
||||
event: z.object({
|
||||
id: z.string(),
|
||||
pubkey: z.string(),
|
||||
kind: z.number(),
|
||||
created_at: z.number(),
|
||||
tags: z.array(z.array(z.string())),
|
||||
content: z.string(),
|
||||
sig: z.string(),
|
||||
}),
|
||||
receivedAt: z.number(),
|
||||
sourceType: z.string(),
|
||||
sourceInfo: z.string(),
|
||||
});
|
||||
|
||||
type StrfryAction = "accept" | "reject" | "shadowReject";
|
||||
|
||||
router.post("/relay/policy", (req, res) => {
|
||||
if (!isAuthenticated(req)) {
|
||||
return res.status(Status.UNAUTHORIZED).json({
|
||||
action: "reject",
|
||||
msg: "unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
const parse = relayPolicyRequestSchema.safeParse(req.body);
|
||||
if (!parse.success) {
|
||||
log.warn("invalid /relay/policy request", { error: parse.error.format() });
|
||||
return res.status(Status.BAD_REQUEST).json({
|
||||
action: "reject",
|
||||
msg: "invalid request",
|
||||
});
|
||||
}
|
||||
|
||||
const eventId = parse.data.event.id;
|
||||
|
||||
// Bootstrap state: reject everything.
|
||||
// This will be extended by whitelist + moderation tasks.
|
||||
const action: StrfryAction = "reject";
|
||||
const msg = "bootstrapped: all events rejected";
|
||||
|
||||
log.info("policy decision", { eventId: eventId.slice(0, 8), action, msg });
|
||||
res.json({ id: eventId, action, msg });
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user