refactor: replace better-sqlite3 with lowdb for request details storage

- Removed better-sqlite3 dependency and implemented lowdb for JSON-based storage.
- Updated observability settings handling and configuration retrieval.
- Cleaned up unused observability settings UI components in ProfilePage.
- Adjusted contributors badge display in README for better visibility.
This commit is contained in:
decolua
2026-03-09 16:50:18 +07:00
parent 753a04b49e
commit 8c8eeecc70
5 changed files with 206 additions and 592 deletions
+11 -113
View File
@@ -223,24 +223,6 @@ export default function ProfilePage() {
}
};
const updateObservabilitySetting = async (key, value) => {
const numValue = parseInt(value);
if (isNaN(numValue) || numValue < 1) return;
try {
const res = await fetch("/api/settings", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ [key]: numValue }),
});
if (res.ok) {
setSettings(prev => ({ ...prev, [key]: numValue }));
}
} catch (err) {
console.error(`Failed to update ${key}:`, err);
}
};
const updateObservabilityEnabled = async (enabled) => {
try {
const res = await fetch("/api/settings", {
@@ -623,102 +605,18 @@ export default function ProfilePage() {
</div>
<h3 className="text-lg font-semibold">Observability</h3>
</div>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Enable Observability</p>
<p className="text-sm text-text-muted">
Turn request detail recording on/off globally
</p>
</div>
<Toggle
checked={observabilityEnabled}
onChange={updateObservabilityEnabled}
disabled={loading}
/>
</div>
<div className={cn("flex flex-col gap-4", !observabilityEnabled && "opacity-60")}>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Max Records</p>
<p className="text-sm text-text-muted">
Maximum request detail records to keep (older records are auto-deleted)
</p>
</div>
<Input
type="number"
min="100"
max="10000"
step="100"
value={settings.observabilityMaxRecords || 1000}
onChange={(e) => updateObservabilitySetting("observabilityMaxRecords", parseInt(e.target.value))}
disabled={loading || !observabilityEnabled}
className="w-28 text-center"
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Batch Size</p>
<p className="text-sm text-text-muted">
Number of items to accumulate before writing to database (higher = better performance)
</p>
</div>
<Input
type="number"
min="5"
max="100"
step="5"
value={settings.observabilityBatchSize || 20}
onChange={(e) => updateObservabilitySetting("observabilityBatchSize", parseInt(e.target.value))}
disabled={loading || !observabilityEnabled}
className="w-28 text-center"
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Flush Interval (ms)</p>
<p className="text-sm text-text-muted">
Maximum time to wait before flushing buffer (prevents data loss during low traffic)
</p>
</div>
<Input
type="number"
min="1000"
max="30000"
step="1000"
value={settings.observabilityFlushIntervalMs || 5000}
onChange={(e) => updateObservabilitySetting("observabilityFlushIntervalMs", parseInt(e.target.value))}
disabled={loading || !observabilityEnabled}
className="w-28 text-center"
/>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Max JSON Size (KB)</p>
<p className="text-sm text-text-muted">
Maximum size for each JSON field (request/response) before truncation
</p>
</div>
<Input
type="number"
min="100"
max="10240"
step="100"
value={settings.observabilityMaxJsonSize || 1024}
onChange={(e) => updateObservabilitySetting("observabilityMaxJsonSize", parseInt(e.target.value))}
disabled={loading || !observabilityEnabled}
className="w-28 text-center"
/>
</div>
<p className="text-xs text-text-muted italic pt-2 border-t border-border/50">
Current: Keeps {settings.observabilityMaxRecords || 1000} records, batches every {settings.observabilityBatchSize || 20} requests, max {settings.observabilityMaxJsonSize || 1024}KB per field
</p>
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Enable Observability</p>
<p className="text-sm text-text-muted">
Record request details for inspection in the logs view
</p>
</div>
<Toggle
checked={observabilityEnabled}
onChange={updateObservabilityEnabled}
disabled={loading}
/>
</div>
</Card>
+8 -28
View File
@@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import { getRequestDetailsDb } from "@/lib/requestDetailsDb";
import { getRequestDetails } from "@/lib/requestDetailsDb";
import { getProviderNodes } from "@/lib/localDb";
import { AI_PROVIDERS, getProviderByAlias } from "@/shared/constants/providers";
@@ -9,46 +9,26 @@ import { AI_PROVIDERS, getProviderByAlias } from "@/shared/constants/providers";
*/
export async function GET() {
try {
const db = await getRequestDetailsDb();
const { details } = await getRequestDetails({ pageSize: 9999 });
const stmt = db.prepare(`
SELECT DISTINCT provider
FROM request_details
WHERE provider IS NOT NULL AND provider != ''
ORDER BY provider ASC
`);
// Extract unique providers
const providerIds = [...new Set(details.map(r => r.provider).filter(Boolean))].sort();
const rows = stmt.all();
// Fetch all provider nodes to get names for custom providers
const providerNodes = await getProviderNodes();
const nodeMap = {};
for (const node of providerNodes) {
nodeMap[node.id] = node.name;
}
const providers = rows.map(row => {
const providerId = row.provider;
// Try to find name from various sources
const providers = providerIds.map(providerId => {
let name = providerId;
// 1. Check if it's a custom provider node
if (nodeMap[providerId]) {
name = nodeMap[providerId];
}
// 2. Check predefined providers
else {
} else {
const providerConfig = getProviderByAlias(providerId) || AI_PROVIDERS[providerId];
if (providerConfig?.name) {
name = providerConfig.name;
}
if (providerConfig?.name) name = providerConfig.name;
}
return {
id: providerId,
name
};
return { id: providerId, name };
});
return NextResponse.json({ providers });