aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/server/middleware/cors.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-30 22:08:47 +0900
committernsfisis <nsfisis@gmail.com>2025-12-30 22:08:47 +0900
commitc2eb7513834eeb5adfa53fff897f585de87e4821 (patch)
tree9e914051ca67e2f9e1fa301119bdec398ec9e55f /src/server/middleware/cors.ts
parentb839cae49efd4b9d35c2868a4137101a4d71bd7f (diff)
downloadkioku-c2eb7513834eeb5adfa53fff897f585de87e4821.tar.gz
kioku-c2eb7513834eeb5adfa53fff897f585de87e4821.tar.zst
kioku-c2eb7513834eeb5adfa53fff897f585de87e4821.zip
feat(security): add rate limiting and CORS middleware
- Add rate limiting to login endpoint (5 requests/minute per IP) - Configure CORS middleware with environment-based origin control - Expose rate limit headers in CORS for client visibility - Update hono to 4.11.3 for rate limiter peer dependency 🤖 Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/server/middleware/cors.ts')
-rw-r--r--src/server/middleware/cors.ts42
1 files changed, 42 insertions, 0 deletions
diff --git a/src/server/middleware/cors.ts b/src/server/middleware/cors.ts
new file mode 100644
index 0000000..ce097ac
--- /dev/null
+++ b/src/server/middleware/cors.ts
@@ -0,0 +1,42 @@
+import { cors } from "hono/cors";
+
+/**
+ * CORS middleware configuration.
+ * Uses CORS_ORIGIN environment variable to configure allowed origins.
+ * If not set, defaults to same-origin only (no CORS headers).
+ *
+ * Examples:
+ * - CORS_ORIGIN=https://kioku.example.com (single origin)
+ * - CORS_ORIGIN=https://example.com,https://app.example.com (multiple origins)
+ */
+function getAllowedOrigins(): string[] {
+ const origins = process.env.CORS_ORIGIN;
+ if (!origins) {
+ return [];
+ }
+ return origins.split(",").map((o) => o.trim());
+}
+
+export function createCorsMiddleware() {
+ const allowedOrigins = getAllowedOrigins();
+
+ // If no origins configured, don't add CORS headers
+ if (allowedOrigins.length === 0) {
+ return cors({
+ origin: () => "",
+ });
+ }
+
+ return cors({
+ origin: allowedOrigins,
+ allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
+ allowHeaders: ["Content-Type", "Authorization"],
+ exposeHeaders: [
+ "RateLimit-Limit",
+ "RateLimit-Remaining",
+ "RateLimit-Reset",
+ ],
+ maxAge: 86400, // 24 hours
+ credentials: true,
+ });
+}