feat: tambah API Key Desa Plus ke Settings panel dan proxy
- Tambah kolom API_KEY_DESA_PLUS di CONFIG_DEFINITIONS (ditampilkan sebagai password field) - Proxy otomatis menyertakan X-API-Key header jika API key sudah dikonfigurasi Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1583,13 +1583,17 @@ export function createApp() {
|
|||||||
// ─── Desa Plus Proxy ───────────────────────────────────────────────────────
|
// ─── Desa Plus Proxy ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
.all('/api/proxy/desa-plus/*', async ({ request, set }) => {
|
.all('/api/proxy/desa-plus/*', async ({ request, set }) => {
|
||||||
const baseConfig = await prisma.appConfig.findUnique({ where: { key: 'URL_API_DESA_PLUS' } })
|
const [baseConfig, apiKeyConfig] = await Promise.all([
|
||||||
|
prisma.appConfig.findUnique({ where: { key: 'URL_API_DESA_PLUS' } }),
|
||||||
|
prisma.appConfig.findUnique({ where: { key: 'API_KEY_DESA_PLUS' } }),
|
||||||
|
])
|
||||||
if (!baseConfig?.value) { set.status = 503; return { error: 'URL_API_DESA_PLUS belum dikonfigurasi. Set di /dev → Settings.' } }
|
if (!baseConfig?.value) { set.status = 503; return { error: 'URL_API_DESA_PLUS belum dikonfigurasi. Set di /dev → Settings.' } }
|
||||||
const base = baseConfig.value.replace(/\/$/, '')
|
const base = baseConfig.value.replace(/\/$/, '')
|
||||||
const url = new URL(request.url)
|
const url = new URL(request.url)
|
||||||
const upstream = `${base}${url.pathname.replace('/api/proxy/desa-plus', '')}${url.search}`
|
const upstream = `${base}${url.pathname.replace('/api/proxy/desa-plus', '')}${url.search}`
|
||||||
const headers = new Headers(request.headers)
|
const headers = new Headers(request.headers)
|
||||||
headers.delete('host')
|
headers.delete('host')
|
||||||
|
if (apiKeyConfig?.value) headers.set('X-API-Key', apiKeyConfig.value)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(upstream, { method: request.method, headers, body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined })
|
const res = await fetch(upstream, { method: request.method, headers, body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body : undefined })
|
||||||
const contentType = res.headers.get('content-type') ?? 'application/json'
|
const contentType = res.headers.get('content-type') ?? 'application/json'
|
||||||
|
|||||||
@@ -1463,13 +1463,20 @@ function StaticFlowPanel({ graph, flowKey }: { graph: { nodes: Node[]; edges: Ed
|
|||||||
|
|
||||||
interface AppConfigEntry { key: string; value: string; updatedAt: string }
|
interface AppConfigEntry { key: string; value: string; updatedAt: string }
|
||||||
|
|
||||||
const CONFIG_DEFINITIONS: { key: string; label: string; description: string; placeholder: string }[] = [
|
const CONFIG_DEFINITIONS: { key: string; label: string; description: string; placeholder: string; secret?: boolean }[] = [
|
||||||
{
|
{
|
||||||
key: 'URL_API_DESA_PLUS',
|
key: 'URL_API_DESA_PLUS',
|
||||||
label: 'URL API Desa Plus',
|
label: 'URL API Desa Plus',
|
||||||
description: 'Base URL untuk API eksternal Desa Plus. Semua request dari frontend akan diproxy melalui server ke URL ini.',
|
description: 'Base URL untuk API eksternal Desa Plus. Semua request dari frontend akan diproxy melalui server ke URL ini.',
|
||||||
placeholder: 'https://api.desa-plus.example.com',
|
placeholder: 'https://api.desa-plus.example.com',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'API_KEY_DESA_PLUS',
|
||||||
|
label: 'API Key Desa Plus',
|
||||||
|
description: 'API key untuk autentikasi ke API Desa Plus. Dikirim otomatis sebagai header X-API-Key pada setiap request proxy.',
|
||||||
|
placeholder: 'your-secret-api-key',
|
||||||
|
secret: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
function SettingsPanel() {
|
function SettingsPanel() {
|
||||||
@@ -1535,6 +1542,7 @@ function SettingsPanel() {
|
|||||||
<Group gap="xs" align="flex-end">
|
<Group gap="xs" align="flex-end">
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<input
|
<input
|
||||||
|
type={def.secret ? 'password' : 'text'}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '8px 12px',
|
padding: '8px 12px',
|
||||||
|
|||||||
Reference in New Issue
Block a user