79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
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' }
|
|
});
|
|
}
|
|
};
|