changeset 78:e7bf9e002850

amend
author June Park <parkjune1995@gmail.com>
date Wed, 31 Dec 2025 15:07:43 -0800
parents c348ac875294
children 5710108c949e
files mrjunejune/main.c mrjunejune/pages/index.html mrjunejune/pages/parts/header.html mrjunejune/pages/parts/headers.html mrjunejune/pages/resume/index.html mrjunejune/pages/tools/index.html mrjunejune/pages/tools/markdown_to_html/index.html seobeo/s_router.c seobeo/s_web.c seobeo/seobeo.h
diffstat 10 files changed, 195 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- a/mrjunejune/main.c	Wed Dec 31 14:17:40 2025 -0800
+++ b/mrjunejune/main.c	Wed Dec 31 15:07:43 2025 -0800
@@ -8,20 +8,88 @@
   stop_server = 1;
 }
 
+void Seobeo_ServerSideRender(
+    char *final_body,
+    char *path,
+    Dowa_Arena *arena
+) {
+  size_t html_size = 0;
+  char *template = Seobeo_Web_LoadFile(path, &html_size);
+  if (!template) return;
+
+  size_t current_offset = 0;
+  char *cursor = template;
+
+  int32 token_len = 2;
+
+  while (true)
+  {
+    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);
+    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 *body = Dowa_Arena_Allocate(arena, 10 * 1024);
-  size_t body_size = 0; 
-  char *file_content = Seobeo_Web_LoadFile("/index.html", &body_size);
-  snprintf(body, 10 * 1024, file_content);
-  free(file_content);
+  Seobeo_Request_Entry *resp = NULL; 
+  char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024); // 50KB buffer
+  Seobeo_ServerSideRender(final_body, "/index.html", arena);
+  Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
+  return resp;
+}
 
-  Dowa_HashMap_Push_Arena(resp, "body", body, arena);
+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* GetUser(Seobeo_Request_Entry *req, Dowa_Arena *arena)
 {
@@ -36,10 +104,20 @@
   return resp;
 }
 
-
 int main(void)
 {
   Seobeo_Router_Init();
   Seobeo_Router_Register("GET", "/", GetHomePage);
+  Seobeo_Router_Register("GET", "/index.html", GetHomePage);
+
+  Seobeo_Router_Register("GET", "/resume", GetResume);
+  Seobeo_Router_Register("GET", "/resume/index.html", GetResume);
+
+  Seobeo_Router_Register("GET", "/tools", GetTools);
+  Seobeo_Router_Register("GET", "/tools/index.html", GetTools);
+
+  Seobeo_Router_Register("GET", "/tools/markdown_to_html", GetMDToHTML);
+  Seobeo_Router_Register("GET", "/tools/markdown_to_html/index.html", GetMDToHTML);
+
   Seobeo_Web_Server_Start("mrjunejune/pages", "6969", SEOBEO_MODE_EDGE, 2);
 }
--- a/mrjunejune/pages/index.html	Wed Dec 31 14:17:40 2025 -0800
+++ b/mrjunejune/pages/index.html	Wed Dec 31 15:07:43 2025 -0800
@@ -6,7 +6,7 @@
   </head>
   <body>
      <main>
-       <^ parts/header.html ^>
+       {{/parts/header.html}}
        <h1> Useful scripts </h1>
        <ul>
          <li> <a href="/resume"> Resume </a> </li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mrjunejune/pages/parts/header.html	Wed Dec 31 15:07:43 2025 -0800
@@ -0,0 +1,102 @@
+<header>
+	<nav>
+      <h2 class="header-logo">
+        <div id="themeToggle">
+          <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_favicon.svg" height="50" width="50">
+        </div>
+        <a href="/">MrJuneJune</a>
+      </h2>
+		<div class="internal-links">
+			<a href="/resume">Resume</a>
+			<a href="/tools">Tools</a>
+    </div>
+    <div class="social-links">
+			<a href="https://github.com/mrjunejune" target="_blank">
+				<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
+					><path
+						fill="currentColor"
+						d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
+					></path></svg>
+			</a>
+		</div>
+	</nav>
+</header>
+<style>
+  :root {
+    --header-background: var(--white);
+    --header-color: rgb(var(--black));
+    --link-hover-accent: var(--awesome);
+  	--social-link-hover: rgb(var(--gray-dark));
+  }
+  .internal-links {
+    width: 33%;
+  }
+  .header-logo {
+    display: flex;
+    align-items: center;
+  }
+  header {
+  	margin: auto;
+  	padding: 0 1em;
+      background: var(--header-background);
+      color: rgb(var(--header-color));
+      font-family: "Atkinson", sans-serif;
+      box-shadow: 0 2px 8px rgba(var(--black), 5%);
+      width: 720px;
+      max-width: calc(100% - 2em);
+
+  }
+  h2 {
+  	margin: 0;
+  	font-size: 1em;
+  }
+  h2 a,
+  h2 a.active {
+  	text-decoration: none;
+  }
+  nav {
+  	display: flex;
+  	align-items: center;
+  	justify-content: space-between;
+  }
+  nav a {
+  	padding: 1em 0.5em;
+  	color: rgb(var(--header-color));
+  	border-bottom: 4px solid transparent;
+  	text-decoration: none;
+  }
+  nav a.active {
+  	text-decoration: none;
+  	border-bottom-color: var(--link-hover-accent);
+  }
+  .social-links,
+  .social-links a {
+    display: flex;
+  }
+  .social-links a {
+  	text-decoration: none;
+  }
+  .social-links a:hover {
+  	color: rgb(var(--social-link-hover));
+  }
+  @media (max-width: 720px) {
+  	.social-links {
+  		display: none;
+  	}
+  .internal-links {
+    width: 45%;
+  }
+  }
+  :global(.dark) img {
+    -webkit-filter: invert(1); /* safari 6.0 - 9.0 */
+            filter: invert(1);
+  }
+  #themeToggle {
+    background: var(--header-background);
+    display: flex;
+    align-items: center;
+    border-radius: 25px;
+    filter: invert(1);
+    cursor: pointer;
+  }
+</style>
--- a/mrjunejune/pages/parts/headers.html	Wed Dec 31 14:17:40 2025 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-<header>
-	<nav>
-      <h2 class="header-logo">
-        <div id="themeToggle">
-          <img id="epiChan" class="epi-logo" aria-label="Toggle dark mode" src="/public/epi_favicon.svg" height="50" width="50">
-        </div>
-        <a href="/">MrJuneJune</a>
-      </h2>
-		<div class="internal-links">
-			<a href="/resume">Resume</a>
-			<a href="/tools">Tools</a>
-    </div>
-    <div class="social-links">
-			<a href="https://github.com/mrjunejune" target="_blank">
-				<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32"
-					><path
-						fill="currentColor"
-						d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
-					></path></svg>
-			</a>
-		</div>
-	</nav>
-</header>
-<style>
-  :root {
-    --header-background: var(--white);
-    --header-color: rgb(var(--black));
-    --link-hover-accent: var(--awesome);
-  	--social-link-hover: rgb(var(--gray-dark));
-  }
-  .internal-links {
-    width: 33%;
-  }
-  .header-logo {
-    display: flex;
-    align-items: center;
-  }
-  header {
-  	margin: auto;
-  	padding: 0 1em;
-      background: var(--header-background);
-      color: rgb(var(--header-color));
-      font-family: "Atkinson", sans-serif;
-      box-shadow: 0 2px 8px rgba(var(--black), 5%);
-      width: 720px;
-      max-width: calc(100% - 2em);
-
-  }
-  h2 {
-  	margin: 0;
-  	font-size: 1em;
-  }
-  h2 a,
-  h2 a.active {
-  	text-decoration: none;
-  }
-  nav {
-  	display: flex;
-  	align-items: center;
-  	justify-content: space-between;
-  }
-  nav a {
-  	padding: 1em 0.5em;
-  	color: rgb(var(--header-color));
-  	border-bottom: 4px solid transparent;
-  	text-decoration: none;
-  }
-  nav a.active {
-  	text-decoration: none;
-  	border-bottom-color: var(--link-hover-accent);
-  }
-  .social-links,
-  .social-links a {
-    display: flex;
-  }
-  .social-links a {
-  	text-decoration: none;
-  }
-  .social-links a:hover {
-  	color: rgb(var(--social-link-hover));
-  }
-  @media (max-width: 720px) {
-  	.social-links {
-  		display: none;
-  	}
-  .internal-links {
-    width: 45%;
-  }
-  }
-  :global(.dark) img {
-    -webkit-filter: invert(1); /* safari 6.0 - 9.0 */
-            filter: invert(1);
-  }
-  #themeToggle {
-    background: var(--header-background);
-    display: flex;
-    align-items: center;
-    border-radius: 25px;
-    filter: invert(1);
-    cursor: pointer;
-  }
-</style>
-<script>
--- a/mrjunejune/pages/resume/index.html	Wed Dec 31 14:17:40 2025 -0800
+++ b/mrjunejune/pages/resume/index.html	Wed Dec 31 15:07:43 2025 -0800
@@ -1,12 +1,11 @@
 <!doctype html>
 <html lang="en">
   <head>
-    <BaseHead title="Resume" description="June's resume" />
     <link rel="stylesheet" href="base.css" />
     <link rel="stylesheet" href="resume.css" />
   </head>
   <body>
-    <button class="dark-mode-toggle" aria-label="Toggle dark mode">🔄</button>
+    {{/parts/header.html}}
     <main>
       <div class="info">
         <p><span class="header-firstname-style"> JUNTAE </span><span class="header-lastname-style">PARK</span><p>
--- a/mrjunejune/pages/tools/index.html	Wed Dec 31 14:17:40 2025 -0800
+++ b/mrjunejune/pages/tools/index.html	Wed Dec 31 15:07:43 2025 -0800
@@ -6,7 +6,7 @@
     <link rel="stylesheet" href="/tools/index.css" />
   </head>
   <body>
-     <div id="header"></div>
+      {{/parts/header.html}}
      <main>
        <h1 class="title"> Tools </h1>
        <ul class="nav-list">
@@ -14,5 +14,4 @@
        </ul>
      </main>
   </body>
-  <script src="/index.js"></script>
 </html>
--- a/mrjunejune/pages/tools/markdown_to_html/index.html	Wed Dec 31 14:17:40 2025 -0800
+++ b/mrjunejune/pages/tools/markdown_to_html/index.html	Wed Dec 31 15:07:43 2025 -0800
@@ -8,7 +8,7 @@
     <link rel="stylesheet" href="markdown_to_html/index.css" />
 </head>
 <body>
-    <div id="header"></div>
+    {{/parts/header.html}}
     <div class="header">
         <h1>Markdown to HTML Converter</h1>
     </div>
--- a/seobeo/s_router.c	Wed Dec 31 14:17:40 2025 -0800
+++ b/seobeo/s_router.c	Wed Dec 31 15:07:43 2025 -0800
@@ -18,7 +18,7 @@
 
 void Seobeo_Router_Init()
 {
-  g_routes = NULL;
+  Dowa_Array_Reserve(g_routes, 20);
 }
 
 void Seobeo_Router_Register(const char *method, const char *path_pattern, Seobeo_Route_Handler handler)
@@ -144,7 +144,7 @@
   }
 
   // Default text plain
-  const char *content_type = "text/plain";
+  const char *content_type = "text/html";
   void *p_content_type_kv = Dowa_HashMap_Get_Ptr(p_response_map, "content-type");
   if (p_content_type_kv)
   {
--- a/seobeo/s_web.c	Wed Dec 31 14:17:40 2025 -0800
+++ b/seobeo/s_web.c	Wed Dec 31 15:07:43 2025 -0800
@@ -138,13 +138,9 @@
 
   // --- Try to match API route first ---
   Seobeo_Route_Handler handler = Seobeo_Router_Find_Handler(method, path, &p_req_map, p_request_arena);
-
   if (handler != NULL)
   {
-    // Call the API handler
     Seobeo_Request_Entry *p_response_map = handler(p_req_map, p_response_arena);
-
-    // Send the response
     Seobeo_Router_Send_Response(p_cli_handle, p_response_map, p_response_arena);
     goto clean_up;
   }
--- a/seobeo/seobeo.h	Wed Dec 31 14:17:40 2025 -0800
+++ b/seobeo/seobeo.h	Wed Dec 31 15:07:43 2025 -0800
@@ -74,7 +74,7 @@
 extern Seobeo_Route_Handler Seobeo_Router_Find_Handler(const char *method, const char *path, Seobeo_Request_Entry **pp_request_map, Dowa_Arena *p_arena);
 /* Send HTTP response from response map (internal use) */
 extern void           Seobeo_Router_Send_Response(Seobeo_Handle *p_handle, Seobeo_Request_Entry *p_response_map, Dowa_Arena *p_arena);
-extern char          *Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size)
+extern char          *Seobeo_Web_LoadFile(const char *file_path, size_t *p_file_size);
 
 // --- Helper functions --- //
 /* Destroy handle. It will handle all NULL poointers. */