feat(weeks4-5): operations endpoints + UI components with Recharts & Leaflet
This commit is contained in:
78
src/pages/api/god/logs.ts
Normal file
78
src/pages/api/god/logs.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { pool } from '@/lib/db';
|
||||
|
||||
/**
|
||||
* Live Log Streaming (SSE)
|
||||
* Stream database activity logs
|
||||
*/
|
||||
|
||||
function validateGodToken(request: Request): boolean {
|
||||
const token = request.headers.get('X-God-Token') ||
|
||||
request.headers.get('Authorization')?.replace('Bearer ', '') ||
|
||||
new URL(request.url).searchParams.get('token');
|
||||
|
||||
const godToken = process.env.GOD_MODE_TOKEN || import.meta.env.GOD_MODE_TOKEN;
|
||||
if (!godToken) return true;
|
||||
return token === godToken;
|
||||
}
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
if (!validateGodToken(request)) {
|
||||
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
|
||||
status: 401,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const lines = parseInt(url.searchParams.get('lines') || '100');
|
||||
|
||||
try {
|
||||
// Get recent pg_stat_activity as "logs"
|
||||
const result = await pool.query(`
|
||||
SELECT
|
||||
pid,
|
||||
usename,
|
||||
application_name,
|
||||
state,
|
||||
query,
|
||||
state_change,
|
||||
EXTRACT(EPOCH FROM (now() - query_start)) as duration_seconds
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = current_database()
|
||||
AND pid != pg_backend_pid()
|
||||
ORDER BY query_start DESC
|
||||
LIMIT $1
|
||||
`, [lines]);
|
||||
|
||||
// Format as log entries
|
||||
const logs = result.rows.map(row => ({
|
||||
timestamp: row.state_change,
|
||||
pid: row.pid,
|
||||
user: row.usename,
|
||||
app: row.application_name,
|
||||
state: row.state,
|
||||
duration: `${Math.round(row.duration_seconds)}s`,
|
||||
query: row.query?.substring(0, 200)
|
||||
}));
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
logs,
|
||||
count: logs.length,
|
||||
requested: lines,
|
||||
timestamp: new Date().toISOString()
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
} catch (error: any) {
|
||||
return new Response(JSON.stringify({
|
||||
error: 'Failed to fetch logs',
|
||||
details: error.message
|
||||
}), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user