view mrjunejune/main.c @ 205:e07b4b5a66bb

Bad named files.
author MrJuneJune <me@mrjunejune.com>
date Sun, 15 Feb 2026 11:07:52 -0800
parents e5aed6c36672
children 240337164a80
line wrap: on
line source

#include "seobeo/seobeo.h"
#include "markdown_converter/markdown_to_html.h"
#include "s3/s3_uploader.h"
#include "deita/deita.h"
#include <time.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <pthread.h>

// UUID + /tmp/ + format (max 4)
#define TMP_FILE_LENGTH 47
#define UUID_LEN 37

volatile sig_atomic_t stop_server = 0;
static _Atomic uint32_t counter = 0;

// Media Processing Context for background threads
typedef struct {
  int64    media_id;
  char     s3_key_original[512];
  char     s3_key_processed[512];
  char     content_type[128];
  char     access_token[256];
  char     db_path[256];
  S3_Config s3_config;
} Media_Processing_Context;

// Server configuration (loaded from .config)
static char g_upload_auth_token[256] = {0};
static char g_s3_region[64] = "us-west-2";
static char g_s3_bucket[128] = "mrjunejune";
static char g_s3_cloudfront_url[256] = {0};
static char g_db_path[256] = "mrjunejune/data/mrjunejune.db";
static int  g_s3_url_expires = 3600;
static S3_Config g_s3_config = {0};
static Deita_Connection *g_db_connection = NULL;

static void load_config(const char *config_path)
{
  FILE *f = fopen(config_path, "r");
  if (!f)
  {
    printf("[CONFIG] Warning: Could not open %s, using defaults\n", config_path);
    return;
  }

  char line[512];
  while (fgets(line, sizeof(line), f))
  {
    // Skip comments and empty lines
    if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') continue;

    char *eq = strchr(line, '=');
    if (!eq) continue;

    *eq = '\0';
    char *key = line;
    char *value = eq + 1;

    // Trim newline from value
    size_t vlen = strlen(value);
    while (vlen > 0 && (value[vlen-1] == '\n' || value[vlen-1] == '\r'))
      value[--vlen] = '\0';

    if (strcmp(key, "UPLOAD_AUTH_TOKEN") == 0)
    {
      strncpy(g_upload_auth_token, value, sizeof(g_upload_auth_token) - 1);
    }
    else if (strcmp(key, "S3_REGION") == 0)
    {
      strncpy(g_s3_region, value, sizeof(g_s3_region) - 1);
    }
    else if (strcmp(key, "S3_BUCKET") == 0)
    {
      strncpy(g_s3_bucket, value, sizeof(g_s3_bucket) - 1);
    }
    else if (strcmp(key, "S3_URL_EXPIRES") == 0)
    {
      g_s3_url_expires = atoi(value);
    }
    else if (strcmp(key, "S3_CLOUDFRONT_URL") == 0)
    {
      strncpy(g_s3_cloudfront_url, value, sizeof(g_s3_cloudfront_url) - 1);
    }
    else if (strcmp(key, "DB_PATH") == 0)
    {
      strncpy(g_db_path, value, sizeof(g_db_path) - 1);
    }
  }
  fclose(f);

  printf("[CONFIG] Loaded: token=%s..., region=%s, bucket=%s, expires=%d, cloudfront=%s, db=%s\n",
         g_upload_auth_token[0] ? "***" : "(empty)",
         g_s3_region, g_s3_bucket, g_s3_url_expires,
         g_s3_cloudfront_url[0] ? g_s3_cloudfront_url : "(none)",
         g_db_path);
}

static void init_database(void)
{
  // Create data directory if needed
  char *last_slash = strrchr(g_db_path, '/');
  if (last_slash)
  {
    char dir_path[256];
    size_t dir_len = last_slash - g_db_path;
    strncpy(dir_path, g_db_path, dir_len);
    dir_path[dir_len] = '\0';
    mkdir(dir_path, 0755);
  }

  g_db_connection = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, g_db_path);
  if (!g_db_connection || !Deita_Connection_Is_Open(g_db_connection))
  {
    printf("[DB] ERROR: Failed to open database at %s\n", g_db_path);
    return;
  }

  // Create editor_content table
  const char *create_table =
    "CREATE TABLE IF NOT EXISTS editor_content ("
    "  id INTEGER PRIMARY KEY AUTOINCREMENT,"
    "  access_token TEXT NOT NULL,"
    "  doc_id TEXT NOT NULL,"
    "  content TEXT,"
    "  created_at INTEGER DEFAULT (strftime('%s', 'now')),"
    "  updated_at INTEGER DEFAULT (strftime('%s', 'now')),"
    "  UNIQUE(access_token, doc_id)"
    ")";

  int32 result = Deita_Query_Execute_Update(g_db_connection, create_table);
  if (result < 0)
  {
    printf("[DB] ERROR: Failed to create editor_content table\n");
  }

  // Create media_uploads table
  const char *create_media_uploads =
    "CREATE TABLE IF NOT EXISTS media_uploads ("
    "  id INTEGER PRIMARY KEY AUTOINCREMENT,"
    "  access_token TEXT NOT NULL,"
    "  original_filename TEXT NOT NULL,"
    "  content_type TEXT NOT NULL,"
    "  s3_key_original TEXT NOT NULL,"
    "  s3_key_processed TEXT,"
    "  file_size INTEGER,"
    "  status TEXT NOT NULL DEFAULT 'pending',"
    "  error_message TEXT,"
    "  created_at INTEGER DEFAULT (strftime('%s', 'now')),"
    "  updated_at INTEGER DEFAULT (strftime('%s', 'now'))"
    ")";

  result = Deita_Query_Execute_Update(g_db_connection, create_media_uploads);
  if (result < 0)
  {
    printf("[DB] ERROR: Failed to create media_uploads table\n");
  }

  // Create indices for media_uploads
  const char *create_status_idx =
    "CREATE INDEX IF NOT EXISTS idx_media_uploads_status ON media_uploads(status)";
  result = Deita_Query_Execute_Update(g_db_connection, create_status_idx);
  if (result < 0)
  {
    printf("[DB] ERROR: Failed to create status index\n");
  }

  const char *create_token_status_idx =
    "CREATE INDEX IF NOT EXISTS idx_media_uploads_token_status "
    "ON media_uploads(access_token, status)";
  result = Deita_Query_Execute_Update(g_db_connection, create_token_status_idx);
  if (result < 0)
  {
    printf("[DB] ERROR: Failed to create token_status index\n");
  }
  else
  {
    printf("[DB] Initialized: %s\n", g_db_path);
  }
}

void handle_sigint(int sig)
{
  printf("Failed\n");
  stop_server = 1;
}

void Seobeo_Render_Html(
    char *final_body,
    char *template,
    Dowa_Arena *arena
)
{
  size_t current_offset = 0;
  char *cursor = template;

  int32 token_len = 2;

  while (1)
  {
    char *start_tag = strstr(cursor, "{{");
    if (!start_tag) break;

    char *end_tag = strstr(start_tag, "}}");
    if (!end_tag) break;

    Seobeo_Log(SEOBEO_INFO, "[Curr] Life\n");

    size_t leading_len = start_tag - cursor;
    memcpy(final_body + current_offset, cursor, leading_len);
    current_offset += leading_len;

    size_t name_len = end_tag - (start_tag + token_len);
    char *include_name = Dowa_Arena_Allocate(arena, name_len + 1);
    memcpy(include_name, start_tag + token_len, name_len);
    include_name[name_len] = '\0';

    size_t sub_file_size = 0;
    char *sub_content = Seobeo_Web_LoadFile(include_name, &sub_file_size);
    Seobeo_Log(SEOBEO_DEBUG, "[TEMPLATE] Loading include: '%s' -> %s (size=%zu)\n",
              include_name, sub_content ? "OK" : "FAILED", sub_file_size);
    if (sub_content)
    {
      memcpy(final_body + current_offset, sub_content, sub_file_size);
      current_offset += sub_file_size;
      free(sub_content);
    }

    cursor = end_tag + 2;
  }
  strcpy(final_body + current_offset, cursor);
}

void Seobeo_Render_Html_FilePath(
    char *final_body,
    char *path,
    Dowa_Arena *arena
) {
  Seobeo_Log(SEOBEO_DEBUG, "[TEMPLATE] Loading main template: '%s'\n", path);
  size_t html_size = 0;
  char *template = Seobeo_Web_LoadFile(path, &html_size);
  Seobeo_Log(SEOBEO_DEBUG, "[TEMPLATE] Main template loaded: %s (size=%zu)\n", template ? "OK" : "FAILED", html_size);
  if (!template) return;
  Seobeo_Render_Html(final_body, template, arena);
}

Seobeo_Request_Entry* GetHomePage(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL; 
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry* GetResume(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL; 
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/resume/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry* GetTools(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL; 
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/tools/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}


Seobeo_Request_Entry* GetMDToHTML(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/tools/markdown_to_html/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry* GetFileConverter(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/tools/file_converter/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry *ConvertImageToWebP(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  if (!req)
  {
    Seobeo_Log(SEOBEO_ERROR, "Request is NULL\n");
    char *error_msg = "Internal error: no request data";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  size_t req_length = Dowa_Array_Length(req);
  printf("Request has %zu entries\n", req_length);

  for (size_t i = 0; i < req_length; i++)
  {
    Seobeo_Log(SEOBEO_INFO, "  Key[%zu]: '%s'\n", i, req[i].key);
  }

  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
  if (!body_kv)
  {
    printf("ERROR: No 'Body' key found in request\n");

    char *error_msg = "No file data provided";
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  void *cl_kv = Dowa_HashMap_Get_Ptr(req, "Content-Length");
  if (!cl_kv)
  {
    char *error_msg = "No Content-Length header";
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  const char *file_data = ((Seobeo_Request_Entry*)body_kv)->value;
  const char *content_length_str = ((Seobeo_Request_Entry*)cl_kv)->value;
  size_t file_size = atoi(content_length_str);

  printf("DEBUG: Converting image, file_size=%zu bytes\n", file_size);

  int open_flags = O_RDWR | O_CREAT | O_EXCL;

  char *uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN);
  uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid4);
  char *input_path = Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);;
  snprintf(input_path, TMP_FILE_LENGTH, "/tmp/%s", uuid4);
  int input_fd = open(input_path, open_flags, 0600);
  if (input_fd == -1)
  {
    char *error_msg = "Failed to create temporary file";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }
  write(input_fd, file_data, file_size);
  close(input_fd);


  uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN);
  seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid4);
  char *output_path = (char *)Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);;
  snprintf(output_path, TMP_FILE_LENGTH, "/tmp/%s.webp", uuid4);
  printf("[DEBUG] output_path %s\n", output_path);
  printf("[DEBUG] open_flags: 0x%x\n", open_flags);
  printf("[DEBUG] input_path: %s\n", input_path);
  int output_fd = open(output_path, open_flags, 0600);
  printf("[DEBUG] output_fd: %d\n", output_fd);
  if (output_fd == -1)
  {
    unlink(input_path);
    printf("[DEBUG] errno: %d (%s)\n", errno, strerror(errno));
    char *error_msg = "Failed to create output file";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }
  close(output_fd);

  char cmd[1024];
  snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>/tmp/error_log",
           input_path, output_path);
  int result = system(cmd);
  if (result != 0)
  {
    unlink(input_path);
    unlink(output_path);
    char *error_msg = "FFmpeg conversion failed";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  size_t converted_size = 0;
  FILE *out_file = fopen(output_path, "rb");
  if (!out_file)
  {
    unlink(input_path);
    unlink(output_path);
    char *error_msg = "Failed to read converted file";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }
  fclose(out_file);

  unlink(input_path);

  char *filename = strrchr(output_path, '/') + 1;
  char *response_body = Dowa_Arena_Allocate(arena, 512);
  snprintf(response_body, 512,
           "{\"success\":true,\"download_url\":\"/api/download/%s\",\"expires\":\"10 minutes\"}",
           filename);
  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

  printf("DEBUG: Image converted, available at /api/download/%s\n", filename);

  return resp;
}

Seobeo_Request_Entry *ConvertVideoToMP4(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
  if (!body_kv)
  {
    char *error_msg = "No file data provided";
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  // Get Content-Length to know the actual binary size
  void *cl_kv = Dowa_HashMap_Get_Ptr(req, "Content-Length");
  if (!cl_kv)
  {
    char *error_msg = "No Content-Length header";
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  const char *file_data = ((Seobeo_Request_Entry*)body_kv)->value;
  const char *content_length_str = ((Seobeo_Request_Entry*)cl_kv)->value;
  size_t file_size = atoi(content_length_str);

  printf("DEBUG: Converting video, file_size=%zu bytes\n", file_size);

  int open_flags = O_RDWR | O_CREAT | O_EXCL;

  char *uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN);
  uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid4);
  char *input_path = Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);
  snprintf(input_path, TMP_FILE_LENGTH, "/tmp/%s", uuid4);
  Seobeo_Log(SEOBEO_DEBUG, "Input path: %s\n", input_path);

  int input_fd = open(input_path, open_flags, 0600);
  if (input_fd == -1)
  {
    Seobeo_Log(SEOBEO_DEBUG, "errno: %d (%s)\n", errno, strerror(errno));
    char *error_msg = "Failed to create temporary file";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  write(input_fd, file_data, file_size);
  close(input_fd);

  seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid4);
  char *output_path = (char *)Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH);;
  snprintf(output_path, TMP_FILE_LENGTH, "/tmp/%s.mp4", uuid4);
  int output_fd = open(output_path, open_flags, 0600);
  if (output_fd == -1)
  {
    unlink(input_path);
    char *error_msg = "Failed to create output file";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }
  close(output_fd);

  char cmd[512];
  snprintf(cmd, sizeof(cmd),
           "ffmpeg -y -i %s -c:v libx264 -preset fast -crf 23 -c:a aac %s 2>/tmp/error_log",
           input_path, output_path);
  int result = system(cmd);
  if (result != 0)
  {
    unlink(input_path);
    unlink(output_path);
    char *error_msg = "FFmpeg conversion failed";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  unlink(input_path);
  char *filename = strrchr(output_path, '/') + 1;
  char *response_body = Dowa_Arena_Allocate(arena, 512);
  snprintf(response_body, 512,
           "{\"success\":true,\"download_url\":\"/api/download/%s\",\"expires\":\"10 minutes\"}",
           filename);
  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

  printf("DEBUG: Video converted, available at /api/download/%s\n", filename);
  return resp;
}

Seobeo_Request_Entry *DownloadConvertedFile(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  void *filename_kv = Dowa_HashMap_Get_Ptr(req, ":filename");
  if (!filename_kv)
  {
    char *error_msg = "No filename specified";
    Dowa_HashMap_Push_Arena(resp, "status", "404", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  const char *filename = ((Seobeo_Request_Entry*)filename_kv)->value;

  // TODO: Maybe check if the uuid is allowed or not?
  // if (strlen(filename) != TMP_FILE_LENGTH)
  // {
  //   char *error_msg = "Not Allowed Filename";
  //   Dowa_HashMap_Push_Arena(resp, "status", "404", arena);
  //   Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
  //   Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
  //   return resp;
  // }
  // boolean allowed = FALSE;
  // for (int i = 0; i < Dowa_Array_Length(g_uuid4_array); i++)
  // {
  //   if strcmp(g_uuid4_array, filename)
  //   {
  //     allowed = TRUE;
  //     break;
  //   }
  //   g_uuid4_array++;
  // }

  char filepath[512];
  snprintf(filepath, sizeof(filepath), "/tmp/%s", filename);

  FILE *file = fopen(filepath, "rb");
  if (!file)
  {
    char *error_msg = "File not found or expired";
    Dowa_HashMap_Push_Arena(resp, "status", "404", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  fseek(file, 0, SEEK_END);
  size_t file_size = ftell(file);
  fseek(file, 0, SEEK_SET);

  char *file_data = malloc(file_size + 1);
  if (!file_data)
  {
    fclose(file);
    char *error_msg = "Memory allocation failed";
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena);
    Dowa_HashMap_Push_Arena(resp, "body", error_msg, arena);
    return resp;
  }

  fread(file_data, 1, file_size, file);
  file_data[file_size] = '\0';
  fclose(file);

  const char *content_type = "application/octet-stream";
  if (strstr(filename, ".webp"))
    content_type = "image/webp";
  else if (strstr(filename, ".mp4"))
    content_type = "video/mp4";

  char *body = Dowa_Arena_Allocate(arena, file_size + 1);
  memcpy(body, file_data, file_size);
  body[file_size] = '\0';
  free(file_data);

  unlink(filepath);

  printf("DEBUG: Served and deleted file: %s (%zu bytes)\n", filename, file_size);

  char *content_length = Dowa_Arena_Allocate(arena, 32);
  snprintf(content_length, 32, "%zu", file_size);

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", content_type, arena);
  Dowa_HashMap_Push_Arena(resp, "content-length", content_length, arena);
  Dowa_HashMap_Push_Arena(resp, "body", body, arena);

  return resp;
}

Seobeo_Request_Entry *RenderBlogList(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/blog/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}


Seobeo_Request_Entry *RenderBlog(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  char *file_path = Dowa_Arena_Allocate(arena, 1024);
  void *blog_id_kv = Dowa_HashMap_Get_Ptr(req, ":blog_id");
  char *blog_id = ((Seobeo_Request_Entry*)blog_id_kv)->value;
  snprintf(file_path, 1024, "/blog/%s/index.html", blog_id);

  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, file_path, arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn, Seobeo_WebSocket_Message *p_msg, void *p_user_data)
{
  (void)p_user_data;

  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
  {
    char message[2048];
    snprintf(message, sizeof(message), "[%s]: %.*s", p_conn->client_id, (int)p_msg->length, (char*)p_msg->data);

    Seobeo_Log(SEOBEO_INFO, "[Chat] Broadcasting: %s\n", message);
    Seobeo_WebSocket_Server_Broadcast_Text(message, p_conn);
  }
}

Seobeo_Request_Entry *GetTalk(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/talk/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry *GetNotesLogin(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/notes/login.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry *GetNotes(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  Seobeo_Render_Html_FilePath(final_body, "/notes/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

Seobeo_Request_Entry *GetNoteById(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;
  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
  // Same template - JavaScript handles the note_id from URL
  Seobeo_Render_Html_FilePath(final_body, "/notes/index.html", arena);
  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
  return resp;
}

CREATE_REDIRECT_HANDLER(HomePage, "/")
CREATE_REDIRECT_HANDLER(Resume, "/resume")
CREATE_REDIRECT_HANDLER(Tools, "/tools")
CREATE_REDIRECT_HANDLER(MarkDownToHtml, "/tools/markdown_to_html")
CREATE_REDIRECT_HANDLER(FileConverter, "/tools/file_converter")
CREATE_REDIRECT_HANDLER(Talk, "/talk")
CREATE_REDIRECT_HANDLER(Editor, "/editor")

// S3 Upload URL API
// POST /api/s3/upload-url
// Headers: Authorization: Bearer <token>, Content-Type: application/json
// Body: {"filename": "photo.png", "content_type": "image/png"}
// Returns: {"upload_url": "https://...", "key": "uploads/..."}
Seobeo_Request_Entry *GetS3UploadUrl(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;

  // Expect "Bearer <token>"
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format, use Bearer token\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  // Parse request body for filename and content_type
  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
  if (!body_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing request body\"}", arena);
    return resp;
  }

  const char *body = ((Seobeo_Request_Entry*)body_kv)->value;

  // Simple JSON parsing for filename and content_type
  char filename[256] = {0};
  char content_type[128] = "application/octet-stream";

  // Find "filename":"value"
  const char *fn_key = strstr(body, "\"filename\"");
  if (fn_key)
  {
    const char *fn_start = strchr(fn_key + 10, '"');
    if (fn_start)
    {
      fn_start++;
      const char *fn_end = strchr(fn_start, '"');
      if (fn_end && (size_t)(fn_end - fn_start) < sizeof(filename))
      {
        memcpy(filename, fn_start, fn_end - fn_start);
        filename[fn_end - fn_start] = '\0';
      }
    }
  }

  // Find "content_type":"value"
  const char *ct_key = strstr(body, "\"content_type\"");
  if (ct_key)
  {
    const char *ct_start = strchr(ct_key + 14, '"');
    if (ct_start)
    {
      ct_start++;
      const char *ct_end = strchr(ct_start, '"');
      if (ct_end && (size_t)(ct_end - ct_start) < sizeof(content_type))
      {
        memcpy(content_type, ct_start, ct_end - ct_start);
        content_type[ct_end - ct_start] = '\0';
      }
    }
  }

  if (strlen(filename) == 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing filename in request body\"}", arena);
    return resp;
  }

  // Generate unique S3 key with timestamp
  char s3_key[512];
  char *uuid = Dowa_Arena_Allocate(arena, UUID_LEN);
  uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid);
  snprintf(s3_key, sizeof(s3_key), "uploads/%s/%s", uuid, filename);

  // Generate presigned URL
  S3_Presigned_URL presigned = S3_Presign_Put(&g_s3_config, s3_key, content_type, g_s3_url_expires);

  if (!presigned.success)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    char *error_body = Dowa_Arena_Allocate(arena, 256);
    snprintf(error_body, 256, "{\"error\":\"Failed to generate upload URL: %s\"}",
             presigned.error_message ? presigned.error_message : "unknown");
    Dowa_HashMap_Push_Arena(resp, "body", error_body, arena);
    S3_Presigned_URL_Destroy(&presigned);
    return resp;
  }

  // Build public URL using CloudFront
  char public_url[512];
  if (g_s3_cloudfront_url[0])
  {
    snprintf(public_url, sizeof(public_url), "%s/%s", g_s3_cloudfront_url, s3_key);
  }
  else
  {
    snprintf(public_url, sizeof(public_url), "https://%s.s3.%s.amazonaws.com/%s",
             g_s3_bucket, g_s3_region, s3_key);
  }

  // Build response
  char *response_body = Dowa_Arena_Allocate(arena, 4096 + strlen(presigned.url));
  snprintf(response_body, 4096 + strlen(presigned.url),
           "{\"upload_url\":\"%s\",\"public_url\":\"%s\",\"key\":\"%s\",\"expires\":%d}",
           presigned.url, public_url, s3_key, g_s3_url_expires);

  S3_Presigned_URL_Destroy(&presigned);

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

  printf("[S3] Generated upload URL for: %s\n", s3_key);

  return resp;
}

// Editor Content Save API
// POST /api/editor/save
// Headers: Authorization: Bearer <token>
// Body: {"doc_id": "my-doc", "content": "<html content>"}
Seobeo_Request_Entry *EditorSave(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  if (!g_db_connection)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Database not available\"}", arena);
    return resp;
  }

  // Parse request body
  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
  if (!body_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing request body\"}", arena);
    return resp;
  }

  const char *body = ((Seobeo_Request_Entry*)body_kv)->value;

  // Parse doc_id and content from JSON
  char doc_id[256] = "default";
  char *content = NULL;
  size_t content_len = 0;

  // Find "doc_id":"value"
  const char *doc_key = strstr(body, "\"doc_id\"");
  if (doc_key)
  {
    const char *doc_start = strchr(doc_key + 8, '"');
    if (doc_start)
    {
      doc_start++;
      const char *doc_end = strchr(doc_start, '"');
      if (doc_end && (size_t)(doc_end - doc_start) < sizeof(doc_id))
      {
        memcpy(doc_id, doc_start, doc_end - doc_start);
        doc_id[doc_end - doc_start] = '\0';
      }
    }
  }

  // Find "content":"value" - content can be large and contain escaped characters
  const char *content_key = strstr(body, "\"content\"");
  if (content_key)
  {
    const char *content_start = strchr(content_key + 9, '"');
    if (content_start)
    {
      content_start++;
      // Find closing quote (accounting for escaped quotes)
      const char *p = content_start;
      while (*p)
      {
        if (*p == '\\' && *(p+1))
        {
          p += 2;
          continue;
        }
        if (*p == '"') break;
        p++;
      }
      content_len = p - content_start;
      content = Dowa_Arena_Allocate(arena, content_len + 1);
      memcpy(content, content_start, content_len);
      content[content_len] = '\0';
    }
  }

  if (!content)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing content\"}", arena);
    return resp;
  }

  // Upsert content
  const char *upsert_query =
    "INSERT INTO editor_content (access_token, doc_id, content, updated_at) "
    "VALUES (?, ?, ?, strftime('%s', 'now')) "
    "ON CONFLICT(access_token, doc_id) DO UPDATE SET "
    "content = excluded.content, updated_at = strftime('%s', 'now')";

  const char *params[] = { token, doc_id, content };
  int32 result = Deita_Query_Execute_Update_Prepared(g_db_connection, upsert_query, 3, params);

  if (result < 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to save\"}", arena);
    return resp;
  }

  printf("[EDITOR] Saved doc_id=%s, content_len=%zu\n", doc_id, content_len);

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", "{\"success\":true}", arena);
  return resp;
}

// Editor Content Load API
// GET /api/editor/load/:doc_id
// Headers: Authorization: Bearer <token>
Seobeo_Request_Entry *EditorLoad(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  if (!g_db_connection)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Database not available\"}", arena);
    return resp;
  }

  // Get doc_id from URL parameter
  void *doc_id_kv = Dowa_HashMap_Get_Ptr(req, ":doc_id");
  const char *doc_id = "default";
  if (doc_id_kv)
  {
    doc_id = ((Seobeo_Request_Entry*)doc_id_kv)->value;
  }

  // Query content
  const char *select_query =
    "SELECT content, updated_at FROM editor_content WHERE access_token = ? AND doc_id = ?";
  const char *params[] = { token, doc_id };

  Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, params, arena);

  if (p_result && Deita_Result_Set_Next(p_result))
  {
    const char *content = Deita_Result_Set_Get_Text(p_result, 0);
    int64 updated_at = Deita_Result_Set_Get_Integer(p_result, 1);

    // Build JSON response - escape content
    size_t content_len = content ? strlen(content) : 0;
    char *response_body = Dowa_Arena_Allocate(arena, content_len + 256);
    snprintf(response_body, content_len + 256,
             "{\"doc_id\":\"%s\",\"content\":\"%s\",\"updated_at\":%lld}",
             doc_id, content ? content : "", (long long)updated_at);

    Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

    printf("[EDITOR] Loaded doc_id=%s\n", doc_id);
  }
  else
  {
    // No content found, return empty
    char *response_body = Dowa_Arena_Allocate(arena, 128);
    snprintf(response_body, 128, "{\"doc_id\":\"%s\",\"content\":\"\",\"updated_at\":0}", doc_id);

    Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);
  }

  if (p_result) Deita_Result_Set_Free(p_result);
  return resp;
}

// Media Upload API - Create media record
// POST /api/media/create
// Headers: Authorization: Bearer <token>, Content-Type: application/json
// Body: {"filename": "photo.jpg", "content_type": "image/jpeg"}
// Returns: {"media_id": 123, "upload_url": "https://...", "expires": 3600}
Seobeo_Request_Entry *MediaCreate(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  if (!g_db_connection)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Database not available\"}", arena);
    return resp;
  }

  // Parse request body
  void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body");
  if (!body_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing request body\"}", arena);
    return resp;
  }

  const char *body = ((Seobeo_Request_Entry*)body_kv)->value;

  // Parse filename and content_type
  char filename[256] = {0};
  char content_type[128] = "application/octet-stream";

  // Find "filename":"value"
  const char *fn_key = strstr(body, "\"filename\"");
  if (fn_key)
  {
    const char *fn_start = strchr(fn_key + 10, '"');
    if (fn_start)
    {
      fn_start++;
      const char *fn_end = strchr(fn_start, '"');
      if (fn_end && (size_t)(fn_end - fn_start) < sizeof(filename))
      {
        memcpy(filename, fn_start, fn_end - fn_start);
        filename[fn_end - fn_start] = '\0';
      }
    }
  }

  // Find "content_type":"value"
  const char *ct_key = strstr(body, "\"content_type\"");
  if (ct_key)
  {
    const char *ct_start = strchr(ct_key + 14, '"');
    if (ct_start)
    {
      ct_start++;
      const char *ct_end = strchr(ct_start, '"');
      if (ct_end && (size_t)(ct_end - ct_start) < sizeof(content_type))
      {
        memcpy(content_type, ct_start, ct_end - ct_start);
        content_type[ct_end - ct_start] = '\0';
      }
    }
  }

  if (strlen(filename) == 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing filename\"}", arena);
    return resp;
  }

  // Generate UUID for this upload
  char *uuid = Dowa_Arena_Allocate(arena, UUID_LEN);
  uint32 seed = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed, uuid);

  // Generate S3 keys
  char s3_key_original[512];
  char s3_key_processed[512];
  snprintf(s3_key_original, sizeof(s3_key_original), "uploads/%s/%s", uuid, filename);

  // Only use .webp for images
  int is_image = (strncmp(content_type, "image/", 6) == 0);
  if (is_image)
  {
    snprintf(s3_key_processed, sizeof(s3_key_processed), "uploads/%s/processed.webp", uuid);
  }
  else
  {
    s3_key_processed[0] = '\0'; // No processed version for non-images
  }

  // Insert into database
  const char *insert_query =
    "INSERT INTO media_uploads (access_token, original_filename, content_type, s3_key_original, s3_key_processed, status) "
    "VALUES (?, ?, ?, ?, ?, 'pending')";

  const char *params[] = { token, filename, content_type, s3_key_original, s3_key_processed };
  int32 result = Deita_Query_Execute_Update_Prepared(g_db_connection, insert_query, 5, params);

  if (result < 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to create media record\"}", arena);
    return resp;
  }

  // Get the inserted media_id using last_insert_rowid()
  const char *last_id_query = "SELECT last_insert_rowid()";
  Deita_Result_Set *id_result = Deita_Query_Execute(g_db_connection, last_id_query, arena);
  int64 media_id = 0;
  if (id_result && Deita_Result_Set_Next(id_result))
  {
    media_id = Deita_Result_Set_Get_Integer(id_result, 0);
  }
  if (id_result) Deita_Result_Set_Free(id_result);

  // Generate presigned PUT URL
  S3_Presigned_URL presigned = S3_Presign_Put(&g_s3_config, s3_key_original, content_type, g_s3_url_expires);

  if (!presigned.success)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    char *error_body = Dowa_Arena_Allocate(arena, 256);
    snprintf(error_body, 256, "{\"error\":\"Failed to generate upload URL: %s\"}",
             presigned.error_message ? presigned.error_message : "unknown");
    Dowa_HashMap_Push_Arena(resp, "body", error_body, arena);
    S3_Presigned_URL_Destroy(&presigned);
    return resp;
  }

  // Build public URL using CloudFront or S3
  char public_url[512];
  if (g_s3_cloudfront_url[0])
  {
    snprintf(public_url, sizeof(public_url), "%s/%s", g_s3_cloudfront_url, s3_key_original);
  }
  else
  {
    snprintf(public_url, sizeof(public_url), "https://%s.s3.%s.amazonaws.com/%s",
             g_s3_bucket, g_s3_region, s3_key_original);
  }

  // Build response
  char *response_body = Dowa_Arena_Allocate(arena, 4096 + strlen(presigned.url) + strlen(public_url));
  snprintf(response_body, 4096 + strlen(presigned.url) + strlen(public_url),
           "{\"media_id\":%lld,\"upload_url\":\"%s\",\"public_url\":\"%s\",\"expires\":%d}",
           (long long)media_id, presigned.url, public_url, g_s3_url_expires);

  S3_Presigned_URL_Destroy(&presigned);

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

  printf("[MEDIA] Created media_id=%lld, file=%s\n", (long long)media_id, filename);

  return resp;
}

// Background thread function for media processing
void *Media_Process_Background(void *arg)
{
  Media_Processing_Context *ctx = (Media_Processing_Context *)arg;

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Background thread started for media_id=%lld\n", (long long)ctx->media_id);
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key original: %s\n", ctx->s3_key_original);
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key processed: %s\n", ctx->s3_key_processed);
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] DB path: %s\n", ctx->db_path);

  // Open thread-local DB connection
  Deita_Connection *db_conn = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, ctx->db_path);
  if (!db_conn || !Deita_Connection_Is_Open(db_conn))
  {
    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] Thread ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id);
    free(ctx);
    return NULL;
  }

  // Update status to 'processing'
  const char *update_processing =
    "UPDATE media_uploads SET status='processing', updated_at=strftime('%s','now') WHERE id=?";
  char media_id_str[32];
  snprintf(media_id_str, sizeof(media_id_str), "%lld", (long long)ctx->media_id);
  const char *params[] = { media_id_str };
  int32 update_result = Deita_Query_Execute_Update_Prepared(db_conn, update_processing, 1, params);
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Updated status to 'processing' for media_id=%lld (result=%d)\n", (long long)ctx->media_id, update_result);

  // Generate presigned GET URL for download (10 min expiry)
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generating presigned GET URL for media_id=%lld\n", (long long)ctx->media_id);
  S3_Presigned_URL download_url = S3_Presign_Get(&ctx->s3_config, ctx->s3_key_original, 600);
  if (!download_url.success)
  {
    const char *error_msg = download_url.error_message ? download_url.error_message : "Failed to generate download URL";
    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to generate download URL for media_id=%lld: %s\n",
               (long long)ctx->media_id, error_msg);
    const char *update_error =
      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
    const char *error_params[] = { error_msg, media_id_str };
    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
    S3_Presigned_URL_Destroy(&download_url);
    Deita_Connection_Close(db_conn);
    free(ctx);
    return NULL;
  }
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generated presigned URL: %.100s...\n", download_url.url);

  // Generate temp file paths
  char tmp_input[256];
  char tmp_output[256];
  char *uuid_input = malloc(UUID_LEN);
  char *uuid_output = malloc(UUID_LEN);
  uint32 seed1 = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  uint32 seed2 = (uint32)time(NULL) ^ (uint32)pthread_self() ^ counter++;
  Dowa_String_UUID(seed1, uuid_input);
  Dowa_String_UUID(seed2, uuid_output);
  snprintf(tmp_input, sizeof(tmp_input), "/tmp/%s", uuid_input);
  snprintf(tmp_output, sizeof(tmp_output), "/tmp/%s.webp", uuid_output);
  free(uuid_input);
  free(uuid_output);

  // Download from S3
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Downloading from S3 to %s for media_id=%lld\n", tmp_input, (long long)ctx->media_id);
  Seobeo_Client_Request *download_req = Seobeo_Client_Request_Create(download_url.url);
  Seobeo_Client_Request_Set_Download_Path(download_req, tmp_input);
  Seobeo_Client_Response *download_resp = Seobeo_Client_Request_Execute(download_req);

  S3_Presigned_URL_Destroy(&download_url);

  if (!download_resp || download_resp->status_code != 200)
  {
    int status = download_resp ? download_resp->status_code : 0;
    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to download from S3 for media_id=%lld (status=%d)\n",
               (long long)ctx->media_id, status);
    const char *update_error =
      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
    const char *error_params[] = { "Failed to download from S3", media_id_str };
    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
    if (download_req) Seobeo_Client_Request_Destroy(download_req);
    if (download_resp) Seobeo_Client_Response_Destroy(download_resp);
    unlink(tmp_input);
    Deita_Connection_Close(db_conn);
    free(ctx);
    return NULL;
  }

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully downloaded file to %s\n", tmp_input);
  Seobeo_Client_Request_Destroy(download_req);
  Seobeo_Client_Response_Destroy(download_resp);

  // Convert to webp using FFmpeg
  char cmd[1024];
  char log_file[256];
  snprintf(log_file, sizeof(log_file), "/tmp/ffmpeg_%lld.log", (long long)ctx->media_id);
  snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>%s",
           tmp_input, tmp_output, log_file);

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Running FFmpeg: %s\n", cmd);
  int ffmpeg_result = system(cmd);
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] FFmpeg result: %d for media_id=%lld\n", ffmpeg_result, (long long)ctx->media_id);

  if (ffmpeg_result != 0)
  {
    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: FFmpeg conversion failed for media_id=%lld (exit code %d). Check log: %s\n",
               (long long)ctx->media_id, ffmpeg_result, log_file);
    const char *update_error =
      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
    const char *error_params[] = { "Image conversion failed", media_id_str };
    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
    unlink(tmp_input);
    unlink(tmp_output);
    Deita_Connection_Close(db_conn);
    free(ctx);
    return NULL;
  }
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully converted to webp: %s\n", tmp_output);

  // Upload processed file to S3
  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Uploading processed file to S3: %s -> %s\n", tmp_output, ctx->s3_key_processed);
  S3_Result upload_result = S3_Upload_File_With_Content_Type(
    &ctx->s3_config, tmp_output, ctx->s3_key_processed, "image/webp");

  if (!upload_result.success)
  {
    const char *error_msg = upload_result.error_message ? upload_result.error_message : "Failed to upload processed file";
    Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to upload processed file for media_id=%lld: %s\n",
               (long long)ctx->media_id, error_msg);
    const char *update_error =
      "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
    const char *error_params[] = { error_msg, media_id_str };
    Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
    unlink(tmp_input);
    unlink(tmp_output);
    Deita_Connection_Close(db_conn);
    free(ctx);
    return NULL;
  }

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully uploaded processed file to S3\n");

  // Update status to 'finished'
  const char *update_finished =
    "UPDATE media_uploads SET status='finished', updated_at=strftime('%s','now') WHERE id=?";
  Deita_Query_Execute_Update_Prepared(db_conn, update_finished, 1, params);

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully processed media_id=%lld - COMPLETE\n", (long long)ctx->media_id);

  // Cleanup
  unlink(tmp_input);
  unlink(tmp_output);
  Deita_Connection_Close(db_conn);
  free(ctx);

  return NULL;
}

// Media Upload API - Mark uploaded
// POST /api/media/:id/uploaded
// Headers: Authorization: Bearer <token>
Seobeo_Request_Entry *MediaUploaded(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  if (!g_db_connection)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Database not available\"}", arena);
    return resp;
  }

  // Extract media_id from URL params
  void *id_kv = Dowa_HashMap_Get_Ptr(req, ":id");
  if (!id_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing media ID\"}", arena);
    return resp;
  }

  const char *media_id_str = ((Seobeo_Request_Entry*)id_kv)->value;
  int64 media_id = atoll(media_id_str);

  // Verify access_token matches and get content_type
  const char *select_query =
    "SELECT content_type, s3_key_original, s3_key_processed FROM media_uploads WHERE id = ? AND access_token = ?";
  const char *select_params[] = { media_id_str, token };

  Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, select_params, arena);

  if (!p_result || !Deita_Result_Set_Next(p_result))
  {
    if (p_result) Deita_Result_Set_Free(p_result);
    Dowa_HashMap_Push_Arena(resp, "status", "404", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Media not found or access denied\"}", arena);
    return resp;
  }

  const char *content_type = Deita_Result_Set_Get_Text(p_result, 0);
  const char *s3_key_original = Deita_Result_Set_Get_Text(p_result, 1);
  const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 2);

  // Copy values before freeing result set
  char content_type_copy[128];
  char s3_key_original_copy[512];
  char s3_key_processed_copy[512];
  strncpy(content_type_copy, content_type, sizeof(content_type_copy) - 1);
  strncpy(s3_key_original_copy, s3_key_original, sizeof(s3_key_original_copy) - 1);
  strncpy(s3_key_processed_copy, s3_key_processed, sizeof(s3_key_processed_copy) - 1);
  content_type_copy[sizeof(content_type_copy) - 1] = '\0';
  s3_key_original_copy[sizeof(s3_key_original_copy) - 1] = '\0';
  s3_key_processed_copy[sizeof(s3_key_processed_copy) - 1] = '\0';

  Deita_Result_Set_Free(p_result);

  // Update status to 'uploaded'
  const char *update_query =
    "UPDATE media_uploads SET status='uploaded', updated_at=strftime('%s','now') WHERE id=?";
  const char *update_params[] = { media_id_str };
  int32 result = Deita_Query_Execute_Update_Prepared(g_db_connection, update_query, 1, update_params);

  if (result < 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to update status\"}", arena);
    return resp;
  }

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Content type for media_id=%lld: '%s'\n", (long long)media_id, content_type_copy);

  // If content_type starts with "image/", spawn background processing thread
  if (strncmp(content_type_copy, "image/", 6) == 0)
  {
    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Detected image type, preparing to spawn background thread for media_id=%lld\n", (long long)media_id);

    // Create context for background thread (heap allocated)
    Media_Processing_Context *ctx = malloc(sizeof(Media_Processing_Context));
    ctx->media_id = media_id;
    strncpy(ctx->s3_key_original, s3_key_original_copy, sizeof(ctx->s3_key_original) - 1);
    strncpy(ctx->s3_key_processed, s3_key_processed_copy, sizeof(ctx->s3_key_processed) - 1);
    strncpy(ctx->content_type, content_type_copy, sizeof(ctx->content_type) - 1);
    strncpy(ctx->access_token, token, sizeof(ctx->access_token) - 1);
    strncpy(ctx->db_path, g_db_path, sizeof(ctx->db_path) - 1);
    ctx->s3_key_original[sizeof(ctx->s3_key_original) - 1] = '\0';
    ctx->s3_key_processed[sizeof(ctx->s3_key_processed) - 1] = '\0';
    ctx->content_type[sizeof(ctx->content_type) - 1] = '\0';
    ctx->access_token[sizeof(ctx->access_token) - 1] = '\0';
    ctx->db_path[sizeof(ctx->db_path) - 1] = '\0';
    ctx->s3_config = g_s3_config;

    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Creating pthread for media_id=%lld\n", (long long)media_id);

    // Spawn detached thread
    pthread_t thread_id;
    int thread_result = pthread_create(&thread_id, NULL, Media_Process_Background, ctx);

    if (thread_result != 0)
    {
      Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: pthread_create failed with result=%d for media_id=%lld\n", thread_result, (long long)media_id);
      free(ctx);
    }
    else
    {
      // Detach thread so it cleans up automatically when done
      pthread_detach(thread_id);
      Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully spawned and detached thread for media_id=%lld\n", (long long)media_id);
    }
  }
  else
  {
    Seobeo_Log(SEOBEO_INFO, "[MEDIA] Non-image file, skipping background processing for media_id=%lld\n", (long long)media_id);
  }

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", "{\"success\":true,\"status\":\"uploaded\"}", arena);

  Seobeo_Log(SEOBEO_INFO, "[MEDIA] Marked uploaded media_id=%lld\n", (long long)media_id);

  return resp;
}

// Media Upload API - Get status
// GET /api/media/:id/status
// Headers: Authorization: Bearer <token>
// Returns: {"id": 123, "status": "finished", "processed_url": "https://...", "error_message": null}
Seobeo_Request_Entry *MediaStatus(Seobeo_Request_Entry *req, Dowa_Arena *arena)
{
  Seobeo_Request_Entry *resp = NULL;

  // Check auth token
  void *auth_kv = Dowa_HashMap_Get_Ptr(req, "Authorization");
  if (!auth_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing Authorization header\"}", arena);
    return resp;
  }

  const char *auth_header = ((Seobeo_Request_Entry*)auth_kv)->value;
  if (strncmp(auth_header, "Bearer ", 7) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "401", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid Authorization format\"}", arena);
    return resp;
  }

  const char *token = auth_header + 7;
  if (strlen(g_upload_auth_token) == 0 || strcmp(token, g_upload_auth_token) != 0)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "403", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Invalid token\"}", arena);
    return resp;
  }

  if (!g_db_connection)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "500", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Database not available\"}", arena);
    return resp;
  }

  // Extract media_id from URL params
  void *id_kv = Dowa_HashMap_Get_Ptr(req, ":id");
  if (!id_kv)
  {
    Dowa_HashMap_Push_Arena(resp, "status", "400", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Missing media ID\"}", arena);
    return resp;
  }

  const char *media_id_str = ((Seobeo_Request_Entry*)id_kv)->value;

  // Query media status
  const char *select_query =
    "SELECT id, status, s3_key_original, s3_key_processed, error_message FROM media_uploads WHERE id = ? AND access_token = ?";
  const char *select_params[] = { media_id_str, token };

  Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, select_params, arena);

  if (!p_result || !Deita_Result_Set_Next(p_result))
  {
    if (p_result) Deita_Result_Set_Free(p_result);
    Dowa_HashMap_Push_Arena(resp, "status", "404", arena);
    Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
    Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Media not found\"}", arena);
    return resp;
  }

  int64 id = Deita_Result_Set_Get_Integer(p_result, 0);
  const char *status = Deita_Result_Set_Get_Text(p_result, 1);
  const char *s3_key_original = Deita_Result_Set_Get_Text(p_result, 2);
  const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 3);
  const char *error_message = Deita_Result_Set_Get_Text(p_result, 4);

  // Build CloudFront URL for processed file if status is 'finished'
  char processed_url[1024] = {0};
  if (strcmp(status, "finished") == 0 && s3_key_processed && strlen(s3_key_processed) > 0)
  {
    if (g_s3_cloudfront_url[0])
    {
      snprintf(processed_url, sizeof(processed_url), "%s/%s", g_s3_cloudfront_url, s3_key_processed);
    }
    else
    {
      snprintf(processed_url, sizeof(processed_url), "https://%s.s3.%s.amazonaws.com/%s",
               g_s3_bucket, g_s3_region, s3_key_processed);
    }
  }

  // Build CloudFront URL for original file (for non-images or before processing completes)
  char original_url[1024] = {0};
  if (s3_key_original && strlen(s3_key_original) > 0)
  {
    if (g_s3_cloudfront_url[0])
    {
      snprintf(original_url, sizeof(original_url), "%s/%s", g_s3_cloudfront_url, s3_key_original);
    }
    else
    {
      snprintf(original_url, sizeof(original_url), "https://%s.s3.%s.amazonaws.com/%s",
               g_s3_bucket, g_s3_region, s3_key_original);
    }
  }

  // Build JSON response with both processed_url and original_url
  char *response_body = Dowa_Arena_Allocate(arena, 3072);

  // Build the base response
  int offset = snprintf(response_body, 3072,
                        "{\"id\":%lld,\"status\":\"%s\",",
                        (long long)id, status);

  // Add processed_url
  if (strlen(processed_url) > 0)
  {
    offset += snprintf(response_body + offset, 3072 - offset,
                       "\"processed_url\":\"%s\",", processed_url);
  }
  else
  {
    offset += snprintf(response_body + offset, 3072 - offset,
                       "\"processed_url\":null,");
  }

  // Add original_url
  if (strlen(original_url) > 0)
  {
    offset += snprintf(response_body + offset, 3072 - offset,
                       "\"original_url\":\"%s\",", original_url);
  }
  else
  {
    offset += snprintf(response_body + offset, 3072 - offset,
                       "\"original_url\":null,");
  }

  // Add error_message
  if (error_message && strlen(error_message) > 0)
  {
    snprintf(response_body + offset, 3072 - offset,
             "\"error_message\":\"%s\"}", error_message);
  }
  else
  {
    snprintf(response_body + offset, 3072 - offset,
             "\"error_message\":null}");
  }

  Deita_Result_Set_Free(p_result);

  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
  Dowa_HashMap_Push_Arena(resp, "body", response_body, arena);

  return resp;
}

int main(void)
{
  // Load server config
  load_config("mrjunejune/.config");

  // Load S3 credentials from .env
  FILE *env_file = fopen(".env", "r");
  static char s3_access_key[128] = {0};
  static char s3_secret_key[128] = {0};

  if (env_file)
  {
    char line[512];
    while (fgets(line, sizeof(line), env_file))
    {
      if (strncmp(line, "AWS_MRJUNEJUNE_ACCESS_KEY=", 26) == 0)
      {
        char *val = line + 26;
        size_t len = strlen(val);
        while (len > 0 && (val[len-1] == '\n' || val[len-1] == '\r')) val[--len] = '\0';
        strncpy(s3_access_key, val, sizeof(s3_access_key) - 1);
      }
      else if (strncmp(line, "AWS_MRJUNEJUNE_SECRET_ACCESS_KEY=", 33) == 0)
      {
        char *val = line + 33;
        size_t len = strlen(val);
        while (len > 0 && (val[len-1] == '\n' || val[len-1] == '\r')) val[--len] = '\0';
        strncpy(s3_secret_key, val, sizeof(s3_secret_key) - 1);
      }
    }
    fclose(env_file);
  }

  // Initialize S3 config
  g_s3_config.access_key_id = s3_access_key;
  g_s3_config.secret_access_key = s3_secret_key;
  g_s3_config.region = g_s3_region;
  g_s3_config.bucket = g_s3_bucket;
  g_s3_config.endpoint = NULL;
  g_s3_config.use_path_style = FALSE;

  printf("[S3] Configured: region=%s, bucket=%s, key=%s...\n",
         g_s3_region, g_s3_bucket, s3_access_key[0] ? "***" : "(missing)");

  // Show current working directory
  char cwd[1024];
  if (getcwd(cwd, sizeof(cwd)) != NULL)
  {
    printf("[STARTUP] Current working directory: %s\n", cwd);
    printf("[STARTUP] Database path (relative): %s\n", g_db_path);
  }

  // Initialize database
  init_database();

  Seobeo_Router_Init();

  Seobeo_Router_Register("GET", "/", GetHomePage);
  Seobeo_Router_Register("GET", "/index.html", GetRedirectHomePage);

  Seobeo_Router_Register("GET", "/resume", GetResume);
  Seobeo_Router_Register("GET", "/resume/index.html", GetRedirectResume);

  Seobeo_Router_Register("GET", "/tools", GetTools);
  Seobeo_Router_Register("GET", "/tools/index.html", GetRedirectTools);

  Seobeo_Router_Register("GET", "/tools/markdown_to_html", GetMDToHTML);
  Seobeo_Router_Register("GET", "/tools/markdown_to_html/index.html", GetRedirectMarkDownToHtml);

  Seobeo_Router_Register("GET", "/tools/file_converter", GetFileConverter);
  Seobeo_Router_Register("GET", "/tools/file_converter/index.html", GetRedirectFileConverter);

  // -- File converter --/
  Seobeo_Router_Register("POST", "/api/convert/image-to-webp", ConvertImageToWebP);
  Seobeo_Router_Register("POST", "/api/convert/video-to-mp4", ConvertVideoToMP4);
  Seobeo_Router_Register("GET", "/api/download/:filename", DownloadConvertedFile);

  // -- S3 Upload --/
  Seobeo_Router_Register("POST", "/api/s3/upload-url", GetS3UploadUrl);

  // -- Media Upload --/
  Seobeo_Router_Register("POST", "/api/media/create", MediaCreate);
  Seobeo_Router_Register("POST", "/api/media/:id/uploaded", MediaUploaded);
  Seobeo_Router_Register("GET", "/api/media/:id/status", MediaStatus);

  // -- Editor --/
  Seobeo_Router_Register("POST", "/api/editor/save", EditorSave);
  Seobeo_Router_Register("GET", "/api/editor/load/:doc_id", EditorLoad);

  // -- Blog --/
  Seobeo_Router_Register("GET", "/blog", RenderBlogList);
  Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog);

  // -- Talk --/
  Seobeo_Router_Register("GET", "/talk", GetTalk);
  Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk);

  // -- Notes --/
  Seobeo_Router_Register("GET", "/notes", GetNotes);
  Seobeo_Router_Register("GET", "/notes/", GetNotes);
  Seobeo_Router_Register("GET", "/notes/index.html", GetNotes);
  Seobeo_Router_Register("GET", "/notes/login", GetNotesLogin);
  Seobeo_Router_Register("GET", "/notes/login/", GetNotesLogin);
  Seobeo_Router_Register("GET", "/notes/:note_id", GetNoteById);

  Seobeo_WebSocket_Server_Init();
  Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL);

  Seobeo_Log(SEOBEO_INFO, "WTF is going on\n");
  Seobeo_Web_Server_Start("mrjunejune/src", "6969", SEOBEO_MODE_EDGE, 1);
}