fix: replace error() with set.status across all API routes (fixes undefined error helper)
This commit is contained in:
parent
c89b63bd97
commit
bead49b870
|
|
@ -70,9 +70,9 @@ const COOKIE_OPTS = {
|
|||
|
||||
export const account = new Elysia({ prefix: "/account" })
|
||||
|
||||
.post("/login", async ({ body, cookie, set, request, error }) => {
|
||||
.post("/login", async ({ body, cookie, set, request }) => {
|
||||
const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
|
||||
if (!checkAuthRateLimit(ip, 10)) return error(429, { error: "Too many login attempts. Try again later." });
|
||||
if (!checkAuthRateLimit(ip, 10)) { set.status = 429; return { error: "Too many login attempts. Try again later." }; }
|
||||
|
||||
const key = (body.key as string)?.trim();
|
||||
if (!key) { set.status = 400; return { error: "Key required" }; }
|
||||
|
|
@ -94,9 +94,9 @@ export const account = new Elysia({ prefix: "/account" })
|
|||
set.redirect = "/dashboard";
|
||||
}, { detail: { hide: true } })
|
||||
|
||||
.post("/register", async ({ body, cookie, request, error }) => {
|
||||
.post("/register", async ({ body, cookie, request, set }) => {
|
||||
const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
|
||||
if (!checkAuthRateLimit(ip, 5)) return error(429, { error: "Too many registrations. Try again later." });
|
||||
if (!checkAuthRateLimit(ip, 5)) { set.status = 429; return { error: "Too many registrations. Try again later." }; }
|
||||
|
||||
const key = generateKey();
|
||||
const emailHash = body.email ? hashEmail(body.email) : null;
|
||||
|
|
@ -130,8 +130,8 @@ export const account = new Elysia({ prefix: "/account" })
|
|||
};
|
||||
})
|
||||
|
||||
.post("/email", async ({ accountId, keyId, body, error }) => {
|
||||
if (keyId) return error(403, { error: "Sub-keys cannot modify account email" });
|
||||
.post("/email", async ({ accountId, keyId, body, set }) => {
|
||||
if (keyId) { set.status = 403; return { error: "Sub-keys cannot modify account email" }; }
|
||||
const emailHash = body.email ? hashEmail(body.email) : null;
|
||||
await sql`UPDATE accounts SET email_hash = ${emailHash} WHERE id = ${accountId}`;
|
||||
return { ok: true };
|
||||
|
|
@ -141,16 +141,16 @@ export const account = new Elysia({ prefix: "/account" })
|
|||
}),
|
||||
})
|
||||
|
||||
.post("/reset-key", async ({ accountId, keyId, cookie, error }) => {
|
||||
if (keyId) return error(403, { error: "Sub-keys cannot rotate the account key" });
|
||||
.post("/reset-key", async ({ accountId, keyId, cookie, set }) => {
|
||||
if (keyId) { set.status = 403; return { error: "Sub-keys cannot rotate the account key" }; }
|
||||
const key = generateKey();
|
||||
await sql`UPDATE accounts SET key = ${key} WHERE id = ${accountId}`;
|
||||
cookie.pingql_key.set({ value: key, ...COOKIE_OPTS });
|
||||
return { key, message: "Primary key rotated. Your old key is now invalid." };
|
||||
})
|
||||
|
||||
.post("/keys", async ({ accountId, keyId, body, error }) => {
|
||||
if (keyId) return error(403, { error: "Sub-keys cannot create other sub-keys" });
|
||||
.post("/keys", async ({ accountId, keyId, body, set }) => {
|
||||
if (keyId) { set.status = 403; return { error: "Sub-keys cannot create other sub-keys" }; }
|
||||
const key = generateKey();
|
||||
const [created] = await sql`INSERT INTO api_keys (key, account_id, label) VALUES (${key}, ${accountId}, ${body.label}) RETURNING id`;
|
||||
return { key, id: created.id, label: body.label };
|
||||
|
|
@ -160,11 +160,11 @@ export const account = new Elysia({ prefix: "/account" })
|
|||
}),
|
||||
})
|
||||
|
||||
.delete("/keys/:id", async ({ accountId, keyId, params, error }) => {
|
||||
if (keyId) return error(403, { error: "Sub-keys cannot revoke other sub-keys" });
|
||||
.delete("/keys/:id", async ({ accountId, keyId, params, set }) => {
|
||||
if (keyId) { set.status = 403; return { error: "Sub-keys cannot revoke other sub-keys" }; }
|
||||
const [deleted] = await sql`
|
||||
DELETE FROM api_keys WHERE id = ${params.id} AND account_id = ${accountId} RETURNING id
|
||||
`;
|
||||
if (!deleted) return error(404, { error: "Key not found" });
|
||||
if (!deleted) { set.status = 404; return { error: "Key not found" }; }
|
||||
return { deleted: true };
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,11 +17,19 @@ setInterval(() => {
|
|||
}, 60 * 60 * 1000);
|
||||
|
||||
export const internal = new Elysia({ prefix: "/internal", detail: { hide: true } })
|
||||
.derive(({ headers, error }) => {
|
||||
if (!safeTokenCompare(headers["x-monitor-token"], process.env.MONITOR_TOKEN))
|
||||
return error(401, { error: "Unauthorized" });
|
||||
.derive(({ headers, set }) => {
|
||||
if (!safeTokenCompare(headers["x-monitor-token"], process.env.MONITOR_TOKEN)) {
|
||||
set.status = 401;
|
||||
return { _unauthorized: true };
|
||||
}
|
||||
return {};
|
||||
})
|
||||
.onBeforeHandle(({ _unauthorized, set }: any) => {
|
||||
if (_unauthorized) {
|
||||
set.status = 401;
|
||||
return { error: "Unauthorized" };
|
||||
}
|
||||
})
|
||||
|
||||
// Returns monitors due within the next `lookahead_ms` milliseconds (default 2000).
|
||||
// Nodes receive scheduled_at as an exact unix ms timestamp and sleep until that
|
||||
|
|
|
|||
|
|
@ -25,24 +25,26 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
|||
}, { detail: { summary: "List monitors", tags: ["monitors"] } })
|
||||
|
||||
// Create monitor
|
||||
.post("/", async ({ accountId, plan, body, error }) => {
|
||||
.post("/", async ({ accountId, plan, body, set }) => {
|
||||
const limits = getPlanLimits(plan);
|
||||
|
||||
// Enforce monitor count limit
|
||||
const [{ count }] = await sql`SELECT COUNT(*)::int as count FROM monitors WHERE account_id = ${accountId}`;
|
||||
if (count >= limits.maxMonitors) {
|
||||
return error(403, { error: `Plan limit reached: ${limits.maxMonitors} monitors (${plan}). Upgrade to create more.` });
|
||||
set.status = 403;
|
||||
return { error: `Plan limit reached: ${limits.maxMonitors} monitors (${plan}). Upgrade to create more.` };
|
||||
}
|
||||
|
||||
// Enforce minimum interval for plan
|
||||
const interval = body.interval_s ?? 30;
|
||||
if (interval < limits.minIntervalS) {
|
||||
return error(400, { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` });
|
||||
set.status = 400;
|
||||
return { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` };
|
||||
}
|
||||
|
||||
// SSRF protection
|
||||
const ssrfError = await validateMonitorUrl(body.url);
|
||||
if (ssrfError) return error(400, { error: ssrfError });
|
||||
if (ssrfError) { set.status = 400; return { error: ssrfError }; }
|
||||
|
||||
const regions = body.regions ?? [];
|
||||
const [monitor] = await sql`
|
||||
|
|
@ -63,11 +65,11 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
|||
}, { body: MonitorBody, detail: { summary: "Create monitor", tags: ["monitors"] } })
|
||||
|
||||
// Get monitor + recent status
|
||||
.get("/:id", async ({ accountId, params, error }) => {
|
||||
.get("/:id", async ({ accountId, params, set }) => {
|
||||
const [monitor] = await sql`
|
||||
SELECT * FROM monitors WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
`;
|
||||
if (!monitor) return error(404, { error: "Not found" });
|
||||
if (!monitor) { set.status = 404; return { error: "Not found" }; }
|
||||
|
||||
const results = await sql`
|
||||
SELECT * FROM pings WHERE monitor_id = ${params.id}
|
||||
|
|
@ -77,19 +79,20 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
|||
}, { detail: { summary: "Get monitor with results", tags: ["monitors"] } })
|
||||
|
||||
// Update monitor
|
||||
.patch("/:id", async ({ accountId, plan, params, body, error }) => {
|
||||
.patch("/:id", async ({ accountId, plan, params, body, set }) => {
|
||||
// Enforce minimum interval for plan
|
||||
if (body.interval_s != null) {
|
||||
const limits = getPlanLimits(plan);
|
||||
if (body.interval_s < limits.minIntervalS) {
|
||||
return error(400, { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` });
|
||||
set.status = 400;
|
||||
return { error: `Minimum interval for ${plan} plan is ${limits.minIntervalS}s` };
|
||||
}
|
||||
}
|
||||
|
||||
// SSRF protection on URL change
|
||||
if (body.url) {
|
||||
const ssrfError = await validateMonitorUrl(body.url);
|
||||
if (ssrfError) return error(400, { error: ssrfError });
|
||||
if (ssrfError) { set.status = 400; return { error: ssrfError }; }
|
||||
}
|
||||
|
||||
const [monitor] = await sql`
|
||||
|
|
@ -106,36 +109,36 @@ export const monitors = new Elysia({ prefix: "/monitors" })
|
|||
WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
RETURNING *
|
||||
`;
|
||||
if (!monitor) return error(404, { error: "Not found" });
|
||||
if (!monitor) { set.status = 404; return { error: "Not found" }; }
|
||||
return monitor;
|
||||
}, { body: t.Partial(MonitorBody), detail: { summary: "Update monitor", tags: ["monitors"] } })
|
||||
|
||||
// Delete monitor
|
||||
.delete("/:id", async ({ accountId, params, error }) => {
|
||||
.delete("/:id", async ({ accountId, params, set }) => {
|
||||
const [deleted] = await sql`
|
||||
DELETE FROM monitors WHERE id = ${params.id} AND account_id = ${accountId} RETURNING id
|
||||
`;
|
||||
if (!deleted) return error(404, { error: "Not found" });
|
||||
if (!deleted) { set.status = 404; return { error: "Not found" }; }
|
||||
return { deleted: true };
|
||||
}, { detail: { summary: "Delete monitor", tags: ["monitors"] } })
|
||||
|
||||
// Toggle enabled
|
||||
.post("/:id/toggle", async ({ accountId, params, error }) => {
|
||||
.post("/:id/toggle", async ({ accountId, params, set }) => {
|
||||
const [monitor] = await sql`
|
||||
UPDATE monitors SET enabled = NOT enabled
|
||||
WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
RETURNING id, enabled
|
||||
`;
|
||||
if (!monitor) return error(404, { error: "Not found" });
|
||||
if (!monitor) { set.status = 404; return { error: "Not found" }; }
|
||||
return monitor;
|
||||
}, { detail: { summary: "Toggle monitor on/off", tags: ["monitors"] } })
|
||||
|
||||
// Check history
|
||||
.get("/:id/pings", async ({ accountId, params, query, error }) => {
|
||||
.get("/:id/pings", async ({ accountId, params, query, set }) => {
|
||||
const [monitor] = await sql`
|
||||
SELECT id FROM monitors WHERE id = ${params.id} AND account_id = ${accountId}
|
||||
`;
|
||||
if (!monitor) return error(404, { error: "Not found" });
|
||||
if (!monitor) { set.status = 404; return { error: "Not found" }; }
|
||||
const limit = Math.min(Number(query.limit) || 100, 1000);
|
||||
return sql`
|
||||
SELECT * FROM pings
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ function makeSSEStream(accountId: string): Response {
|
|||
export const ingest = new Elysia()
|
||||
|
||||
// Internal: called by Rust monitor runner
|
||||
.post("/internal/ingest", async ({ body, headers, error }) => {
|
||||
.post("/internal/ingest", async ({ body, headers, set }) => {
|
||||
const token = headers["x-monitor-token"];
|
||||
if (!safeTokenCompare(token, process.env.MONITOR_TOKEN)) return error(401, { error: "Unauthorized" });
|
||||
if (!safeTokenCompare(token, process.env.MONITOR_TOKEN)) { set.status = 401; return { error: "Unauthorized" }; }
|
||||
|
||||
// Validate monitor exists
|
||||
const [monitor_check] = await sql`SELECT id FROM monitors WHERE id = ${body.monitor_id}`;
|
||||
if (!monitor_check) return error(404, { error: "Monitor not found" });
|
||||
if (!monitor_check) { set.status = 404; return { error: "Monitor not found" }; }
|
||||
|
||||
const meta = body.meta ? { ...body.meta } : {};
|
||||
if (body.cert_expiry_days != null) meta.cert_expiry_days = body.cert_expiry_days;
|
||||
|
|
|
|||
Loading…
Reference in New Issue