Skip to content

Instantly share code, notes, and snippets.

@LuisCusihuaman
Created July 19, 2024 05:04
Show Gist options
  • Save LuisCusihuaman/f06c9ad3387dd00cfa03ba557b2c52c9 to your computer and use it in GitHub Desktop.
Save LuisCusihuaman/f06c9ad3387dd00cfa03ba557b2c52c9 to your computer and use it in GitHub Desktop.
Server TCP ruby
require 'socket'
require 'fileutils'
require 'json'
require 'logger'
require 'time'
PORT = 80
UPLOADS_DIR = '/mnt/nfs/uploads'
PUBLIC_DIR = '/public'
MAX_BODY_SIZE = 10 * 1024 * 1024 # 10 MB
# Ensure the uploads directory exists
FileUtils.mkdir_p(UPLOADS_DIR)
server = TCPServer.new(PORT)
# Set up logging
log_file = File.open("server.log", "a")
logger = Logger.new(log_file)
logger.level = Logger::DEBUG
def read_full_body(client, content_length)
body = ''
while body.size < content_length
body << client.readpartial(content_length - body.size)
end
body
end
def handle_upload(request, client, logger)
content_length = request.scan(/Content-Length: (\d+)/i).flatten.first.to_i
body = read_full_body(client, content_length)
boundary = request.scan(/boundary=(.+)/).flatten.first
logger.debug("Boundary: #{boundary}")
parts = body.split("--#{boundary}")
files = []
parts.each do |part|
logger.debug("Part: #{part}")
if part.include?('Content-Disposition: form-data; name="file";')
timestamp = Time.now.to_i
filename = "image-#{timestamp}.png" # Ensure the file has a proper extension
logger.debug("Filename: #{filename}")
file_content = part.split("\r\n\r\n", 2).last.split("\r\n--").first
# Save to local disk
file_path = File.join(UPLOADS_DIR, filename)
File.open(file_path, 'wb') { |file| file.write(file_content) }
files << file_path.sub(UPLOADS_DIR, '/mnt/nfs/uploads')
end
end
files
rescue => e
logger.error("Error handling upload: #{e.message}")
logger.error(e.backtrace.join("\n"))
[]
end
def serve_html_with_images
files = Dir.glob(File.join(UPLOADS_DIR, '*.{jpg,jpeg,png,gif}'))
images_html = files.map { |file| "<img src='/mnt/nfs/uploads/#{File.basename(file)}' class='w-full h-auto border border-gray-300 rounded p-1'>" }.join('')
html_content = File.read(File.join(PUBLIC_DIR, 'index.html')).gsub('<!-- PHOTO_GRID_PLACEHOLDER -->', images_html)
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n#{html_content}"
end
loop do
Thread.start(server.accept) do |client|
begin
request = client.readpartial(2048)
logger.debug("Request: #{request}")
method, path, _ = request.lines[0].split(' ')
if method == 'POST' && path == '/upload'
file_paths = handle_upload(request, client, logger)
response = file_paths.to_json
client.puts "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: #{response.bytesize}\r\n\r\n#{response}"
elsif method == 'GET' && path == '/'
response = serve_html_with_images
client.puts response
elsif method == 'GET' && path.start_with?('/mnt/nfs/uploads')
file_path = File.join(UPLOADS_DIR, File.basename(path))
if File.exist?(file_path)
client.puts "HTTP/1.1 200 OK\r\nContent-Type: image/jpeg\r\nContent-Length: #{File.size(file_path)}\r\n\r\n"
IO.copy_stream(file_path, client)
else
client.puts "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"
end
else
client.puts "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n"
end
rescue => e
logger.error("Error processing request: #{e.message}")
logger.error(e.backtrace.join("\n"))
client.puts "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
ensure
client.close
end
end
end
@LuisCusihuaman
Copy link
Author

THIS WORKING, the TCP don't

require 'webrick'
require 'fileutils'
require 'json'
require 'logger'
require 'time'

UPLOADS_DIR = '/mnt/nfs/uploads'
PUBLIC_DIR = '/public'
MAX_BODY_SIZE = 10 * 1024 * 1024  # 10 MB

# Ensure the uploads directory exists
FileUtils.mkdir_p(UPLOADS_DIR)

# Set up logging
log_file = File.open("server.log", "a")
logger = Logger.new(log_file)
logger.level = Logger::DEBUG

server = WEBrick::HTTPServer.new(Port: 80, DocumentRoot: PUBLIC_DIR)

# Handle file uploads
server.mount_proc '/upload' do |req, res|
  if req.request_method == 'POST'
    begin
      boundary = req.content_type.match(/boundary=(.+)/)[1]
      logger.debug("Boundary: #{boundary}")
      body = req.body
      parts = body.split("--#{boundary}")

      files = []
      parts.each do |part|
        next unless part.include?('Content-Disposition: form-data; name="file";')
        
        timestamp = Time.now.to_i
        filename = "image-#{timestamp}.png"  # Ensure the file has a proper extension
        logger.debug("Filename: #{filename}")

        # Extract file content
        file_content = part.split("\r\n\r\n", 2).last.split("\r\n--").first

        # Save to local disk
        file_path = File.join(UPLOADS_DIR, filename)
        File.open(file_path, 'wb') { |file| file.write(file_content) }
        files << file_path.sub(UPLOADS_DIR, '/mnt/nfs/uploads')
      end
      
      res['Content-Type'] = 'application/json'
      res.body = files.to_json
    rescue => e
      logger.error("Error handling upload: #{e.message}")
      logger.error(e.backtrace.join("\n"))
      res.status = 500
      res.body = { error: 'Internal Server Error' }.to_json
    end
  else
    res.status = 405
    res.body = { error: 'Method Not Allowed' }.to_json
  end
end

# Serve static files from uploads directory
server.mount '/mnt/nfs/uploads', WEBrick::HTTPServlet::FileHandler, UPLOADS_DIR

# Serve HTML with embedded images
server.mount_proc '/' do |req, res|
  if req.request_method == 'GET'
    files = Dir.glob(File.join(UPLOADS_DIR, '*.{jpg,jpeg,png,gif}'))
    images_html = files.map { |file| "<img src='/mnt/nfs/uploads/#{File.basename(file)}' class='w-full h-auto border border-gray-300 rounded p-1'>" }.join('')
    html_content = File.read(File.join(PUBLIC_DIR, 'index.html')).gsub('<!-- PHOTO_GRID_PLACEHOLDER -->', images_html)
    res['Content-Type'] = 'text/html'
    res.body = html_content
  else
    res.status = 405
    res.body = { error: 'Method Not Allowed' }.to_json
  end
end

trap 'INT' do
  server.shutdown
end

server.start

@LuisCusihuaman
Copy link
Author

Screenshot 2024-07-19 at 2 22 39 AM

FRONTEND

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Photo Upload Grid</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
    <style>
        .disabled-btn {
            background-color: #d1d5db; /* Gray background */
            cursor: not-allowed;
            opacity: 0.5;
        }
    </style>
</head>
<body class="bg-gray-100 font-sans">
    <div class="container mx-auto p-4">
        <h1 class="text-3xl font-bold mb-4">Photo Upload Grid</h1>
        <div id="photoGrid" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
            <!-- PHOTO_GRID_PLACEHOLDER -->
        </div>
        <form id="uploadForm" class="mt-4">
            <input type="file" id="fileInput" accept="image/*" multiple class="hidden" onchange="previewImages()">
            <button type="button" onclick="fileInput.click()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Select Photos</button>
            <button type="submit" id="uploadButton" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded ml-2">Upload Photos</button>
        </form>
        <div id="previewGrid" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4 mt-4">
            <!-- PREVIEW_IMAGES_PLACEHOLDER -->
        </div>
    </div>
    <script>
        const previewImages = () => {
            const previewGrid = document.getElementById('previewGrid');
            previewGrid.innerHTML = '';
            Array.from(fileInput.files).forEach(file => {
                const reader = new FileReader();
                reader.onload = e => previewGrid.innerHTML += `<img src="${e.target.result}" class="w-full h-auto border border-gray-600 rounded p-1">`;
                reader.readAsDataURL(file);
            });
        };

        uploadForm.onsubmit = async event => {
            event.preventDefault();
            const uploadButton = document.getElementById('uploadButton');
            if (fileInput.files.length === 0) {
                console.log('No images selected');
                return; // No enviar si no hay imágenes seleccionadas
            }
            uploadButton.disabled = true; // Deshabilitar el botón para evitar múltiples envíos
            uploadButton.classList.add('disabled-btn'); // Añadir clase para estilo deshabilitado
            uploadButton.textContent = 'Uploading...'; // Cambiar el texto del botón

            const formData = new FormData();
            Array.from(fileInput.files).forEach(file => formData.append('file', file));
            try {
                const response = await fetch('/upload', { method: 'POST', body: formData });
                if (!response.ok) throw new Error('Upload failed.');
                // Recargar la página después de subir las imágenes
                window.location.reload();
            } catch (error) {
                console.error('Error during upload:', error);
                uploadButton.disabled = false; // Rehabilitar el botón en caso de error
                uploadButton.classList.remove('disabled-btn'); // Quitar clase de estilo deshabilitado
                uploadButton.textContent = 'Upload Photos'; // Restaurar el texto del botón
            }
        };
    </script>
</body>
</html>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment