Compare commits

...

3 Commits

5 changed files with 40 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "sistem-desa-mandiri",
"version": "0.1.14",
"version": "0.1.15",
"private": true,
"scripts": {
"dev": "next dev --experimental-https",

View File

@@ -0,0 +1 @@
-- This is an empty migration.

View File

@@ -1,3 +1,4 @@
import { isValidApiKey } from "@/lib/apiKey";
import { prisma } from "@/module/_global";
import cors from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
@@ -6,20 +7,6 @@ import _ from "lodash";
import moment from "moment";
import "moment/locale/id";
const CACHE_TTL_MS = 60_000;
let apiKeyCache: Set<string> = new Set();
let cacheExpiresAt = 0;
async function isValidApiKey(incoming: string): Promise<boolean> {
const now = Date.now();
if (now > cacheExpiresAt) {
const rows = await prisma.apiKey.findMany({ where: { isActive: true }, select: { key: true } });
apiKeyCache = new Set(rows.map((r) => r.key));
cacheExpiresAt = now + CACHE_TTL_MS;
}
return apiKeyCache.has(incoming);
}
const AiServer = new Elysia({ prefix: "/api/ai" })
.use(cors({
origin: "*",

View File

@@ -1,3 +1,4 @@
import { isValidApiKey } from "@/lib/apiKey";
import { prisma } from "@/module/_global";
import cors from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
@@ -11,20 +12,40 @@ const NocServer = new Elysia({ prefix: "/api/noc" })
.use(cors({
origin: "*",
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["Content-Type", "x-api-key"],
}))
.use(swagger({
path: "/docs", // Karena prefix instance adalah /api/noc, maka ini akan diakses di /api/noc/docs
path: "/docs",
documentation: {
info: {
title: "Sistem Desa Mandiri - NOC API",
version: "1.0.0",
description: "API Khusus untuk kebutuhan NOC (Network Operation Center) dan Monitoring Desa",
},
components: {
securitySchemes: {
ApiKeyAuth: {
type: "apiKey",
in: "header",
name: "x-api-key",
},
},
},
security: [{ ApiKeyAuth: [] }],
tags: [
{ name: "NOC", description: "Endpoint khusus monitoring" }
]
}
}))
.onBeforeHandle(async ({ request, set, path }) => {
if (path.startsWith("/api/noc/docs")) return;
const incoming = request.headers.get("x-api-key");
if (!incoming || !(await isValidApiKey(incoming))) {
set.status = 401;
return { success: false, message: "Unauthorized" };
}
})
// ── GET /api/noc/active-divisions ──────────────────────────────────────────
.get(

15
src/lib/apiKey.ts Normal file
View File

@@ -0,0 +1,15 @@
import { prisma } from "@/module/_global";
const CACHE_TTL_MS = 60_000;
let apiKeyCache: Set<string> = new Set();
let cacheExpiresAt = 0;
export async function isValidApiKey(incoming: string): Promise<boolean> {
const now = Date.now();
if (now > cacheExpiresAt) {
const rows = await prisma.apiKey.findMany({ where: { isActive: true }, select: { key: true } });
apiKeyCache = new Set(rows.map((r) => r.key));
cacheExpiresAt = now + CACHE_TTL_MS;
}
return apiKeyCache.has(incoming);
}