80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
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;
|