Mercurial
view mrjunejune/main.c @ 271:13d61401c57d
redirect Copilot cache to service state
Set XDG_CACHE_HOME from the configured inference state so the systemd service can extract Copilot outside its protected home directory.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Fri, 07 Aug 2026 13:24:05 -0700 |
| parents | 056790c4fb0d |
| children | e02e2036ef84 |
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 "mrjunejune/latex_renderer.h" #include "mrjunejune/conversation_api.h" #include "mrjunejune/auth_api.h" #include "mrjunejune/admin_api.h" #include "mrjunejune/template_renderer.h" #include "auth/auth_crypto.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <time.h> #include <sys/stat.h> #include <stdarg.h> #include <stdatomic.h> #include <pthread.h> #include <arpa/inet.h> #include <openssl/crypto.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 counter = 0; static _Atomic boolean g_latex_rendering = FALSE; static Seobeo_Worker_Pool *g_media_worker_pool = NULL; // Media processing context owned by a background worker. 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; typedef struct { char *input_path; char *output_path; int result; } File_Converter_Config; // 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; /* S3 credentials — never logged; zero after use in init. */ static char g_s3_access_key[128] = {0}; static char g_s3_secret_key[128] = {0}; /* Auth configuration */ static char g_auth_cookie_secret[AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES * 2 + 1] = {0}; static char g_auth_bootstrap_username[64] = {0}; static char g_auth_bootstrap_password_hash[AUTH_CRYPTO_PASSWORD_HASH_ENCODED_SIZE] = {0}; static char g_auth_trusted_proxy[AUTH_CRYPTO_IP_MAX_BYTES] = {0}; static int64 g_auth_session_idle_ttl = AUTH_API_SESSION_IDLE_TTL_DEFAULT; static int64 g_auth_session_abs_ttl = AUTH_API_SESSION_ABS_TTL_DEFAULT; static int64 g_auth_guest_ttl = AUTH_API_GUEST_TTL_DEFAULT; static boolean g_auth_dev_insecure = FALSE; static char g_server_host[128] = {0}; /* SERVER_HOST; empty → 0.0.0.0 */ /* Guest inference / quota configuration */ #define G_GUEST_DAILY_TURNS_DEFAULT 10 #define G_GUEST_DAILY_OUTPUT_TOKENS_DEFAULT 20000 #define G_GUEST_REQUEST_OUTPUT_TOKENS_MIN 1 #define G_GUEST_DAILY_TURNS_MIN 1 #define G_GUEST_DAILY_TURNS_MAX 10000 #define G_GUEST_DAILY_OUTPUT_TOKENS_MIN 1 #define G_GUEST_DAILY_OUTPUT_TOKENS_MAX 1000000 #define G_GUEST_REQUEST_OUTPUT_TOKENS_DEFAULT 2048 static boolean g_guest_inference_enabled = FALSE; static int64 g_guest_daily_turns = G_GUEST_DAILY_TURNS_DEFAULT; static int64 g_guest_daily_output_tokens = G_GUEST_DAILY_OUTPUT_TOKENS_DEFAULT; static int64 g_guest_request_output_tokens = G_GUEST_REQUEST_OUTPUT_TOKENS_DEFAULT; /* * Strict full-string integer parse: returns FALSE if value is empty, * has trailing non-digit characters, or overflows a long. */ static boolean config__parse_int64_strict(const char *value, int64 *out) { if (!value || value[0] == '\0') return FALSE; char *end = NULL; long v = strtol(value, &end, 10); if (end == value || *end != '\0') return FALSE; *out = (int64)v; return TRUE; } /* * Decode a lowercase or uppercase hex string into raw bytes. * Returns the number of decoded bytes, or 0 on any error. * hex_len must be even; each pair of hex chars produces one byte. */ static size_t config__hex_decode(const char *hex, uint8 *out, size_t out_capacity) { if (!hex || !out) return 0; size_t hex_len = strlen(hex); if (hex_len == 0 || hex_len % 2 != 0) return 0; size_t byte_count = hex_len / 2; if (byte_count > out_capacity) return 0; for (size_t i = 0; i < byte_count; i++) { int h, l; char hi = hex[i * 2]; char lo = hex[i * 2 + 1]; if (hi >= '0' && hi <= '9') h = hi - '0'; else if (hi >= 'a' && hi <= 'f') h = hi - 'a' + 10; else if (hi >= 'A' && hi <= 'F') h = hi - 'A' + 10; else return 0; if (lo >= '0' && lo <= '9') l = lo - '0'; else if (lo >= 'a' && lo <= 'f') l = lo - 'a' + 10; else if (lo >= 'A' && lo <= 'F') l = lo - 'A' + 10; else return 0; out[i] = (uint8)((h << 4) | l); } return byte_count; } static void load_config(const char *config_path) { boolean config_loaded = FALSE; FILE *f = fopen(config_path, "r"); char workspace_config_path[1024] = {0}; if (!f) { const char *workspace = getenv("BUILD_WORKSPACE_DIRECTORY"); if (workspace && workspace[0] != '\0') { int written = snprintf( workspace_config_path, sizeof(workspace_config_path), "%s/%s", workspace, config_path); if (written > 0 && (size_t)written < sizeof(workspace_config_path)) f = fopen(workspace_config_path, "r"); } } if (!f) { printf("[CONFIG] Warning: Could not open %s, using defaults\n", config_path); } else { config_loaded = TRUE; 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) { int64 v; if (!config__parse_int64_strict(value, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: S3_URL_EXPIRES must be a positive integer\n"); exit(1); } g_s3_url_expires = (int)v; } 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); } else if (strcmp(key, "AWS_MRJUNEJUNE_ACCESS_KEY") == 0) { strncpy(g_s3_access_key, value, sizeof(g_s3_access_key) - 1); } else if (strcmp(key, "AWS_MRJUNEJUNE_SECRET_ACCESS_KEY") == 0) { strncpy(g_s3_secret_key, value, sizeof(g_s3_secret_key) - 1); } else if (strcmp(key, "AUTH_COOKIE_SECRET") == 0) { /* Never log this value */ strncpy(g_auth_cookie_secret, value, sizeof(g_auth_cookie_secret) - 1); } else if (strcmp(key, "AUTH_BOOTSTRAP_USERNAME") == 0) { strncpy(g_auth_bootstrap_username, value, sizeof(g_auth_bootstrap_username) - 1); } else if (strcmp(key, "AUTH_BOOTSTRAP_PASSWORD_HASH") == 0) { strncpy(g_auth_bootstrap_password_hash, value, sizeof(g_auth_bootstrap_password_hash) - 1); } else if (strcmp(key, "AUTH_TRUSTED_PROXY") == 0) { strncpy(g_auth_trusted_proxy, value, sizeof(g_auth_trusted_proxy) - 1); } else if (strcmp(key, "AUTH_SESSION_IDLE_TTL") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_SESSION_IDLE_TTL must be a positive integer\n"); exit(1); } g_auth_session_idle_ttl = v; } else if (strcmp(key, "AUTH_SESSION_ABS_TTL") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_SESSION_ABS_TTL must be a positive integer\n"); exit(1); } g_auth_session_abs_ttl = v; } else if (strcmp(key, "AUTH_GUEST_TTL") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_GUEST_TTL must be a positive integer\n"); exit(1); } g_auth_guest_ttl = v; } else if (strcmp(key, "AUTH_DEV_INSECURE_COOKIE") == 0) { g_auth_dev_insecure = (strcmp(value, "1") == 0 || strcmp(value, "true") == 0); } else if (strcmp(key, "SERVER_HOST") == 0) { strncpy(g_server_host, value, sizeof(g_server_host) - 1); } else if (strcmp(key, "MRJUNEJUNE_ALLOW_GUEST_INFERENCE") == 0) { if (strcmp(value, "1") == 0 || strcasecmp(value, "true") == 0) g_guest_inference_enabled = TRUE; else if (strcmp(value, "0") == 0 || strcasecmp(value, "false") == 0) g_guest_inference_enabled = FALSE; else { fprintf( stderr, "[CONFIG] ERROR: MRJUNEJUNE_ALLOW_GUEST_INFERENCE must be true or false\n"); exit(1); } } else if (strcmp(key, "AUTH_GUEST_DAILY_TURNS") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v < G_GUEST_DAILY_TURNS_MIN || v > G_GUEST_DAILY_TURNS_MAX) { printf("[CONFIG] ERROR: AUTH_GUEST_DAILY_TURNS must be %d..%d\n", G_GUEST_DAILY_TURNS_MIN, G_GUEST_DAILY_TURNS_MAX); exit(1); } g_guest_daily_turns = v; } else if (strcmp(key, "AUTH_GUEST_DAILY_OUTPUT_TOKENS") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v < G_GUEST_DAILY_OUTPUT_TOKENS_MIN || v > G_GUEST_DAILY_OUTPUT_TOKENS_MAX) { printf("[CONFIG] ERROR: AUTH_GUEST_DAILY_OUTPUT_TOKENS must be %d..%d\n", G_GUEST_DAILY_OUTPUT_TOKENS_MIN, G_GUEST_DAILY_OUTPUT_TOKENS_MAX); exit(1); } g_guest_daily_output_tokens = v; } else if (strcmp(key, "AUTH_GUEST_REQUEST_OUTPUT_TOKENS") == 0) { int64 v; if (!config__parse_int64_strict(value, &v) || v < G_GUEST_REQUEST_OUTPUT_TOKENS_MIN) { printf("[CONFIG] ERROR: AUTH_GUEST_REQUEST_OUTPUT_TOKENS must be >= %d\n", G_GUEST_REQUEST_OUTPUT_TOKENS_MIN); exit(1); } g_guest_request_output_tokens = v; } } 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); printf("[CONFIG] Auth: secret=%s, bootstrap_user=%s, trusted_proxy=%s\n", g_auth_cookie_secret[0] ? "(set)" : "(not set)", g_auth_bootstrap_username[0] ? g_auth_bootstrap_username : "(none)", g_auth_trusted_proxy[0] ? "(set)" : "(not set)"); const char *database_path_override = getenv("MRJUNEJUNE_DB_PATH"); const char *test_tmpdir = getenv("TEST_TMPDIR"); if (database_path_override && database_path_override[0] != '\0') { strncpy(g_db_path, database_path_override, sizeof(g_db_path) - 1); } else if (test_tmpdir && test_tmpdir[0] != '\0') { snprintf(g_db_path, sizeof(g_db_path), "%s/mrjunejune.db", test_tmpdir); } /* Environment configuration is a compatibility/testing fallback only when * no config file is present. Dynamic supervisor wiring is read separately. */ if (!config_loaded) { const char *env; /* S3 / server config env overrides */ if ((env = getenv("UPLOAD_AUTH_TOKEN")) && env[0] != '\0') strncpy(g_upload_auth_token, env, sizeof(g_upload_auth_token) - 1); if ((env = getenv("S3_REGION")) && env[0] != '\0') strncpy(g_s3_region, env, sizeof(g_s3_region) - 1); if ((env = getenv("S3_BUCKET")) && env[0] != '\0') strncpy(g_s3_bucket, env, sizeof(g_s3_bucket) - 1); if ((env = getenv("S3_CLOUDFRONT_URL")) && env[0] != '\0') strncpy(g_s3_cloudfront_url, env, sizeof(g_s3_cloudfront_url) - 1); if ((env = getenv("S3_URL_EXPIRES")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: S3_URL_EXPIRES must be a positive integer\n"); exit(1); } g_s3_url_expires = (int)v; } if ((env = getenv("AWS_MRJUNEJUNE_ACCESS_KEY")) && env[0] != '\0') strncpy(g_s3_access_key, env, sizeof(g_s3_access_key) - 1); if ((env = getenv("AWS_MRJUNEJUNE_SECRET_ACCESS_KEY")) && env[0] != '\0') strncpy(g_s3_secret_key, env, sizeof(g_s3_secret_key) - 1); /* Auth env overrides */ if ((env = getenv("AUTH_COOKIE_SECRET")) && env[0] != '\0') strncpy(g_auth_cookie_secret, env, sizeof(g_auth_cookie_secret) - 1); if ((env = getenv("AUTH_BOOTSTRAP_USERNAME")) && env[0] != '\0') strncpy(g_auth_bootstrap_username, env, sizeof(g_auth_bootstrap_username) - 1); if ((env = getenv("AUTH_BOOTSTRAP_PASSWORD_HASH")) && env[0] != '\0') strncpy(g_auth_bootstrap_password_hash, env, sizeof(g_auth_bootstrap_password_hash) - 1); if ((env = getenv("AUTH_TRUSTED_PROXY")) && env[0] != '\0') strncpy(g_auth_trusted_proxy, env, sizeof(g_auth_trusted_proxy) - 1); if ((env = getenv("AUTH_SESSION_IDLE_TTL")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_SESSION_IDLE_TTL must be a positive integer\n"); exit(1); } g_auth_session_idle_ttl = v; } if ((env = getenv("AUTH_SESSION_ABS_TTL")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_SESSION_ABS_TTL must be a positive integer\n"); exit(1); } g_auth_session_abs_ttl = v; } if ((env = getenv("AUTH_GUEST_TTL")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v <= 0) { fprintf(stderr, "[CONFIG] ERROR: AUTH_GUEST_TTL must be a positive integer\n"); exit(1); } g_auth_guest_ttl = v; } if ((env = getenv("AUTH_DEV_INSECURE_COOKIE")) && env[0] != '\0') g_auth_dev_insecure = (strcmp(env, "1") == 0 || strcmp(env, "true") == 0); if ((env = getenv("SERVER_HOST")) && env[0] != '\0') strncpy(g_server_host, env, sizeof(g_server_host) - 1); /* Guest inference enable: runtime-only, boolean parsed strictly. */ if ((env = getenv("MRJUNEJUNE_ALLOW_GUEST_INFERENCE")) && env[0] != '\0') g_guest_inference_enabled = (strcmp(env, "1") == 0 || strcasecmp(env, "true") == 0); else if ((env = getenv("MRJUNEJUNE_ALLOW_ANONYMOUS_INFERENCE")) && env[0] != '\0') g_guest_inference_enabled = (strcmp(env, "1") == 0 || strcasecmp(env, "true") == 0); /* Guest quota env overrides: strict full-string integer, fail on malformed. */ if ((env = getenv("AUTH_GUEST_DAILY_TURNS")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v < G_GUEST_DAILY_TURNS_MIN || v > G_GUEST_DAILY_TURNS_MAX) { fprintf(stderr, "[CONFIG] ERROR: AUTH_GUEST_DAILY_TURNS must be %d..%d\n", G_GUEST_DAILY_TURNS_MIN, G_GUEST_DAILY_TURNS_MAX); exit(1); } g_guest_daily_turns = v; } if ((env = getenv("AUTH_GUEST_DAILY_OUTPUT_TOKENS")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v < G_GUEST_DAILY_OUTPUT_TOKENS_MIN || v > G_GUEST_DAILY_OUTPUT_TOKENS_MAX) { fprintf(stderr, "[CONFIG] ERROR: AUTH_GUEST_DAILY_OUTPUT_TOKENS must be %d..%d\n", G_GUEST_DAILY_OUTPUT_TOKENS_MIN, G_GUEST_DAILY_OUTPUT_TOKENS_MAX); exit(1); } g_guest_daily_output_tokens = v; } if ((env = getenv("AUTH_GUEST_REQUEST_OUTPUT_TOKENS")) && env[0] != '\0') { int64 v; if (!config__parse_int64_strict(env, &v) || v < G_GUEST_REQUEST_OUTPUT_TOKENS_MIN) { fprintf(stderr, "[CONFIG] ERROR: AUTH_GUEST_REQUEST_OUTPUT_TOKENS must be >= %d\n", G_GUEST_REQUEST_OUTPUT_TOKENS_MIN); exit(1); } g_guest_request_output_tokens = v; } } } 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) { (void)sig; stop_server = 1; Seobeo_Web_Server_Stop(); } static Seobeo_Request_Entry *html_render_error(Dowa_Arena *arena, const char *msg) { Seobeo_Request_Entry *resp = NULL; Dowa_HashMap_Push_Arena(resp, "status", "500", arena); Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain; charset=utf-8", arena); Dowa_HashMap_Push_Arena(resp, "body", (char *)msg, arena); return resp; } #define HTML_PAGE_CAP (128 * 1024) Seobeo_Request_Entry* GetHomePage(Seobeo_Request_Entry *req, Dowa_Arena *arena) { Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/resume/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/tools/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/tools/markdown_to_html/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/tools/file_converter/index.html", arena)) return html_render_error(arena, "Internal Server Error"); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); return resp; } Seobeo_Request_Entry* GetHlsPlayer(Seobeo_Request_Entry *req, Dowa_Arena *arena) { Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/tools/hls_player/index.html", arena)) return html_render_error(arena, "Internal Server Error"); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); return resp; } Seobeo_Request_Entry* GetLatexEditor(Seobeo_Request_Entry *req, Dowa_Arena *arena) { Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/tools/latex_editor/index.html", arena)) return html_render_error(arena, "Internal Server Error"); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); return resp; } static Seobeo_Request_Entry *LatexErrorResponse( Dowa_Arena *arena, int status, const char *message) { Seobeo_Request_Entry *resp = NULL; char *status_value = Dowa_Arena_Allocate(arena, 16); snprintf(status_value, 16, "%d", status); char *body = Dowa_Arena_Allocate(arena, strlen(message) + 1); strcpy(body, message); Dowa_HashMap_Push_Arena(resp, "status", status_value, arena); Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain; charset=utf-8", arena); Dowa_HashMap_Push_Arena(resp, "cache-control", "no-store", arena); Dowa_HashMap_Push_Arena(resp, "body", body, arena); return resp; } Seobeo_Request_Entry *RenderLatexPdf(Seobeo_Request_Entry *req, Dowa_Arena *arena) { void *body_kv = Dowa_HashMap_Get_Ptr(req, "Body"); void *length_kv = Dowa_HashMap_Get_Ptr(req, "Content-Length"); if (!body_kv || !length_kv) return LatexErrorResponse(arena, 400, "LaTeX source and Content-Length are required."); const char *length_value = ((Seobeo_Request_Entry *)length_kv)->value; char *length_end = NULL; errno = 0; unsigned long long parsed_length = strtoull(length_value, &length_end, 10); if (errno != 0 || length_end == length_value || *length_end != '\0' || parsed_length == 0) return LatexErrorResponse(arena, 400, "Content-Length is invalid."); if (parsed_length > LATEX_SOURCE_MAX_BYTES) return LatexErrorResponse(arena, 413, "LaTeX source exceeds the 64 KiB limit."); boolean expected = FALSE; if (!atomic_compare_exchange_strong( &g_latex_rendering, &expected, TRUE)) return LatexErrorResponse(arena, 429, "The LaTeX compiler is busy. Try again shortly."); Latex_Render_Result render = Latex_Render( (const uint8 *)((Seobeo_Request_Entry *)body_kv)->value, (size_t)parsed_length); atomic_store(&g_latex_rendering, FALSE); if (render.status != LATEX_RENDER_OK) { int status = 500; if (render.status == LATEX_RENDER_INVALID_INPUT) status = 400; else if (render.status == LATEX_RENDER_COMPILE_ERROR) status = 422; else if (render.status == LATEX_RENDER_TIMEOUT) status = 504; else if (render.status == LATEX_RENDER_COMPILER_UNAVAILABLE) status = 503; const char *diagnostics = render.diagnostics ? render.diagnostics : "LaTeX rendering failed."; char *body = Dowa_Arena_Allocate(arena, strlen(diagnostics) + 1); strcpy(body, diagnostics); Latex_Render_Result_Destroy(&render); return LatexErrorResponse(arena, status, body); } char *pdf = Dowa_Arena_Allocate(arena, render.pdf_size + 1); if (!pdf) { Latex_Render_Result_Destroy(&render); return LatexErrorResponse(arena, 500, "The generated PDF is too large to return."); } memcpy(pdf, render.pdf_data, render.pdf_size); pdf[render.pdf_size] = '\0'; char *content_length = Dowa_Arena_Allocate(arena, 32); snprintf(content_length, 32, "%zu", render.pdf_size); Latex_Render_Result_Destroy(&render); Seobeo_Request_Entry *resp = NULL; Dowa_HashMap_Push_Arena(resp, "status", "200", arena); Dowa_HashMap_Push_Arena(resp, "content-type", "application/pdf", arena); Dowa_HashMap_Push_Arena(resp, "content-length", content_length, arena); Dowa_HashMap_Push_Arena(resp, "content-disposition", "inline; filename=\"document.pdf\"", arena); Dowa_HashMap_Push_Arena(resp, "cache-control", "no-store", arena); Dowa_HashMap_Push_Arena(resp, "x-content-type-options", "nosniff", arena); Dowa_HashMap_Push_Arena(resp, "body", pdf, arena); return resp; } // Joinable worker function for local image conversion. void Simple_WebpConverter_Background(void *arg) { File_Converter_Config *configuration = (File_Converter_Config *)arg; char cmd[1024]; snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>/tmp/error_log", configuration->input_path, configuration->output_path); Seobeo_Log(SEOBEO_INFO, "[MEDIA] Running FFmpeg: %s\n", cmd); configuration->result = system(cmd); Seobeo_Log( SEOBEO_INFO, "[MEDIA] FFmpeg result: %d\n", configuration->result); if (configuration->result != 0) { Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: FFmpeg conversion failed\n"); return; } Seobeo_Log( SEOBEO_INFO, "[MEDIA] Successfully converted to webp: %s\n", configuration->output_path); } 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); Seobeo_Log(SEOBEO_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)Seobeo_Thread_Current_Id() ^ 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)Seobeo_Thread_Current_Id() ^ 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); Seobeo_Log(SEOBEO_DEBUG, "output_path %s\n", output_path); Seobeo_Log(SEOBEO_DEBUG, "open_flags: 0x%x\n", open_flags); Seobeo_Log(SEOBEO_DEBUG, "input_path: %s\n", input_path); int output_fd = open(output_path, open_flags, 0600); Seobeo_Log(SEOBEO_DEBUG, "output_fd: %d\n", output_fd); if (output_fd == -1) { unlink(input_path); Seobeo_Log(SEOBEO_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); File_Converter_Config *configuration = Dowa_Arena_Allocate(arena, sizeof(File_Converter_Config)); configuration->input_path = input_path; configuration->output_path = output_path; configuration->result = -1; Seobeo_Thread *p_worker = Seobeo_Thread_Start( Simple_WebpConverter_Background, configuration, NULL); if (!p_worker || Seobeo_Thread_Join(p_worker) != SEOBEO_WORKER_OK || configuration->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; } 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); Seobeo_Log(SEOBEO_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)Seobeo_Thread_Current_Id() ^ 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)Seobeo_Thread_Current_Id() ^ 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; } static boolean Converted_Filename_Is_Valid(const char *filename) { if (!filename) return FALSE; const char *extension = strrchr(filename, '.'); if (!extension || (strcmp(extension, ".webp") != 0 && strcmp(extension, ".mp4") != 0)) return FALSE; if ((size_t)(extension - filename) != 36) return FALSE; for (size_t i = 0; i < 36; i++) { char value = filename[i]; if (i == 8 || i == 13 || i == 18 || i == 23) { if (value != '-') return FALSE; continue; } if (!((value >= '0' && value <= '9') || (value >= 'a' && value <= 'f') || (value >= 'A' && value <= 'F'))) return FALSE; } return TRUE; } static Seobeo_Request_Entry *Converted_File_Error( Dowa_Arena *arena, char *status, char *message) { Seobeo_Request_Entry *resp = NULL; Dowa_HashMap_Push_Arena(resp, "status", status, arena); Dowa_HashMap_Push_Arena(resp, "content-type", "text/plain", arena); Dowa_HashMap_Push_Arena(resp, "body", message, arena); return resp; } Seobeo_Request_Entry *DeleteConvertedFile( Seobeo_Request_Entry *req, Dowa_Arena *arena) { void *filename_kv = Dowa_HashMap_Get_Ptr(req, ":filename"); const char *filename = filename_kv ? ((Seobeo_Request_Entry *)filename_kv)->value : NULL; if (!Converted_Filename_Is_Valid(filename)) return Converted_File_Error(arena, "400", "Invalid converted filename"); char filepath[512]; snprintf(filepath, sizeof(filepath), "/tmp/%s", filename); if (unlink(filepath) != 0 && errno != ENOENT) return Converted_File_Error(arena, "500", "Unable to delete converted file"); Seobeo_Request_Entry *resp = NULL; Dowa_HashMap_Push_Arena(resp, "status", "204", arena); Dowa_HashMap_Push_Arena(resp, "body", "", arena); 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; if (!Converted_Filename_Is_Valid(filename)) return Converted_File_Error(arena, "400", "Invalid converted filename"); 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); char *content_type = "application/octet-stream"; if (strcmp(strrchr(filename, '.'), ".webp") == 0) 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/blog/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, file_path, arena)) { Seobeo_Request_Entry *err = NULL; Dowa_HashMap_Push_Arena(err, "status", "404", arena); Dowa_HashMap_Push_Arena(err, "content-type", "text/plain; charset=utf-8", arena); Dowa_HashMap_Push_Arena(err, "body", "Not found", arena); return err; } 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/talk/index.html", arena)) return html_render_error(arena, "Internal Server Error"); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); return resp; } Seobeo_Request_Entry *GetJrpg(Seobeo_Request_Entry *req, Dowa_Arena *arena) { Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/jrpg/index.html", arena)) return html_render_error(arena, "Internal Server Error"); Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); Dowa_HashMap_Push_Arena(resp, "referrer-policy", "no-referrer", arena); return resp; } Seobeo_Request_Entry *GetLogin(Seobeo_Request_Entry *req, Dowa_Arena *arena) { (void)req; Seobeo_Request_Entry *resp = NULL; char *final_body = Dowa_Arena_Allocate(arena, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/login/index.html", arena)) { Seobeo_Request_Entry *err = NULL; Dowa_HashMap_Push_Arena(err, "status", "500", arena); Dowa_HashMap_Push_Arena(err, "content-type", "text/plain; charset=utf-8", arena); Dowa_HashMap_Push_Arena(err, "cache-control", "no-store", arena); Dowa_HashMap_Push_Arena(err, "body", "Internal Server Error", arena); return err; } Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); Dowa_HashMap_Push_Arena( resp, "content-type", "text/html; charset=utf-8", arena); Dowa_HashMap_Push_Arena(resp, "cache-control", "no-store", arena); Dowa_HashMap_Push_Arena(resp, "pragma", "no-cache", arena); Dowa_HashMap_Push_Arena(resp, "x-content-type-options", "nosniff", arena); Dowa_HashMap_Push_Arena(resp, "referrer-policy", "no-referrer", arena); Dowa_HashMap_Push_Arena( resp, "content-security-policy", "frame-ancestors 'none'", 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/notes/login.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/notes/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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, HTML_PAGE_CAP); if (!final_body || !Mjj_Template_Render_File(final_body, HTML_PAGE_CAP, "/notes/index.html", arena)) return html_render_error(arena, "Internal Server Error"); 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(HlsPlayer, "/tools/hls_player") CREATE_REDIRECT_HANDLER(LatexEditor, "/tools/latex_editor") CREATE_REDIRECT_HANDLER(Talk, "/talk") CREATE_REDIRECT_HANDLER(Jrpg, "/jrpg") 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)Seobeo_Thread_Current_Id() ^ 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)Seobeo_Thread_Current_Id() ^ 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') RETURNING id"; const char *params[] = { token, filename, content_type, s3_key_original, s3_key_processed }; Deita_Result_Set *id_result = Deita_Query_Execute_Prepared( g_db_connection, insert_query, 5, params, arena); if (!id_result || !Deita_Result_Set_Next(id_result)) { if (id_result) Deita_Result_Set_Free(id_result); 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; } int64 media_id = Deita_Result_Set_Get_Integer(id_result, 0); 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; } // Worker-pool task for S3 media processing. void Media_Process_Background(void *arg) { Media_Processing_Context *ctx = (Media_Processing_Context *)arg; Seobeo_Log(SEOBEO_INFO, "[MEDIA] Background worker 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 a worker-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] Worker ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id); return; } // 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); return; } 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)Seobeo_Thread_Current_Id() ^ counter++; uint32 seed2 = (uint32)time(NULL) ^ (uint32)Seobeo_Thread_Current_Id() ^ 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); return; } 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); return; } 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 (HTTP status: %d)\n", (long long)ctx->media_id, error_msg, upload_result.status_code); 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); return; } 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); } // 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); // Images are processed asynchronously by the bounded media pool. if (strncmp(content_type_copy, "image/", 6) == 0) { Seobeo_Log(SEOBEO_INFO, "[MEDIA] Queueing image processing for media_id=%lld\n", (long long)media_id); // The pool owns this context after a successful submission. Media_Processing_Context *ctx = malloc(sizeof(Media_Processing_Context)); if (!ctx) { 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\":\"Unable to allocate media work\"}", arena); return resp; } 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_Worker_Result worker_result = g_media_worker_pool ? Seobeo_Worker_Pool_Submit( g_media_worker_pool, Media_Process_Background, ctx, free) : SEOBEO_WORKER_STOPPED; if (worker_result != SEOBEO_WORKER_OK) { Seobeo_Log( SEOBEO_ERROR, "[MEDIA] Worker submission failed with result=%d for media_id=%lld\n", worker_result, (long long)media_id); free(ctx); const char *update_error = "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?"; const char *error_params[] = { "Media worker queue is unavailable", media_id_str, }; Deita_Query_Execute_Update_Prepared( g_db_connection, update_error, 2, error_params); Dowa_HashMap_Push_Arena(resp, "status", "503", arena); Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Media worker queue is unavailable\"}", arena); return resp; } Seobeo_Log( SEOBEO_INFO, "[MEDIA] Submitted media_id=%lld to the worker pool\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) { signal(SIGINT, handle_sigint); signal(SIGTERM, handle_sigint); // Load the ignored runtime config when present; environment overrides follow. const char *config_path = getenv("MRJUNEJUNE_CONFIG_PATH"); load_config( config_path && config_path[0] != '\0' ? config_path : "mrjunejune/.config"); // Initialize S3 config using global credentials populated by load_config g_s3_config.access_key_id = g_s3_access_key; g_s3_config.secret_access_key = g_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, g_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(); { /* Validate per-request token cap against daily limit now that both * are finalised (env overrides run inside load_config). */ if (g_guest_request_output_tokens > g_guest_daily_output_tokens) g_guest_request_output_tokens = g_guest_daily_output_tokens; Conversation_API_Guest_Policy policy = { .guest_inference_enabled = g_guest_inference_enabled, .daily_turns = g_guest_daily_turns, .daily_output_tokens = g_guest_daily_output_tokens, .request_output_tokens = g_guest_request_output_tokens, }; if (!Conversation_API_Init(g_db_path, &policy)) Seobeo_Log(SEOBEO_ERROR, "[CONVERSATION] Store unavailable\n"); } /* Validate and decode AUTH_COOKIE_SECRET: hex string → raw bytes. */ uint8 cookie_secret_bytes[AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES]; size_t cookie_secret_byte_len = 0; { size_t hex_len = strlen(g_auth_cookie_secret); boolean bad_len = ( hex_len < (size_t)(AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES * 2) || hex_len > (size_t)(AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES * 2) || hex_len % 2 != 0); if (bad_len) { OPENSSL_cleanse(g_auth_cookie_secret, sizeof(g_auth_cookie_secret)); fprintf(stderr, "[AUTH] AUTH_COOKIE_SECRET must be %d–%d hex chars " "(%d–%d random bytes). " "Generate with: openssl rand -hex 32\n", AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES * 2, AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES * 2, AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES, AUTH_CRYPTO_COOKIE_SECRET_MAX_BYTES); exit(1); } cookie_secret_byte_len = config__hex_decode( g_auth_cookie_secret, cookie_secret_bytes, sizeof(cookie_secret_bytes)); OPENSSL_cleanse(g_auth_cookie_secret, sizeof(g_auth_cookie_secret)); if (cookie_secret_byte_len < (size_t)AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_COOKIE_SECRET contains non-hex characters or " "is too short.\n"); exit(1); } } /* Bootstrap pairing: both username+hash must be set, or both absent. */ { boolean has_user = g_auth_bootstrap_username[0] != '\0'; boolean has_hash = g_auth_bootstrap_password_hash[0] != '\0'; if (has_user != has_hash) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_BOOTSTRAP_USERNAME and AUTH_BOOTSTRAP_PASSWORD_HASH " "must both be set or both absent.\n"); exit(1); } if (has_hash && Auth_Crypto_Password_Hash_Validate(g_auth_bootstrap_password_hash) != AUTH_CRYPTO_OK) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_BOOTSTRAP_PASSWORD_HASH has unrecognized format " "(expected zenbu-scrypt$v=1$...).\n"); exit(1); } } /* TTL validation: all positive, idle <= abs, all within 1 min – 1 year. */ #define CONFIG_TTL_MIN_SECS 60 #define CONFIG_TTL_MAX_SECS 31536000 { int64 idle = g_auth_session_idle_ttl; int64 abst = g_auth_session_abs_ttl; int64 guest = g_auth_guest_ttl; if (idle <= 0 || abst <= 0 || guest <= 0 || idle < CONFIG_TTL_MIN_SECS || idle > CONFIG_TTL_MAX_SECS || abst < CONFIG_TTL_MIN_SECS || abst > CONFIG_TTL_MAX_SECS || guest < CONFIG_TTL_MIN_SECS || guest > CONFIG_TTL_MAX_SECS) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] TTL values must be %d–%d seconds.\n", CONFIG_TTL_MIN_SECS, CONFIG_TTL_MAX_SECS); exit(1); } if (idle > abst) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_SESSION_IDLE_TTL must not exceed " "AUTH_SESSION_ABS_TTL.\n"); exit(1); } } /* Trusted proxy: validate and canonicalize via inet_pton/inet_ntop. */ if (g_auth_trusted_proxy[0] != '\0') { struct in_addr addr4; struct in6_addr addr6; char canonical[AUTH_CRYPTO_IP_MAX_BYTES]; canonical[0] = '\0'; if (inet_pton(AF_INET, g_auth_trusted_proxy, &addr4) == 1) { if (!inet_ntop(AF_INET, &addr4, canonical, sizeof(canonical))) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_TRUSTED_PROXY: IPv4 canonicalization failed.\n"); exit(1); } } else if (inet_pton(AF_INET6, g_auth_trusted_proxy, &addr6) == 1) { if (!inet_ntop(AF_INET6, &addr6, canonical, sizeof(canonical))) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_TRUSTED_PROXY: IPv6 canonicalization failed.\n"); exit(1); } } else { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_TRUSTED_PROXY must be a valid IPv4 or IPv6 " "address (e.g. 192.168.1.1 or ::1).\n"); exit(1); } strncpy(g_auth_trusted_proxy, canonical, sizeof(g_auth_trusted_proxy) - 1); g_auth_trusted_proxy[sizeof(g_auth_trusted_proxy) - 1] = '\0'; } /* * Loopback enforcement: AUTH_DEV_INSECURE_COOKIE=true is only permitted * when SERVER_HOST is explicitly a loopback address. Production/edge * deployments must use Secure cookies. */ { boolean is_loopback = ( strcmp(g_server_host, "127.0.0.1") == 0 || strcmp(g_server_host, "::1") == 0 || strcmp(g_server_host, "localhost") == 0); if (g_auth_dev_insecure && !is_loopback) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] AUTH_DEV_INSECURE_COOKIE=true requires SERVER_HOST " "to be a loopback address (127.0.0.1, ::1, or localhost). " "Set SERVER_HOST=127.0.0.1 for local development.\n"); exit(1); } } /* Initialize auth module — fail closed: no auth means no server. */ { if (!Auth_API_Init( g_db_path, cookie_secret_bytes, cookie_secret_byte_len, g_auth_bootstrap_username[0] ? g_auth_bootstrap_username : NULL, g_auth_bootstrap_password_hash[0] ? g_auth_bootstrap_password_hash : NULL, g_auth_trusted_proxy[0] ? g_auth_trusted_proxy : NULL, g_auth_session_idle_ttl, g_auth_session_abs_ttl, g_auth_guest_ttl, g_auth_dev_insecure)) { OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); fprintf(stderr, "[AUTH] Auth init failed — refusing to start server.\n"); exit(1); } OPENSSL_cleanse(cookie_secret_bytes, sizeof(cookie_secret_bytes)); } const char *sidecar_path = getenv("MRJUNEJUNE_INFERENCE_SIDECAR_PATH"); const char *copilot_cli_path = getenv("MRJUNEJUNE_COPILOT_CLI_PATH"); if (sidecar_path && copilot_cli_path) { if (!Conversation_API_Enable_Inference(sidecar_path, copilot_cli_path)) Seobeo_Log(SEOBEO_ERROR, "[INFERENCE] Sidecar unavailable\n"); } else { Seobeo_Log( SEOBEO_WARNING, "[INFERENCE] Runtime paths not configured; turns return 503\n"); } g_media_worker_pool = Seobeo_Worker_Pool_Create(2, 16); if (!g_media_worker_pool) { Seobeo_Log( SEOBEO_ERROR, "[MEDIA] Unable to initialize the media worker pool\n"); } Seobeo_Router_Init(); Auth_API_Register_Routes(); Admin_API_Register_Routes(); Conversation_API_Register_Routes(); 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); Seobeo_Router_Register("GET", "/tools/hls_player", GetHlsPlayer); Seobeo_Router_Register("GET", "/tools/hls_player/index.html", GetRedirectHlsPlayer); Seobeo_Router_Register("GET", "/tools/latex_editor", GetLatexEditor); Seobeo_Router_Register("GET", "/tools/latex_editor/index.html", GetRedirectLatexEditor); // -- 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); Seobeo_Router_Register("DELETE", "/api/download/:filename", DeleteConvertedFile); Seobeo_Router_Register("POST", "/api/latex/render", RenderLatexPdf); // -- 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); // -- JRPG agent chat --/ Seobeo_Router_Register("GET", "/jrpg", GetJrpg); Seobeo_Router_Register("GET", "/jrpg/index.html", GetRedirectJrpg); // -- Login --/ Seobeo_Router_Register("GET", "/login", GetLogin); // -- 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"); const char *server_port = getenv("MRJUNEJUNE_PORT"); if (!server_port || server_port[0] == '\0') server_port = "6969"; const char *server_bind = g_server_host[0] ? g_server_host : "0.0.0.0"; Mjj_Template_Renderer_Init("mrjunejune/src"); int server_result = Seobeo_Web_Server_Start_On( server_bind, "mrjunejune/src", server_port, SEOBEO_MODE_EDGE, 4); Seobeo_Worker_Pool_Destroy(g_media_worker_pool); g_media_worker_pool = NULL; Conversation_API_Destroy(); Auth_API_Destroy(); if (server_result != 0) { fprintf(stderr, "[STARTUP] Server bind/listen failed (host=%s port=%s)\n", server_bind, server_port); return 1; } return 0; }