comparison mrjunejune/main.c @ 202:b9b184b3303c

[Notes] Images get processed and it is properly fetched. Thank you.
author MrJuneJune <me@mrjunejune.com>
date Sun, 15 Feb 2026 09:12:57 -0800
parents 6cdee35a7ba9
children e5aed6c36672
comparison
equal deleted inserted replaced
201:6cdee35a7ba9 202:b9b184b3303c
19 int64 media_id; 19 int64 media_id;
20 char s3_key_original[512]; 20 char s3_key_original[512];
21 char s3_key_processed[512]; 21 char s3_key_processed[512];
22 char content_type[128]; 22 char content_type[128];
23 char access_token[256]; 23 char access_token[256];
24 char db_path[256];
24 S3_Config s3_config; 25 S3_Config s3_config;
25 } Media_Processing_Context; 26 } Media_Processing_Context;
26 27
27 // Server configuration (loaded from .config) 28 // Server configuration (loaded from .config)
28 static char g_upload_auth_token[256] = {0}; 29 static char g_upload_auth_token[256] = {0};
666 Seobeo_Render_Html_FilePath(final_body, "/talk/index.html", arena); 667 Seobeo_Render_Html_FilePath(final_body, "/talk/index.html", arena);
667 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena); 668 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
668 return resp; 669 return resp;
669 } 670 }
670 671
671 Seobeo_Request_Entry *GetEditor(Seobeo_Request_Entry *req, Dowa_Arena *arena)
672 {
673 Seobeo_Request_Entry *resp = NULL;
674 char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
675 Seobeo_Render_Html_FilePath(final_body, "/editor/index.html", arena);
676 Dowa_HashMap_Push_Arena(resp, "body", final_body, arena);
677 return resp;
678 }
679
680 Seobeo_Request_Entry *GetNotesLogin(Seobeo_Request_Entry *req, Dowa_Arena *arena) 672 Seobeo_Request_Entry *GetNotesLogin(Seobeo_Request_Entry *req, Dowa_Arena *arena)
681 { 673 {
682 Seobeo_Request_Entry *resp = NULL; 674 Seobeo_Request_Entry *resp = NULL;
683 char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024); 675 char *final_body = Dowa_Arena_Allocate(arena, 50 * 1024);
684 Seobeo_Render_Html_FilePath(final_body, "/notes/login.html", arena); 676 Seobeo_Render_Html_FilePath(final_body, "/notes/login.html", arena);
1266 // Background thread function for media processing 1258 // Background thread function for media processing
1267 void *Media_Process_Background(void *arg) 1259 void *Media_Process_Background(void *arg)
1268 { 1260 {
1269 Media_Processing_Context *ctx = (Media_Processing_Context *)arg; 1261 Media_Processing_Context *ctx = (Media_Processing_Context *)arg;
1270 1262
1263 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Background thread started for media_id=%lld\n", (long long)ctx->media_id);
1264 Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key original: %s\n", ctx->s3_key_original);
1265 Seobeo_Log(SEOBEO_INFO, "[MEDIA] S3 key processed: %s\n", ctx->s3_key_processed);
1266 Seobeo_Log(SEOBEO_INFO, "[MEDIA] DB path: %s\n", ctx->db_path);
1267
1271 // Open thread-local DB connection 1268 // Open thread-local DB connection
1272 Deita_Connection *db_conn = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, g_db_path); 1269 Deita_Connection *db_conn = Deita_Connection_Create(DEITA_DATABASE_TYPE_SQLITE3, ctx->db_path);
1273 if (!db_conn || !Deita_Connection_Is_Open(db_conn)) 1270 if (!db_conn || !Deita_Connection_Is_Open(db_conn))
1274 { 1271 {
1275 printf("[MEDIA] Thread ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id); 1272 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] Thread ERROR: Failed to open database for media_id=%lld\n", (long long)ctx->media_id);
1276 free(ctx); 1273 free(ctx);
1277 return NULL; 1274 return NULL;
1278 } 1275 }
1279 1276
1280 // Update status to 'processing' 1277 // Update status to 'processing'
1281 const char *update_processing = 1278 const char *update_processing =
1282 "UPDATE media_uploads SET status='processing', updated_at=strftime('%s','now') WHERE id=?"; 1279 "UPDATE media_uploads SET status='processing', updated_at=strftime('%s','now') WHERE id=?";
1283 char media_id_str[32]; 1280 char media_id_str[32];
1284 snprintf(media_id_str, sizeof(media_id_str), "%lld", (long long)ctx->media_id); 1281 snprintf(media_id_str, sizeof(media_id_str), "%lld", (long long)ctx->media_id);
1285 const char *params[] = { media_id_str }; 1282 const char *params[] = { media_id_str };
1286 Deita_Query_Execute_Update_Prepared(db_conn, update_processing, 1, params); 1283 int32 update_result = Deita_Query_Execute_Update_Prepared(db_conn, update_processing, 1, params);
1287 1284 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Updated status to 'processing' for media_id=%lld (result=%d)\n", (long long)ctx->media_id, update_result);
1288 printf("[MEDIA] Processing media_id=%lld\n", (long long)ctx->media_id);
1289 1285
1290 // Generate presigned GET URL for download (10 min expiry) 1286 // Generate presigned GET URL for download (10 min expiry)
1287 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generating presigned GET URL for media_id=%lld\n", (long long)ctx->media_id);
1291 S3_Presigned_URL download_url = S3_Presign_Get(&ctx->s3_config, ctx->s3_key_original, 600); 1288 S3_Presigned_URL download_url = S3_Presign_Get(&ctx->s3_config, ctx->s3_key_original, 600);
1292 if (!download_url.success) 1289 if (!download_url.success)
1293 { 1290 {
1291 const char *error_msg = download_url.error_message ? download_url.error_message : "Failed to generate download URL";
1292 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to generate download URL for media_id=%lld: %s\n",
1293 (long long)ctx->media_id, error_msg);
1294 const char *update_error = 1294 const char *update_error =
1295 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?"; 1295 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
1296 const char *error_params[] = { "Failed to generate download URL", media_id_str }; 1296 const char *error_params[] = { error_msg, media_id_str };
1297 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params); 1297 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
1298 printf("[MEDIA] ERROR: Failed to generate download URL for media_id=%lld\n", (long long)ctx->media_id);
1299 S3_Presigned_URL_Destroy(&download_url); 1298 S3_Presigned_URL_Destroy(&download_url);
1300 Deita_Connection_Close(db_conn); 1299 Deita_Connection_Close(db_conn);
1301 free(ctx); 1300 free(ctx);
1302 return NULL; 1301 return NULL;
1303 } 1302 }
1303 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Generated presigned URL: %.100s...\n", download_url.url);
1304 1304
1305 // Generate temp file paths 1305 // Generate temp file paths
1306 char tmp_input[256]; 1306 char tmp_input[256];
1307 char tmp_output[256]; 1307 char tmp_output[256];
1308 char *uuid_input = malloc(UUID_LEN); 1308 char *uuid_input = malloc(UUID_LEN);
1315 snprintf(tmp_output, sizeof(tmp_output), "/tmp/%s.webp", uuid_output); 1315 snprintf(tmp_output, sizeof(tmp_output), "/tmp/%s.webp", uuid_output);
1316 free(uuid_input); 1316 free(uuid_input);
1317 free(uuid_output); 1317 free(uuid_output);
1318 1318
1319 // Download from S3 1319 // Download from S3
1320 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Downloading from S3 to %s for media_id=%lld\n", tmp_input, (long long)ctx->media_id);
1320 Seobeo_Client_Request *download_req = Seobeo_Client_Request_Create(download_url.url); 1321 Seobeo_Client_Request *download_req = Seobeo_Client_Request_Create(download_url.url);
1321 Seobeo_Client_Request_Set_Download_Path(download_req, tmp_input); 1322 Seobeo_Client_Request_Set_Download_Path(download_req, tmp_input);
1322 Seobeo_Client_Response *download_resp = Seobeo_Client_Request_Execute(download_req); 1323 Seobeo_Client_Response *download_resp = Seobeo_Client_Request_Execute(download_req);
1323 1324
1324 S3_Presigned_URL_Destroy(&download_url); 1325 S3_Presigned_URL_Destroy(&download_url);
1325 1326
1326 if (!download_resp || download_resp->status_code != 200) 1327 if (!download_resp || download_resp->status_code != 200)
1327 { 1328 {
1329 int status = download_resp ? download_resp->status_code : 0;
1330 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to download from S3 for media_id=%lld (status=%d)\n",
1331 (long long)ctx->media_id, status);
1328 const char *update_error = 1332 const char *update_error =
1329 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?"; 1333 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
1330 const char *error_params[] = { "Failed to download from S3", media_id_str }; 1334 const char *error_params[] = { "Failed to download from S3", media_id_str };
1331 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params); 1335 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
1332 printf("[MEDIA] ERROR: Failed to download from S3 for media_id=%lld\n", (long long)ctx->media_id);
1333 if (download_req) Seobeo_Client_Request_Destroy(download_req); 1336 if (download_req) Seobeo_Client_Request_Destroy(download_req);
1334 if (download_resp) Seobeo_Client_Response_Destroy(download_resp); 1337 if (download_resp) Seobeo_Client_Response_Destroy(download_resp);
1335 unlink(tmp_input); 1338 unlink(tmp_input);
1336 Deita_Connection_Close(db_conn); 1339 Deita_Connection_Close(db_conn);
1337 free(ctx); 1340 free(ctx);
1338 return NULL; 1341 return NULL;
1339 } 1342 }
1340 1343
1344 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully downloaded file to %s\n", tmp_input);
1341 Seobeo_Client_Request_Destroy(download_req); 1345 Seobeo_Client_Request_Destroy(download_req);
1342 Seobeo_Client_Response_Destroy(download_resp); 1346 Seobeo_Client_Response_Destroy(download_resp);
1343 1347
1344 // Convert to webp using FFmpeg 1348 // Convert to webp using FFmpeg
1345 char cmd[1024]; 1349 char cmd[1024];
1346 char log_file[256]; 1350 char log_file[256];
1347 snprintf(log_file, sizeof(log_file), "/tmp/ffmpeg_%lld.log", (long long)ctx->media_id); 1351 snprintf(log_file, sizeof(log_file), "/tmp/ffmpeg_%lld.log", (long long)ctx->media_id);
1348 snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>%s", 1352 snprintf(cmd, sizeof(cmd), "ffmpeg -y -i %s -quality 80 %s 2>%s",
1349 tmp_input, tmp_output, log_file); 1353 tmp_input, tmp_output, log_file);
1350 1354
1355 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Running FFmpeg: %s\n", cmd);
1351 int ffmpeg_result = system(cmd); 1356 int ffmpeg_result = system(cmd);
1357 Seobeo_Log(SEOBEO_INFO, "[MEDIA] FFmpeg result: %d for media_id=%lld\n", ffmpeg_result, (long long)ctx->media_id);
1358
1352 if (ffmpeg_result != 0) 1359 if (ffmpeg_result != 0)
1353 { 1360 {
1361 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: FFmpeg conversion failed for media_id=%lld (exit code %d). Check log: %s\n",
1362 (long long)ctx->media_id, ffmpeg_result, log_file);
1354 const char *update_error = 1363 const char *update_error =
1355 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?"; 1364 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
1356 const char *error_params[] = { "Image conversion failed", media_id_str }; 1365 const char *error_params[] = { "Image conversion failed", media_id_str };
1357 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params); 1366 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
1358 printf("[MEDIA] ERROR: FFmpeg conversion failed for media_id=%lld\n", (long long)ctx->media_id);
1359 unlink(tmp_input); 1367 unlink(tmp_input);
1360 unlink(tmp_output); 1368 unlink(tmp_output);
1361 Deita_Connection_Close(db_conn); 1369 Deita_Connection_Close(db_conn);
1362 free(ctx); 1370 free(ctx);
1363 return NULL; 1371 return NULL;
1364 } 1372 }
1373 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully converted to webp: %s\n", tmp_output);
1365 1374
1366 // Upload processed file to S3 1375 // Upload processed file to S3
1376 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Uploading processed file to S3: %s -> %s\n", tmp_output, ctx->s3_key_processed);
1367 S3_Result upload_result = S3_Upload_File_With_Content_Type( 1377 S3_Result upload_result = S3_Upload_File_With_Content_Type(
1368 &ctx->s3_config, tmp_output, ctx->s3_key_processed, "image/webp"); 1378 &ctx->s3_config, tmp_output, ctx->s3_key_processed, "image/webp");
1369 1379
1370 if (!upload_result.success) 1380 if (!upload_result.success)
1371 { 1381 {
1382 const char *error_msg = upload_result.error_message ? upload_result.error_message : "Failed to upload processed file";
1383 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to upload processed file for media_id=%lld: %s\n",
1384 (long long)ctx->media_id, error_msg);
1372 const char *update_error = 1385 const char *update_error =
1373 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?"; 1386 "UPDATE media_uploads SET status='error', error_message=?, updated_at=strftime('%s','now') WHERE id=?";
1374 const char *error_params[] = { "Failed to upload processed file", media_id_str }; 1387 const char *error_params[] = { error_msg, media_id_str };
1375 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params); 1388 Deita_Query_Execute_Update_Prepared(db_conn, update_error, 2, error_params);
1376 printf("[MEDIA] ERROR: Failed to upload processed file for media_id=%lld\n", (long long)ctx->media_id);
1377 unlink(tmp_input); 1389 unlink(tmp_input);
1378 unlink(tmp_output); 1390 unlink(tmp_output);
1379 Deita_Connection_Close(db_conn); 1391 Deita_Connection_Close(db_conn);
1380 free(ctx); 1392 free(ctx);
1381 return NULL; 1393 return NULL;
1382 } 1394 }
1383 1395
1396 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully uploaded processed file to S3\n");
1397
1384 // Update status to 'finished' 1398 // Update status to 'finished'
1385 const char *update_finished = 1399 const char *update_finished =
1386 "UPDATE media_uploads SET status='finished', updated_at=strftime('%s','now') WHERE id=?"; 1400 "UPDATE media_uploads SET status='finished', updated_at=strftime('%s','now') WHERE id=?";
1387 Deita_Query_Execute_Update_Prepared(db_conn, update_finished, 1, params); 1401 Deita_Query_Execute_Update_Prepared(db_conn, update_finished, 1, params);
1388 1402
1389 printf("[MEDIA] Successfully processed media_id=%lld\n", (long long)ctx->media_id); 1403 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully processed media_id=%lld - COMPLETE\n", (long long)ctx->media_id);
1390 1404
1391 // Cleanup 1405 // Cleanup
1392 unlink(tmp_input); 1406 unlink(tmp_input);
1393 unlink(tmp_output); 1407 unlink(tmp_output);
1394 Deita_Connection_Close(db_conn); 1408 Deita_Connection_Close(db_conn);
1498 Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); 1512 Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
1499 Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to update status\"}", arena); 1513 Dowa_HashMap_Push_Arena(resp, "body", "{\"error\":\"Failed to update status\"}", arena);
1500 return resp; 1514 return resp;
1501 } 1515 }
1502 1516
1517 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Content type for media_id=%lld: '%s'\n", (long long)media_id, content_type_copy);
1518
1503 // If content_type starts with "image/", spawn background processing thread 1519 // If content_type starts with "image/", spawn background processing thread
1504 if (strncmp(content_type_copy, "image/", 6) == 0) 1520 if (strncmp(content_type_copy, "image/", 6) == 0)
1505 { 1521 {
1522 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Detected image type, preparing to spawn background thread for media_id=%lld\n", (long long)media_id);
1523
1506 // Create context for background thread (heap allocated) 1524 // Create context for background thread (heap allocated)
1507 Media_Processing_Context *ctx = malloc(sizeof(Media_Processing_Context)); 1525 Media_Processing_Context *ctx = malloc(sizeof(Media_Processing_Context));
1508 ctx->media_id = media_id; 1526 ctx->media_id = media_id;
1509 strncpy(ctx->s3_key_original, s3_key_original_copy, sizeof(ctx->s3_key_original) - 1); 1527 strncpy(ctx->s3_key_original, s3_key_original_copy, sizeof(ctx->s3_key_original) - 1);
1510 strncpy(ctx->s3_key_processed, s3_key_processed_copy, sizeof(ctx->s3_key_processed) - 1); 1528 strncpy(ctx->s3_key_processed, s3_key_processed_copy, sizeof(ctx->s3_key_processed) - 1);
1511 strncpy(ctx->content_type, content_type_copy, sizeof(ctx->content_type) - 1); 1529 strncpy(ctx->content_type, content_type_copy, sizeof(ctx->content_type) - 1);
1512 strncpy(ctx->access_token, token, sizeof(ctx->access_token) - 1); 1530 strncpy(ctx->access_token, token, sizeof(ctx->access_token) - 1);
1531 strncpy(ctx->db_path, g_db_path, sizeof(ctx->db_path) - 1);
1513 ctx->s3_key_original[sizeof(ctx->s3_key_original) - 1] = '\0'; 1532 ctx->s3_key_original[sizeof(ctx->s3_key_original) - 1] = '\0';
1514 ctx->s3_key_processed[sizeof(ctx->s3_key_processed) - 1] = '\0'; 1533 ctx->s3_key_processed[sizeof(ctx->s3_key_processed) - 1] = '\0';
1515 ctx->content_type[sizeof(ctx->content_type) - 1] = '\0'; 1534 ctx->content_type[sizeof(ctx->content_type) - 1] = '\0';
1516 ctx->access_token[sizeof(ctx->access_token) - 1] = '\0'; 1535 ctx->access_token[sizeof(ctx->access_token) - 1] = '\0';
1536 ctx->db_path[sizeof(ctx->db_path) - 1] = '\0';
1517 ctx->s3_config = g_s3_config; 1537 ctx->s3_config = g_s3_config;
1538
1539 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Creating pthread for media_id=%lld\n", (long long)media_id);
1518 1540
1519 // Spawn detached thread 1541 // Spawn detached thread
1520 pthread_t thread_id; 1542 pthread_t thread_id;
1521 int thread_result = pthread_create(&thread_id, NULL, Media_Process_Background, ctx); 1543 int thread_result = pthread_create(&thread_id, NULL, Media_Process_Background, ctx);
1522 1544
1523 if (thread_result != 0) 1545 if (thread_result != 0)
1524 { 1546 {
1525 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: Failed to spawn processing thread for media_id=%lld\n", (long long)media_id); 1547 Seobeo_Log(SEOBEO_ERROR, "[MEDIA] ERROR: pthread_create failed with result=%d for media_id=%lld\n", thread_result, (long long)media_id);
1526 free(ctx); 1548 free(ctx);
1527 } 1549 }
1528 else 1550 else
1529 { 1551 {
1530 // Detach thread so it cleans up automatically when done 1552 // Detach thread so it cleans up automatically when done
1531 pthread_detach(thread_id); 1553 pthread_detach(thread_id);
1532 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Spawned processing thread for media_id=%lld\n", (long long)media_id); 1554 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Successfully spawned and detached thread for media_id=%lld\n", (long long)media_id);
1533 } 1555 }
1556 }
1557 else
1558 {
1559 Seobeo_Log(SEOBEO_INFO, "[MEDIA] Non-image file, skipping background processing for media_id=%lld\n", (long long)media_id);
1534 } 1560 }
1535 1561
1536 Dowa_HashMap_Push_Arena(resp, "status", "200", arena); 1562 Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
1537 Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); 1563 Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
1538 Dowa_HashMap_Push_Arena(resp, "body", "{\"success\":true,\"status\":\"uploaded\"}", arena); 1564 Dowa_HashMap_Push_Arena(resp, "body", "{\"success\":true,\"status\":\"uploaded\"}", arena);
1598 1624
1599 const char *media_id_str = ((Seobeo_Request_Entry*)id_kv)->value; 1625 const char *media_id_str = ((Seobeo_Request_Entry*)id_kv)->value;
1600 1626
1601 // Query media status 1627 // Query media status
1602 const char *select_query = 1628 const char *select_query =
1603 "SELECT id, status, s3_key_processed, error_message FROM media_uploads WHERE id = ? AND access_token = ?"; 1629 "SELECT id, status, s3_key_original, s3_key_processed, error_message FROM media_uploads WHERE id = ? AND access_token = ?";
1604 const char *select_params[] = { media_id_str, token }; 1630 const char *select_params[] = { media_id_str, token };
1605 1631
1606 Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, select_params, arena); 1632 Deita_Result_Set *p_result = Deita_Query_Execute_Prepared(g_db_connection, select_query, 2, select_params, arena);
1607 1633
1608 if (!p_result || !Deita_Result_Set_Next(p_result)) 1634 if (!p_result || !Deita_Result_Set_Next(p_result))
1614 return resp; 1640 return resp;
1615 } 1641 }
1616 1642
1617 int64 id = Deita_Result_Set_Get_Integer(p_result, 0); 1643 int64 id = Deita_Result_Set_Get_Integer(p_result, 0);
1618 const char *status = Deita_Result_Set_Get_Text(p_result, 1); 1644 const char *status = Deita_Result_Set_Get_Text(p_result, 1);
1619 const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 2); 1645 const char *s3_key_original = Deita_Result_Set_Get_Text(p_result, 2);
1620 const char *error_message = Deita_Result_Set_Get_Text(p_result, 3); 1646 const char *s3_key_processed = Deita_Result_Set_Get_Text(p_result, 3);
1621 1647 const char *error_message = Deita_Result_Set_Get_Text(p_result, 4);
1622 // Build CloudFront URL if status is 'finished' and s3_key_processed exists 1648
1649 // Build CloudFront URL for processed file if status is 'finished'
1623 char processed_url[1024] = {0}; 1650 char processed_url[1024] = {0};
1624 if (strcmp(status, "finished") == 0 && s3_key_processed && strlen(s3_key_processed) > 0) 1651 if (strcmp(status, "finished") == 0 && s3_key_processed && strlen(s3_key_processed) > 0)
1625 { 1652 {
1626 if (g_s3_cloudfront_url[0]) 1653 if (g_s3_cloudfront_url[0])
1627 { 1654 {
1632 snprintf(processed_url, sizeof(processed_url), "https://%s.s3.%s.amazonaws.com/%s", 1659 snprintf(processed_url, sizeof(processed_url), "https://%s.s3.%s.amazonaws.com/%s",
1633 g_s3_bucket, g_s3_region, s3_key_processed); 1660 g_s3_bucket, g_s3_region, s3_key_processed);
1634 } 1661 }
1635 } 1662 }
1636 1663
1637 // Build JSON response 1664 // Build CloudFront URL for original file (for non-images or before processing completes)
1638 char *response_body = Dowa_Arena_Allocate(arena, 2048); 1665 char original_url[1024] = {0};
1666 if (s3_key_original && strlen(s3_key_original) > 0)
1667 {
1668 if (g_s3_cloudfront_url[0])
1669 {
1670 snprintf(original_url, sizeof(original_url), "%s/%s", g_s3_cloudfront_url, s3_key_original);
1671 }
1672 else
1673 {
1674 snprintf(original_url, sizeof(original_url), "https://%s.s3.%s.amazonaws.com/%s",
1675 g_s3_bucket, g_s3_region, s3_key_original);
1676 }
1677 }
1678
1679 // Build JSON response with both processed_url and original_url
1680 char *response_body = Dowa_Arena_Allocate(arena, 3072);
1681
1682 // Build the base response
1683 int offset = snprintf(response_body, 3072,
1684 "{\"id\":%lld,\"status\":\"%s\",",
1685 (long long)id, status);
1686
1687 // Add processed_url
1639 if (strlen(processed_url) > 0) 1688 if (strlen(processed_url) > 0)
1640 { 1689 {
1641 snprintf(response_body, 2048, 1690 offset += snprintf(response_body + offset, 3072 - offset,
1642 "{\"id\":%lld,\"status\":\"%s\",\"processed_url\":\"%s\",\"error_message\":%s}", 1691 "\"processed_url\":\"%s\",", processed_url);
1643 (long long)id, status, processed_url,
1644 error_message ? "\"" : "null");
1645 if (error_message)
1646 {
1647 // Append error message if exists
1648 size_t len = strlen(response_body);
1649 snprintf(response_body + len - 1, 2048 - len + 1, "%s\"}", error_message);
1650 }
1651 } 1692 }
1652 else 1693 else
1653 { 1694 {
1654 snprintf(response_body, 2048, 1695 offset += snprintf(response_body + offset, 3072 - offset,
1655 "{\"id\":%lld,\"status\":\"%s\",\"processed_url\":null,\"error_message\":%s}", 1696 "\"processed_url\":null,");
1656 (long long)id, status, 1697 }
1657 error_message ? "\"" : "null"); 1698
1658 if (error_message) 1699 // Add original_url
1659 { 1700 if (strlen(original_url) > 0)
1660 size_t len = strlen(response_body); 1701 {
1661 snprintf(response_body + len - 1, 2048 - len + 1, "%s\"}", error_message); 1702 offset += snprintf(response_body + offset, 3072 - offset,
1662 } 1703 "\"original_url\":\"%s\",", original_url);
1704 }
1705 else
1706 {
1707 offset += snprintf(response_body + offset, 3072 - offset,
1708 "\"original_url\":null,");
1709 }
1710
1711 // Add error_message
1712 if (error_message && strlen(error_message) > 0)
1713 {
1714 snprintf(response_body + offset, 3072 - offset,
1715 "\"error_message\":\"%s\"}", error_message);
1716 }
1717 else
1718 {
1719 snprintf(response_body + offset, 3072 - offset,
1720 "\"error_message\":null}");
1663 } 1721 }
1664 1722
1665 Deita_Result_Set_Free(p_result); 1723 Deita_Result_Set_Free(p_result);
1666 1724
1667 Dowa_HashMap_Push_Arena(resp, "status", "200", arena); 1725 Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
1713 g_s3_config.use_path_style = FALSE; 1771 g_s3_config.use_path_style = FALSE;
1714 1772
1715 printf("[S3] Configured: region=%s, bucket=%s, key=%s...\n", 1773 printf("[S3] Configured: region=%s, bucket=%s, key=%s...\n",
1716 g_s3_region, g_s3_bucket, s3_access_key[0] ? "***" : "(missing)"); 1774 g_s3_region, g_s3_bucket, s3_access_key[0] ? "***" : "(missing)");
1717 1775
1776 // Show current working directory
1777 char cwd[1024];
1778 if (getcwd(cwd, sizeof(cwd)) != NULL)
1779 {
1780 printf("[STARTUP] Current working directory: %s\n", cwd);
1781 printf("[STARTUP] Database path (relative): %s\n", g_db_path);
1782 }
1783
1718 // Initialize database 1784 // Initialize database
1719 init_database(); 1785 init_database();
1720 1786
1721 Seobeo_Router_Init(); 1787 Seobeo_Router_Init();
1722 1788
1757 Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog); 1823 Seobeo_Router_Register("GET", "/blog/:blog_id", RenderBlog);
1758 1824
1759 // -- Talk --/ 1825 // -- Talk --/
1760 Seobeo_Router_Register("GET", "/talk", GetTalk); 1826 Seobeo_Router_Register("GET", "/talk", GetTalk);
1761 Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk); 1827 Seobeo_Router_Register("GET", "/talk/index.html", GetRedirectTalk);
1762
1763 // -- Editor (legacy) --/
1764 Seobeo_Router_Register("GET", "/editor", GetEditor);
1765 Seobeo_Router_Register("GET", "/editor/index.html", GetRedirectEditor);
1766 1828
1767 // -- Notes --/ 1829 // -- Notes --/
1768 Seobeo_Router_Register("GET", "/notes", GetNotes); 1830 Seobeo_Router_Register("GET", "/notes", GetNotes);
1769 Seobeo_Router_Register("GET", "/notes/", GetNotes); 1831 Seobeo_Router_Register("GET", "/notes/", GetNotes);
1770 Seobeo_Router_Register("GET", "/notes/index.html", GetNotes); 1832 Seobeo_Router_Register("GET", "/notes/index.html", GetNotes);