Fix security warnings
This commit is contained in:
116
supabase/functions/docker-logs/index.ts
Normal file
116
supabase/functions/docker-logs/index.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
|
||||
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.39.3';
|
||||
|
||||
const corsHeaders = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||
};
|
||||
|
||||
const DOCKER_SOCKET = '/var/run/docker.sock';
|
||||
|
||||
serve(async (req) => {
|
||||
if (req.method === 'OPTIONS') {
|
||||
return new Response(null, { headers: corsHeaders });
|
||||
}
|
||||
|
||||
try {
|
||||
const authHeader = req.headers.get('Authorization');
|
||||
if (!authHeader) {
|
||||
throw new Error('Missing authorization header');
|
||||
}
|
||||
|
||||
const supabase = createClient(
|
||||
Deno.env.get('SUPABASE_URL') ?? '',
|
||||
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
|
||||
{ global: { headers: { Authorization: authHeader } } }
|
||||
);
|
||||
|
||||
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
||||
if (userError || !user) {
|
||||
throw new Error('Unauthorized');
|
||||
}
|
||||
|
||||
const url = new URL(req.url);
|
||||
const containerId = url.searchParams.get('container');
|
||||
const tail = url.searchParams.get('tail') || '100';
|
||||
|
||||
if (!containerId) {
|
||||
throw new Error('Container ID required');
|
||||
}
|
||||
|
||||
// Connect to Docker socket
|
||||
const conn = await Deno.connect({ transport: "unix", path: DOCKER_SOCKET });
|
||||
|
||||
const request = `GET /containers/${containerId}/logs?stdout=true&stderr=true&tail=${tail}&follow=true HTTP/1.1\r\nHost: localhost\r\n\r\n`;
|
||||
await conn.write(new TextEncoder().encode(request));
|
||||
|
||||
// Create a readable stream from the socket
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = new Uint8Array(8192);
|
||||
let headerRead = false;
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const n = await conn.read(buffer);
|
||||
if (n === null) break;
|
||||
|
||||
let data = decoder.decode(buffer.subarray(0, n));
|
||||
|
||||
// Skip HTTP headers on first read
|
||||
if (!headerRead) {
|
||||
const headerEnd = data.indexOf('\r\n\r\n');
|
||||
if (headerEnd !== -1) {
|
||||
data = data.substring(headerEnd + 4);
|
||||
headerRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Docker logs use a special frame format:
|
||||
// [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
|
||||
// We'll parse and clean this
|
||||
const lines = data.split('\n').filter(line => {
|
||||
// Skip binary headers (first 8 bytes of each frame)
|
||||
if (line.length > 8) {
|
||||
return line.substring(8).trim().length > 0;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.length > 8) {
|
||||
const cleaned = line.substring(8).trim();
|
||||
if (cleaned) {
|
||||
controller.enqueue(new TextEncoder().encode(`data: ${cleaned}\n\n`));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Stream error:', error);
|
||||
} finally {
|
||||
conn.close();
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
...corsHeaders,
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Docker logs error:', error);
|
||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||
return new Response(JSON.stringify({ error: message }), {
|
||||
status: 500,
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user