Merge pull request 'amalia/20-mei-26' (#50) from amalia/20-mei-26 into join
Reviewed-on: #50
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sistem-desa-mandiri",
|
"name": "sistem-desa-mandiri",
|
||||||
"version": "0.1.14",
|
"version": "0.1.15",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --experimental-https",
|
"dev": "next dev --experimental-https",
|
||||||
|
|||||||
1
prisma/migrations/20260519080535_auto/migration.sql
Normal file
1
prisma/migrations/20260519080535_auto/migration.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-- This is an empty migration.
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { isValidApiKey } from "@/lib/apiKey";
|
||||||
import { prisma } from "@/module/_global";
|
import { prisma } from "@/module/_global";
|
||||||
import cors from "@elysiajs/cors";
|
import cors from "@elysiajs/cors";
|
||||||
import { swagger } from "@elysiajs/swagger";
|
import { swagger } from "@elysiajs/swagger";
|
||||||
@@ -6,20 +7,6 @@ import _ from "lodash";
|
|||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import "moment/locale/id";
|
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" })
|
const AiServer = new Elysia({ prefix: "/api/ai" })
|
||||||
.use(cors({
|
.use(cors({
|
||||||
origin: "*",
|
origin: "*",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { isValidApiKey } from "@/lib/apiKey";
|
||||||
import { prisma } from "@/module/_global";
|
import { prisma } from "@/module/_global";
|
||||||
import cors from "@elysiajs/cors";
|
import cors from "@elysiajs/cors";
|
||||||
import { swagger } from "@elysiajs/swagger";
|
import { swagger } from "@elysiajs/swagger";
|
||||||
@@ -11,20 +12,40 @@ const NocServer = new Elysia({ prefix: "/api/noc" })
|
|||||||
.use(cors({
|
.use(cors({
|
||||||
origin: "*",
|
origin: "*",
|
||||||
methods: ["GET", "POST", "OPTIONS"],
|
methods: ["GET", "POST", "OPTIONS"],
|
||||||
|
allowedHeaders: ["Content-Type", "x-api-key"],
|
||||||
}))
|
}))
|
||||||
.use(swagger({
|
.use(swagger({
|
||||||
path: "/docs", // Karena prefix instance adalah /api/noc, maka ini akan diakses di /api/noc/docs
|
path: "/docs",
|
||||||
documentation: {
|
documentation: {
|
||||||
info: {
|
info: {
|
||||||
title: "Sistem Desa Mandiri - NOC API",
|
title: "Sistem Desa Mandiri - NOC API",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "API Khusus untuk kebutuhan NOC (Network Operation Center) dan Monitoring Desa",
|
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: [
|
tags: [
|
||||||
{ name: "NOC", description: "Endpoint khusus monitoring" }
|
{ 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 /api/noc/active-divisions ──────────────────────────────────────────
|
||||||
.get(
|
.get(
|
||||||
|
|||||||
15
src/lib/apiKey.ts
Normal file
15
src/lib/apiKey.ts
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user