From 83f95f86b9785fec53daf4513ab700f3ff87f43c Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 13 Feb 2023 01:39:21 +0100 Subject: [PATCH] backend: fixies --- backend/linux.c | 11 +- backend/main.c | 248 +++++++++++++++---------------- frontend/index.html | 24 --- frontend/src/main.ts | 341 +++++++++++++++++++++++++++++-------------- 4 files changed, 363 insertions(+), 261 deletions(-) diff --git a/backend/linux.c b/backend/linux.c index 368e82b..77b076c 100644 --- a/backend/linux.c +++ b/backend/linux.c @@ -1,9 +1,11 @@ #include "tcp.h" #include +#include #include #include #include #include +#include #include #include #include @@ -36,10 +38,12 @@ TcpServer* tcp_server_create(const char* ip, uint16_t port) server_address.sin_port = htons(port); if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) < 0) { printf("error: tcp: could not bind socket\n"); + close(server_socket); return NULL; } - if (listen(server_socket, SOMAXCONN) < 0) { + if (listen(server_socket, SOMAXCONN - 1) < 0) { printf("error: tcp: could not listen on server\n"); + close(server_socket); return NULL; } @@ -59,12 +63,13 @@ void tcp_server_destroy(TcpServer* server) TcpConnection* tcp_server_accept(TcpServer* server) { - struct sockaddr_in client_address; - socklen_t client_size; + struct sockaddr_in client_address = { 0 }; + socklen_t client_size = 0; int client_socket = accept(server->server_socket, (struct sockaddr*)&client_address, &client_size); if (client_socket < 0) { printf("error: tcp: could not accept connection\n"); + printf("errno: %d %s\n", errno, strerror(errno)); return NULL; } diff --git a/backend/main.c b/backend/main.c index 1512714..86cd7a0 100644 --- a/backend/main.c +++ b/backend/main.c @@ -7,8 +7,6 @@ #include #include -const uint16_t port = 8000; - TcpServer* server = NULL; void interrupt_handler(int a) @@ -20,12 +18,132 @@ void interrupt_handler(int a) exit(1); } +void handle_client_connection(TcpConnection* connection) +{ + + uint8_t buffer[8192] = { 0 }; + ssize_t recieved = tcp_recieve(connection, buffer, 8192); + if (recieved < 0) { + printf("error: could not recieve\n"); + return; + } else if (recieved == 0) { + printf("client disconnected\n"); + return; + } + HttpRequestHeader header = parse_http_request_header((char*)buffer, strlen((char*)buffer)); + char* path = calloc(header.path_length + 1, sizeof(char)); + strncpy(path, (char*)&buffer[header.path_index], header.path_length); + if (strncmp(path, "/api", 4) == 0) { + // something something api + } else { + if (strstr(path, "..") != NULL) { + uint8_t send_buffer[] + = "HTTP/1.1 400 BAD\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "Bad " + "request

Fuck you!

\r\n"; + ssize_t written = tcp_send(connection, send_buffer, sizeof(send_buffer)); + if (written < 0) { + printf("error: could not write\n"); + return; + } + } else if (header.path_length == 0 || strncmp(path, "/", header.path_length) == 0) { + FILE* file = fopen("../frontend/index.html", "r"); + if (file == NULL) { + printf("error: could not open file\n"); + return; + } + uint8_t send_buffer[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html\r\n" + "\r\n"; + ssize_t written = tcp_send(connection, send_buffer, sizeof(send_buffer)); + if (written < 0) { + printf("error: could not write\n"); + return; + } + char char_read; + while ((char_read = (char)fgetc(file)) != EOF) { + tcp_send(connection, (uint8_t*)&char_read, sizeof(char)); + } + } else { + char rootpath[] = "../frontend"; + size_t filepath_size = sizeof(rootpath) + header.path_length + 1; + char* filepath = calloc(filepath_size, sizeof(char)); + snprintf(filepath, filepath_size, "%s%s", rootpath, path); + + char* dot = strrchr(path, '.'); + char mime_type[20] = { 0 }; + char file_flag[3] = { 'r', 0 }; + if (dot != NULL && strncmp(dot, ".html", 5) == 0) { + snprintf(mime_type, 20, "text/html"); + } else if (dot != NULL && strncmp(dot, ".css", 4) == 0) { + snprintf(mime_type, 20, "text/css"); + } else if (dot != NULL && strncmp(dot, ".js", 3) == 0) { + snprintf(mime_type, 20, "text/javascript"); + } else if (dot != NULL && strncmp(dot, ".map", 4) == 0) { + snprintf(mime_type, 20, "application/json"); + } else if (dot != NULL && strncmp(dot, ".ico", 4) == 0) { + snprintf(mime_type, 20, "image/x-icon"); + } else if (dot != NULL && strncmp(dot, ".jpg", 4) == 0) { + snprintf(mime_type, 20, "image/jpeg"); + file_flag[1] = 'b'; + } else if (dot != NULL && strncmp(dot, ".png", 4) == 0) { + snprintf(mime_type, 20, "image/png"); + file_flag[1] = 'b'; + } else if (dot != NULL && strncmp(dot, ".woff2", 6) == 0) { + snprintf(mime_type, 20, "font/woff2"); + file_flag[1] = 'b'; + } else { + printf("error: unknown file type\n"); + return; + } + + FILE* file = fopen(filepath, file_flag); + if (file == NULL) { + printf("error: could not open file\n"); + return; + } + + char send_buffer_1[] = "HTTP/1.1 200 OK\r\n" + "Content-Type: "; + char send_buffer_2[] = "\r\n" + "\r\n"; + ssize_t written = tcp_send(connection, (uint8_t*)send_buffer_1, strlen(send_buffer_1)); + if (written < 0) { + printf("error: could not write\n"); + return; + } + written = tcp_send(connection, (uint8_t*)mime_type, strlen(mime_type)); + if (written < 0) { + printf("error: could not write\n"); + return; + } + written = tcp_send(connection, (uint8_t*)send_buffer_2, strlen(send_buffer_2)); + if (written < 0) { + printf("error: could not write\n"); + return; + } + int char_read; + while ((char_read = fgetc(file)) != EOF) { + tcp_send(connection, (uint8_t*)&char_read, sizeof(char)); + } + + free(filepath); + fclose(file); + } + } + free(path); +} + int main(void) { tcp_global_initialize_sockets(); signal(SIGINT, &interrupt_handler); + const uint16_t port = 8000; + printf("starting server...\n"); server = tcp_server_create("127.0.0.1", port); if (server == NULL) @@ -34,133 +152,15 @@ int main(void) while (true) { printf("waiting for client...\n"); - TcpConnection* client_connection = tcp_server_accept(server); - if (client_connection == NULL) { + TcpConnection* connection = tcp_server_accept(server); + if (connection == NULL) { printf("error: could not accept client\n"); continue; } printf("client connected\n"); - while (true) { - uint8_t buffer[8192] = { 0 }; - ssize_t recieved = tcp_recieve(client_connection, buffer, 8192); - if (recieved < 0) { - printf("error: could not recieve\n"); - break; - } else if (recieved == 0) { - printf("client disconnected\n"); - break; - } - HttpRequestHeader header - = parse_http_request_header((char*)buffer, strlen((char*)buffer)); - char* path = calloc(header.path_length + 1, sizeof(char)); - strncpy(path, (char*)&buffer[header.path_index], header.path_length); - if (strncmp(path, "/api", 4) == 0) { - // something something api - } else { - if (strstr(path, "..") != NULL) { - uint8_t send_buffer[] - = "HTTP/1.1 400 BAD\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "Bad " - "request

Fuck you!

\r\n"; - ssize_t written = tcp_send(client_connection, send_buffer, sizeof(send_buffer)); - if (written < 0) { - printf("error: could not write\n"); - break; - } - } else if (header.path_length == 0 || strncmp(path, "/", header.path_length) == 0) { - FILE* file = fopen("../frontend/index.html", "r"); - if (file == NULL) { - printf("error: could not open file\n"); - break; - } - uint8_t send_buffer[] = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/html\r\n" - "\r\n"; - ssize_t written = tcp_send(client_connection, send_buffer, sizeof(send_buffer)); - if (written < 0) { - printf("error: could not write\n"); - break; - } - char char_read; - while ((char_read = (char)fgetc(file)) != EOF) { - tcp_send(client_connection, (uint8_t*)&char_read, sizeof(char)); - } - } else { - char rootpath[] = "../frontend"; - size_t filepath_size = sizeof(rootpath) + header.path_length + 1; - char* filepath = calloc(filepath_size, sizeof(char)); - snprintf(filepath, filepath_size, "%s%s", rootpath, path); - - char* dot = strrchr(path, '.'); - char mime_type[20] = { 0 }; - char file_flag[3] = { 'r', 0 }; - if (dot != NULL && strncmp(dot, ".html", 5) == 0) { - snprintf(mime_type, 20, "text/html"); - } else if (dot != NULL && strncmp(dot, ".css", 4) == 0) { - snprintf(mime_type, 20, "text/css"); - } else if (dot != NULL && strncmp(dot, ".js", 3) == 0) { - snprintf(mime_type, 20, "text/javascript"); - } else if (dot != NULL && strncmp(dot, ".map", 4) == 0) { - snprintf(mime_type, 20, "application/json"); - } else if (dot != NULL && strncmp(dot, ".ico", 4) == 0) { - snprintf(mime_type, 20, "image/x-icon"); - } else if (dot != NULL && strncmp(dot, ".jpg", 4) == 0) { - snprintf(mime_type, 20, "image/jpeg"); - file_flag[1] = 'b'; - } else if (dot != NULL && strncmp(dot, ".png", 4) == 0) { - snprintf(mime_type, 20, "image/png"); - file_flag[1] = 'b'; - } else if (dot != NULL && strncmp(dot, ".woff2", 6) == 0) { - snprintf(mime_type, 20, "font/woff2"); - file_flag[1] = 'b'; - } else { - printf("error: unknown file type\n"); - break; - } - - FILE* file = fopen(filepath, file_flag); - if (file == NULL) { - printf("error: could not open file\n"); - break; - } - - char send_buffer_1[] = "HTTP/1.1 200 OK\r\n" - "Content-Type: "; - char send_buffer_2[] = "\r\n" - "\r\n"; - ssize_t written = tcp_send( - client_connection, (uint8_t*)send_buffer_1, strlen(send_buffer_1)); - if (written < 0) { - printf("error: could not write\n"); - break; - } - written = tcp_send(client_connection, (uint8_t*)mime_type, strlen(mime_type)); - if (written < 0) { - printf("error: could not write\n"); - break; - } - written = tcp_send( - client_connection, (uint8_t*)send_buffer_2, strlen(send_buffer_2)); - if (written < 0) { - printf("error: could not write\n"); - break; - } - int char_read; - while ((char_read = fgetc(file)) != EOF) { - tcp_send(client_connection, (uint8_t*)&char_read, sizeof(char)); - } - - free(filepath); - fclose(file); - } - } - free(path); - break; - } + handle_client_connection(connection); printf("disconnecting client\n"); - tcp_connection_destroy(client_connection); + tcp_connection_destroy(connection); } tcp_server_destroy(server); return 0; diff --git a/frontend/index.html b/frontend/index.html index e504d7a..1047e98 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -15,29 +15,5 @@ -
-

Postnummer App

-
- - -
-
- - -
-
-
-

Postnummer ikke fundet

-

-

-
-
-
diff --git a/frontend/src/main.ts b/frontend/src/main.ts index f0e0ac5..5adcd1e 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -120,138 +120,259 @@ function displayZipCode( boundaryElem.style.height = `${bottomleft.y - topright.y}px`; } -function setupMap( - mousePositionElement: HTMLParagraphElement, - coordsElement: HTMLParagraphElement, - zipCodeElement: HTMLParagraphElement, -) { - const mapImg = domSelect("#map")!; - const fetcher = new Throttler(200); +type Component = { + children?: Component[], + render(): string, + hydrate?(update: (action?: () => Promise) => Promise): Promise, +}; - mapImg.addEventListener('mousemove', async (event: MouseEvent) => { - const mousePosition: Position = { x: event.offsetX, y: event.offsetY }; - displayMousePosition(mousePositionElement, mousePosition); - - const mapSize: Size = { - width: mapImg.clientWidth, - height: mapImg.clientHeight, - }; - - const coords = convertPixelsToCoordinate(mousePosition, mapSize); - displayCoords(coordsElement, coords); - - fetcher.call(async () => await fetchAndDisplayZipCode(coords)); - }); - - mapImg.addEventListener("mouseup", async (event: MouseEvent) => { - const mousePosition: Position = { x: event.offsetX, y: event.offsetY }; - displayMousePosition(mousePositionElement, mousePosition); - - const mapSize: Size = { - width: mapImg.clientWidth, - height: mapImg.clientHeight, - }; - - const coords = convertPixelsToCoordinate(mousePosition, mapSize); - displayCoords(coordsElement, coords); - - fetcher.call(async () => await fetchAndDisplayZipCode(coords)); - }); - - mapImg.addEventListener("mouseleave", (_event: MouseEvent) => { - displayZipCode(zipCodeElement, null, null, null, null); - }); +function makeReviewsPage(): Component { + return { + render: () => /*html*/` +

Anmeldelser

+
${loadReviews()}
+ `, + } } -function setupSearchBar(zipCodeElement: HTMLParagraphElement) { - const searchBar = - domSelect("#search-bar")!; - const searchInput = - domSelect("#search-input")!; +function makeSearchBar(updateZipCodeInfo: (zipCode: string) => Promise): Component { + return { + render: () => /*html*/` + + `, + async hydrate(update) { + const searchBar = + domSelect("#search-bar")!; + const searchInput = + domSelect("#search-input")!; - searchInput.addEventListener("keydown", (event) => { - if (event.key.length === 1 && !"0123456789".includes(event.key)) - event.preventDefault(); - }); + searchInput.addEventListener("keydown", (event) => { + if (event.key.length === 1 && !"0123456789".includes(event.key)) + event.preventDefault(); + }); - searchBar.addEventListener("submit", async (event: Event) => { - event.preventDefault(); + searchBar.addEventListener("submit", async (event: Event) => { + event.preventDefault(); + update(() => updateZipCodeInfo(searchInput.value)); + }); + }, + }; +} - const inputValue = searchInput.value; +type ZipCodeInfo = { + zipCode: number | null, + name: string | null, + center: Coordinate | null, + boundary: Square | null, +} + +function makeInfoBox(info: ZipCodeInfo): Component { + const { zipCode, name, center, boundary } = info; + return { + render: () => /*html*/` +
+

${info.zipCode === null ? "Postnummer ikke fundet" : `Postnummer: ${zipCode}, ${name}`}

+

+

+
+ `, + async hydrate() { + tooltip.setText(zipCode ? `${zipCode} ${name}` : ""); + + const dot = document.getElementById("dot")!; + const boundaryElem = document.getElementById("boundary")!; + + if (!center || !boundary) { + dot.style.display = "none"; + boundaryElem.style.display = "none"; + return; + } + + const mapImg = document.getElementById("map")!; + const mapSize: Size = { + width: mapImg.clientWidth, + height: mapImg.clientHeight, + }; + + // Draw dot + const position = convertCoordinateToPixels(center, mapSize); + const rect = document.getElementById("map")!.getBoundingClientRect(); + dot.style.display = "block"; + dot.style.left = `${position.x + rect.left}px`; + dot.style.top = `${position.y + rect.top + document.documentElement.scrollTop}px`; + + // Draw boundary + const bottomleft = convertCoordinateToPixels({ longitude: boundary.x1, latitude: boundary.y1 }, mapSize); + const topright = convertCoordinateToPixels({ longitude: boundary.x2, latitude: boundary.y2 }, mapSize); + + boundaryElem.style.display = "block"; + boundaryElem.style.left = `${bottomleft.x + rect.left}px`; + boundaryElem.style.top = `${topright.y + rect.top + document.documentElement.scrollTop}px`; + boundaryElem.style.width = `${topright.x - bottomleft.x}px`; + boundaryElem.style.height = `${bottomleft.y - topright.y}px`; + } + } +} + + +function makeMainPage(): Component { + const zipCodeInfo: ZipCodeInfo = { + zipCode: null, + name: null, + center: null, + boundary: null, + } + const searchBar = makeSearchBar(async (searchInputValue) => { + const inputValue = searchInputValue; if (!/^\d+$/.test(inputValue)) return; const data = await fetch( `https://api.dataforsyningen.dk/postnumre?nr=${inputValue}`, ).then((response) => response.json()) - displayZipCode( - zipCodeElement, - data.length ? parseInt(data[0]["nr"]) : null, - data.length ? data[0]["navn"] : null, - data.length ? { longitude: data[0]["visueltcenter"][0], latitude: data[0]["visueltcenter"][1] } : null, - data.length ? { x1: data[0]["bbox"][0], y1: data[0]["bbox"][1], x2: data[0]["bbox"][2], y2: data[0]["bbox"][3] } : null, - ); - + zipCodeInfo.zipCode = data.length ? parseInt(data[0]["nr"]) : null; + zipCodeInfo.name = data.length ? data[0]["navn"] : null; + zipCodeInfo.center = data.length ? { longitude: data[0]["visueltcenter"][0], latitude: data[0]["visueltcenter"][1] } : null; + zipCodeInfo.boundary = data.length ? { x1: data[0]["bbox"][0], y1: data[0]["bbox"][1], x2: data[0]["bbox"][2], y2: data[0]["bbox"][3] } : null; }); -} - -function pageRedirects() { - const reviewRedirect = document.getElementById("reviews-redirect")! - const mapRedirect = document.getElementById("map-redirect")! - const mainElement = document.getElementById("main")! - - - reviewRedirect.addEventListener("click", () => { - mainElement.innerHTML = /*html*/` -

Anmeldelser

-
${loadReviews()}
- `; - const dropdown = document.getElementById("dropdown")!; - dropdown.classList.remove("enabled"); - - }); - - mapRedirect.addEventListener("click", () => { - mainElement.innerHTML = /*html*/` - + const infoBox = makeInfoBox(zipCodeInfo); + return { + children: [searchBar, infoBox], + render: () => /*html*/` + ${searchBar.render()}
-
-

Postnummer ikke fundet

-

-

-
- `; - const [mousePositionElement, coordsElement, zipCodeElement] = [ - "#mouse-position", - "#coords", - "#zip-code", - ].map((id) => domSelect(id)!); - setupSearchBar(zipCodeElement); - setupMap(mousePositionElement, coordsElement, zipCodeElement); - const dropdown = document.getElementById("dropdown")!; - dropdown.classList.remove("enabled"); + `, + async hydrate() { + const mousePositionElement = domSelect("#mouse-position")!; + const coordsElement = domSelect("#coords")!; + const zipCodeElement = domSelect("#zip-code")!; + const mapImg = domSelect("#map")!; + const fetcher = new Throttler(200); - }) + mapImg.addEventListener('mousemove', async (event: MouseEvent) => { + const mousePosition: Position = { x: event.offsetX, y: event.offsetY }; + displayMousePosition(mousePositionElement, mousePosition); + + const mapSize: Size = { + width: mapImg.clientWidth, + height: mapImg.clientHeight, + }; + + const coords = convertPixelsToCoordinate(mousePosition, mapSize); + displayCoords(coordsElement, coords); + + fetcher.call(async () => await fetchAndDisplayZipCode(coords)); + }); + + mapImg.addEventListener("mouseup", async (event: MouseEvent) => { + const mousePosition: Position = { x: event.offsetX, y: event.offsetY }; + displayMousePosition(mousePositionElement, mousePosition); + + const mapSize: Size = { + width: mapImg.clientWidth, + height: mapImg.clientHeight, + }; + + const coords = convertPixelsToCoordinate(mousePosition, mapSize); + displayCoords(coordsElement, coords); + + fetcher.call(async () => await fetchAndDisplayZipCode(coords)); + }); + + mapImg.addEventListener("mouseleave", (_event: MouseEvent) => { + displayZipCode(zipCodeElement, null, null, null, null); + }); + }, + }; +} + +function makeRouter(route: "main" | "reviews"): Component { + switch (route) { + case "main": + { + const mainPage = makeMainPage(); + return { + children: [mainPage], + render: () => mainPage.render(), + }; + } + case "reviews": { + const reviewsPage = makeReviewsPage(); + return { + children: [reviewsPage], + render: () => reviewsPage.render(), + } + } + } +} + +function makeTooltip(): Component { + return { + render: () => /*html*/` +
+ `, + } +} + +function makeLayout(): Component { + let route: "main" | "reviews" = "main"; + const router = makeRouter(route); + const tooltip = makeTooltip(); + return { + children: [router, tooltip], + render: () => /**/` +
+

Postnummer App

+
+ + +
+
+ ${router.render()} +
+ ${tooltip.render()} + `, + async hydrate(update) { + setTopbarOffset(); + addToggleDropdownListener(); + document.getElementById("reviews-redirect")!.addEventListener('click', () => { + update(async () => route = "reviews"); + }) + document.getElementById("map-redirect")!.addEventListener('click', () => { + update(async () => route = "main"); + }) + }, + } +} + +function hydrateComponentAndChildren(component: Component, update: (action?: () => Promise) => Promise) { + component.hydrate && component.hydrate(update); + component.children?.forEach((child) => hydrateComponentAndChildren(child, update)); +} + +function renderComponent(element: HTMLElement, component: Component) { + element.innerHTML = component.render(); + window.requestAnimationFrame(() => { + hydrateComponentAndChildren(component, async (action) => { + action && await action(); + element.innerHTML = component.render(); + }); + }); } function main() { if (navigator.userAgent.match("Chrome")) { location.href = "https://mozilla.org/firefox"; } - - const mousePositionElement = domSelect("#mouse-position")!; - const coordsElement = domSelect("#coords")!; - const zipCodeElement = domSelect("#zip-code")!; - - setupSearchBar(zipCodeElement); - setupMap(mousePositionElement, coordsElement, zipCodeElement); - setTopbarOffset(); - addToggleDropdownListener(); - pageRedirects(); + const bodyElement = document.querySelector("body")!; + renderComponent(bodyElement, makeLayout()); } main();