Mercurial
view mrjunejune/main.c @ 148:76cd7afa6b8e
[Configs] Updated configs and finally added ctags.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sat, 10 Jan 2026 05:04:19 -0800 |
| parents | 7a63e41a21fb |
| children | 1c0878eb17de |
line wrap: on
line source
// Debug mode is now controlled by build target: use //seobeo:seobeo_tcp_server_ws_debug #include "seobeo/seobeo.h" #include <time.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; void handle_sigint(int sig) { printf("Failed\n"); stop_server = 1; } void Seobeo_ServerSideRender( char *final_body, char *path, Dowa_Arena *arena ) { Seobeo_Log(SEOBEO_DEBUG, "[Curr] %s\n", path); size_t html_size = 0; char *template = Seobeo_Web_LoadFile(path, &html_size); if (!template) return; Seobeo_Log(SEOBEO_DEBUG, "[Curr] ??\n"); 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; 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, "[Curr] Sub content: %s\n", sub_content); 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); free(template); } 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_ServerSideRender(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_ServerSideRender(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_ServerSideRender(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_ServerSideRender(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_ServerSideRender(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) { printf("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++) { printf(" 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); char *uuid4 = (char *)Dowa_Arena_Allocate(arena, UUID_LEN); Dowa_String_UUID((uint32)time(NULL), uuid4); char *input_path = Dowa_Arena_Allocate(arena, TMP_FILE_LENGTH); snprintf(input_path, TMP_FILE_LENGTH, "/tmp/%s", uuid4); int input_fd = mkstemp(input_path); 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); int open_flags = O_RDWR | O_CREAT | O_EXCL; uint32 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); // Set proper Content-Length for binary data 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_ServerSideRender(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_Log(SEOBEO_DEBUG, "[CURR], Hello\n"); Seobeo_Request_Entry *resp = NULL; void *blog_id_kv = Dowa_HashMap_Get_Ptr(req, ":blog_id"); if (!blog_id_kv) { char *error_msg = "No Blog Id"; 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 *blog_id = ((Seobeo_Request_Entry *)blog_id_kv)->value; char *html_path = Dowa_Arena_Allocate(arena, 512); sprintf(html_path, "/blog/%s/index.html", blog_id); char *final_body = Dowa_Arena_Allocate(arena, 100 * 1024); // TODO: Think about the sizes Seobeo_ServerSideRender(final_body, html_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_ServerSideRender(final_body, "/talk/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") int main(void) { 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); // -- Blog --/ Seobeo_Router_Register("GET", "/blog", RenderBlogList); Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog); // -- Talk --/ Seobeo_Router_Register("GET", "/talk", GetTalk); // TODO: Bug where I can't est redriect huh... Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk); printf("Registered Websockets\n"); Seobeo_WebSocket_Server_Init(); Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL); Seobeo_Web_Server_Start("mrjunejune/src", "6969", SEOBEO_MODE_EDGE, 3); }