From 005b9cd41d8240ca8bf7c60fcc4c7cbcf6f7ad5f Mon Sep 17 00:00:00 2001 From: Adrien Date: Sun, 21 Apr 2024 13:59:43 +0200 Subject: [PATCH] Don't work --- Simplicity.drawio.png | Bin 0 -> 22224 bytes Simplicity2.drawio.png | Bin 0 -> 20688 bytes Simplicity3.drawio.png | Bin 0 -> 28005 bytes article.md | 179 +++++++++++++++++- go.mod | 1 + go.sum | 2 + layouts/base.html | 61 ++++++ layouts/chat.html | 58 ++++++ .../welcome_page.html => layouts/welcome.html | 6 +- main.go | 31 ++- templates/base.html | 25 --- templates/chat/messages.html | 31 --- templates/chat/page.html | 33 ---- templates/navbar.html | 19 -- 14 files changed, 310 insertions(+), 136 deletions(-) create mode 100644 Simplicity.drawio.png create mode 100644 Simplicity2.drawio.png create mode 100644 Simplicity3.drawio.png create mode 100644 layouts/base.html create mode 100644 layouts/chat.html rename templates/welcome_page.html => layouts/welcome.html (77%) delete mode 100644 templates/base.html delete mode 100644 templates/chat/messages.html delete mode 100644 templates/chat/page.html delete mode 100644 templates/navbar.html diff --git a/Simplicity.drawio.png b/Simplicity.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..55ed97c22c5333c7d2d73cb2a1d0fb9681f5de9b GIT binary patch literal 22224 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 Vrtpph{1>$l83_gPLQ#Fc{|BGueO>?n literal 0 HcmV?d00001 diff --git a/Simplicity2.drawio.png b/Simplicity2.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..7dec4697579683d360deb9d5335f3b9a493fb736 GIT binary patch literal 20688 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%0AoV_@1 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 literal 0 HcmV?d00001 diff --git a/article.md b/article.md index e114623..b910517 100644 --- a/article.md +++ b/article.md @@ -29,7 +29,7 @@ Another huhg advantages of using HTMX instead of JS is that you can use all HTML The core idea being that the server send all the browser need to know t odisplay the app. It doesn't need to understand what the app is or do, all of that is either in the HTML, in our head or in the server. When I click on a button to remove something, the client don't understand that I want to remove something. It just understand that I want to change this part. And if the change happend to be an empty HTML, well it's been "removed". -Compare to the API phylosophy. The server send just the data, but then the server need a function that understand and interpret the data. +Compare to the API phylosophy where the server send just the data, but then the server need a function that understand and interpret the data. For example I want to display a username and an email. If my API send this: ```html @@ -48,18 +48,19 @@ Now let's see the current approche. Usuqlly server send JSON to the client and t But with that, you don't know how to display it. You need a new JS function to pass from one to another. -Why ? I already did the logic server side of getting the data, ect. Why do I need to split some part in JS and do some part twice ? +Why ? I already did the logic server side of getting the data, ect. Why do I need to split some part in JS and do some shit twice ? ## part 0 - table At my work, I was task to implement an app. The app is pretty simple. You have a table that is a list of test done on a database. You can see the number of errors. For each test there is a button to see the detail. -Let's take an easy example, I have a list of client, I check if any have more than 5 addresses. If so, when I click on the +, I get a new table that list all client with more than 5 addresses. And there is multiple test like that. +To understand here a test example: I have a list of client, I check if any have more than 5 addresses. If so, when I click on the +, I get a new table that list all client with more than 5 addresses. And there is multiple test like that. Pretty simple right ? Well no, obviously. -Man, I stuggeled so hard to just add one + button, and then if I do that, it change that, and that and blablabla and it lag and it's not center. God damm, that why I don't wanted JS, I don't know JS. I did a script to exclude a row of the second table and it would react... What a mistake, I think I touched the ultimate use case here, how easy it is in HTMX compare to the other way is just incridible. And now the ultimate roast for JS. Yes JS on client side is in theory more efficient... in theory. But I don't know what I'm am doing here! chatGPT gave me this and it work but god, I use it for python and sometime it give me somecrazy shit. Just to say that it is if you know what you are doing. Way better to stick to the language you know, here you can do someting opti, here you understand. +Man, I stuggeled so hard to just add one + button for the details. And then if I do that, it change that, and that and blablabla and it lag and it's not center. God damm, that why I don't wanted JS, I don't know JS. I did a script to exclude a row of the second table and it would react... What a mistake, I think I touched the ultimate use case here, how easy it is in HTMX compare to the other way is just incridible. +And now the ultimate roast for JS. Yes JS on client side is in theory more efficient... in theory. But I don't know what I'm am doing here! chatGPT gave me this and it work but god, I use it for python and sometime it give me somecrazy shit. Just to say that it is if you know what you are doing. Way better to stick to the language you know, here you can do someting opti, here you understand. -Let's go into a bit of code. So I have my Flask app running +Let's go into a bit of code. So I have my Flask app running (I use Flask for my work) ```python from flask import Flask, render_template, request @@ -174,7 +175,7 @@ And that's it for this first demo/intro. We were able to do some crazy stuff in I was able to learn and do the app it few days while doing other stuff and managing issue on the dev virtual machine. The shit was really impressive, and my boss is really similare to me in the way he don"t want to learn JS. He was really impressed. -So after that, I was convinced. Let's do JADE 2.0 using the Hyper PyFleX stack. +So after that, I was convinced. Let's do JADE 2.0 using HTMX. ## Why GO ? @@ -183,13 +184,48 @@ I also realise how I want my app to work and it's simple. remember simplicity bo So how it work. Every time I do something in the chat. I recreate it. Let's take an example, I want to add a message to the chat: -User trigger --> +User trigger and send form with message content --> Server get form content and add a message to the database --> Run main generateChatHTML (Read DB -> Generate HTML) -> Send to the client to update just the chat + +The goal is once again simplicity. All data are at one place, the database, where it bellow. If I need it, I read the database. There is no buffer, no global messages array that keep track of mesages in GO, and not at all in both in GO and in JS on the client. Then I need to be sure that they are identical, too much trubble, why would I do that ? +Idk how stupid it is to use this for big app in prod, but worse case you can do a compose and add a mongodb buffer and only receive once and save once when leaving. + +So here a simple diagramm to understand: + +![alt text](Simplicity.drawio.png) + +Compare to the more traditional approche: + +![alt text](Simplicity2.drawio.png) + +Or even worse, with JS + +![alt text](Simplicity3.drawio.png) + +Yep, this is how I see it. WTF is wrong with you peoples? 3 loops ????? 3???? I meqn 2 OOOKKK, I guesssssssss. But 3???? +So If I want to update something in my app, I first need to process the data in JS and updatte what need to be in the JS client then send only the necessarry data through an API in a specific format and have a specific function that receive this specific data and do some stuff on the server, change some stuff and then send the data to the database. Ok we update one thing! +Don't get me wrong, I see the point of using JS. This is not a loop, the data never comeback from the databse to the client! The server never send anything. Meaning it never do the server to client part that HTMX do. So you do transfert more data for similare optimization level for the same thing but, is it better optimizing that if I get a 500mb RAM client ? Depend the app, no in a lot of scenario I beleive. + +Note that in the first one, there is 2 data box. Meaning 2 "states of data". This is the key difference. In the second one, the server is responsible for keeping track of the data as it stay inside it and just send the necessary data to the databse. It is efficient, that is for sure, but the server AND the databse need to keep track of the same thing, that is redondant. + +So after chossing to go with this design, as it is basically the same logic I use in streamlit with st.rerun(). I needed a server efficient, powerful and fast !!! + +*Process to look at python code* + +Yeah..... +I like you python ,that is for sure. But maybe I need someone else on this job. So I alredy had some eyes on GO and was looking for a good project to use it. I saw other framework and language like Rust, ect. I was thinking about mojo at some point, but it is not mature enough at this point. But could of been a good use case for it. + +So GO it is, I did the good old google "Go web framwork" and found a good list, tested with Gin then went with Fiber for the SPEEDDDDDDDDDDDDDDDDDDDDDDDDD!!!!!!!!! YOOOOOOOOOOOLLLLLLLOOOOOOOO!!!!!!!! + +No seriously, had to chose, looked really similare. Tryed Fiber and found it easy to use and powerful so went with it. ## Used teck So here my full teck stack - GO with Fiber - HTMX - MongoDB +- Bulma + +Done ## part 1 - The chat @@ -199,5 +235,134 @@ Ok so as a first thing is a chat. What I need is: Should be easy right ? Hopefully with HTMX yes. Let's try... +Well it do was pretty easy. I first did a base.html and then added an navbar and a page.html a bit like that: +```html +{% extends 'base.html' %} + +{% block content %} +
+
+
+
+ +
+ +
+ + + +{% endblock %} +``` + +So in this HTML, I have a columns with one column empty for now and a form with an input. I also created a small style so the text are on the right or left of the colum depending of if user of bot and that's it. + +Now in this HTML, I use HTMX at 2 places, the column and the form. Let's take a look at the column first: +- `hx-trigger="load"` mean *when the page load* +- `hx-get="/chat/messages"` *get the HTML here* +- `hx-swap="outerHTML"` *to replace yourself with the response* + +The HTML at /chat/messages look like: +```html +
+ {% for message in messages %} + {% if message.Role == 'user' %} +
+
+

User

+
+ + +
+
+
+ {{ message.Content }} +
+
+ {% else %} +
+
+

Bot

+
+ + +
+
+
+ {{ message.Content }} +
+
+ {% endif %} + {% endfor %} +
+``` + +So it create itself back with `
` and then do a for loop on the messages that I extracted from the Database on the server side. + +Now let's take a look at the `form`: +- `hx-posts="/chat"` mean *get the HTML here* +- `hx-swap="outerHTML"` *to replace* +- `hx-target="#messages"` *this with the response* + +The server function at POST /chat will add the message in the databse then return the same as /chat/messages. So if you understand, the function at /chat/messages is the second "Server" box on my diagram. And it's always the same function. Once again simplicity. Out of 4 boxs, only one change basically, the first "Server". Meaning the function that change the database. + +And yeah, that's it. A bit of backend but just this: +```go +func addMessageHandler(c *fiber.Ctx) error { + message := c.FormValue("message") + + collection := mongoClient.Database("chat").Collection("messages") + collection.InsertOne(context.Background(), bson.M{"message": message, "role": "user", "date": time.Now()}) + collection.InsertOne(context.Background(), bson.M{"message": "I did something!", "role": "bot", "date": time.Now()}) + + return generateChatHTML(c) +} + +func generateChatHTML(c *fiber.Ctx) error { + collection := mongoClient.Database("chat").Collection("messages") + + // Get all messages + cursor, _ := collection.Find(context.TODO(), bson.D{}) + + // Convert the cursor to an array of messages + var messages []Message + if err = cursor.All(context.TODO(), &messages); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to convert cursor to array", + }) + } + + // Render the HTML template with the messages + return c.Render("chat/messages", fiber.Map{ + "messages": messages, + }) +} +``` + +And that' it! The chat work like that! Obviously I need to add the model api stuff with it but otherwise... done. I just did an ineractive chat in what ? 100 lines of codes ? And in an easy to understand, share and maintain way ? Isn't it beautifull? + +## part 2 - The API + +Ok so now that we have a chat. I need to be able to communicate with the different API. I'm a bit affray on this one, I hope I don't NEED to use python. Now that I think about, I always used the OpenAI Client, because easy to use. Let's see if I can talk with OpeanAI server through Go first... + + + # Conclusion - Say a lot about JS during the article. JS is great, I guess, I didn't try. Every language have strenght weakness, blablabla. The point is that it shouln't be a necessity to learn it. And it shouldn't be so difficult to do basic stuff. \ No newline at end of file diff --git a/go.mod b/go.mod index c61f486..4060a90 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/gofiber/fiber/v2 v2.52.4 // indirect github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/template/django/v3 v3.1.11 // indirect + github.com/gofiber/template/html/v2 v2.1.1 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/golang/snappy v0.0.1 // indirect github.com/google/uuid v1.5.0 // indirect diff --git a/go.sum b/go.sum index 6f1a44f..a2ffc18 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZx github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= github.com/gofiber/template/django/v3 v3.1.11 h1:wE5k/wWNKGKxfeopaeB6IBijMiEVAxKHJVf1WMH5iNw= github.com/gofiber/template/django/v3 v3.1.11/go.mod h1:sEUp0cr1iCuFx4GEtHEA7yRXgJmRdAVXwGMR3Q5JnyI= +github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8= +github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0= github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= diff --git a/layouts/base.html b/layouts/base.html new file mode 100644 index 0000000..0fcd437 --- /dev/null +++ b/layouts/base.html @@ -0,0 +1,61 @@ + + + + + + + JADE 2.0 + + + + + {{ template "navbar" . }} + + {{template "content" .}} + + + + + + + + +{{define "navbar"}} + +{{end}} \ No newline at end of file diff --git a/layouts/chat.html b/layouts/chat.html new file mode 100644 index 0000000..aba06b6 --- /dev/null +++ b/layouts/chat.html @@ -0,0 +1,58 @@ +{{define "user-message"}} +
+
+

User

+
+ + +
+
+
+ {{ .Content }} +
+
+{{end}} + +{{define "bot-message"}} +
+
+

Bot

+
+ + +
+
+
+ {{ .Content }} +
+
+{{end}} + +{{define "chat-input"}} +
+ +
+{{end}} + +{{define "chat-messages"}} +
+
+ {{range .Messages}} + {{if eq .Role "user"}} + {{template "user-message" .Message}} + {{else}} + {{template "bot-message" .Message}} + {{end}} + {{end}} +
+
+{{end}} + + +{{ define "content" }} +
+

Chat Page

+ {{template "chat-messages" .Messages}} + {{template "chat-input"}} +
+{{ end }} \ No newline at end of file diff --git a/templates/welcome_page.html b/layouts/welcome.html similarity index 77% rename from templates/welcome_page.html rename to layouts/welcome.html index 3f62dfa..80ae790 100644 --- a/templates/welcome_page.html +++ b/layouts/welcome.html @@ -1,11 +1,9 @@ -{% extends 'base.html' %} +{{define "content"}} -{% block content %}

Welcome to JADE 2.0!

- -{% endblock %} \ No newline at end of file +{{end}} \ No newline at end of file diff --git a/main.go b/main.go index 5769651..d105de1 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,11 @@ package main import ( "context" - "log" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/template/django/v3" + "github.com/gofiber/template/html/v2" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" @@ -16,6 +15,11 @@ import ( var mongoClient *mongo.Client +type User struct { + Username string + Email string +} + type Message struct { ID primitive.ObjectID `bson:"_id"` Content string `bson:"message"` @@ -43,10 +47,8 @@ func connectToMongoDB(uri string) { } func main() { - // Import HTML using django engine/template - engine := django.New("./templates", ".html") - // Create new Fiber instance. Can use any framework. I use fiber for speed and simplicity + engine := html.New("./layouts", ".html") app := fiber.New(fiber.Config{ Views: engine, AppName: "JADE 2.0", @@ -66,20 +68,20 @@ func main() { app.Get("/", indexHandler) app.Get("/isMongoDBConnected", isMongoDBConnectedHandler) - app.Get("/chat", chatPageHandler) - app.Post("/chat", addMessageHandler) - app.Delete("/chat", deleteMessageHandler) + app.Get("/chat", chatPageHandler) // Complete chat page + app.Put("/chat", addMessageHandler) // Add message + app.Delete("/chat", deleteMessageHandler) // Delete message // Start server app.Listen(":3000") } func indexHandler(c *fiber.Ctx) error { - return c.Render("welcome_page", fiber.Map{}) + return c.Render("base", fiber.Map{}, "welcome") } func chatPageHandler(c *fiber.Ctx) error { - return c.Render("chat/page", fiber.Map{}) + return c.Render("base", nil, "chat") } func addMessageHandler(c *fiber.Ctx) error { @@ -135,7 +137,7 @@ func deleteMessageHandler(c *fiber.Ctx) error { }) } - return c.SendString("") // TODO Main loog that return the all chat + return generateChatHTML(c) } func generateChatHTML(c *fiber.Ctx) error { @@ -158,13 +160,8 @@ func generateChatHTML(c *fiber.Ctx) error { }) } - // Print messages - for _, message := range messages { - log.Println(message) - } - // Render the HTML template with the messages - return c.Render("chat/messages", fiber.Map{ + return c.Render("chat", fiber.Map{ "messages": messages, }) } diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index 41916d4..0000000 --- a/templates/base.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - JADE 2.0 - - - - - - {% include 'navbar.html' %} -
-
-
-
- {% block content %}{% endblock %} -
-
-
-
- - - \ No newline at end of file diff --git a/templates/chat/messages.html b/templates/chat/messages.html deleted file mode 100644 index ec987e9..0000000 --- a/templates/chat/messages.html +++ /dev/null @@ -1,31 +0,0 @@ -
- {% for message in messages %} - {% if message.Role == 'user' %} -
-
-

User

-
- - -
-
-
- {{ message.Content }} -
-
- {% else %} -
-
-

Bot

-
- - -
-
-
- {{ message.Content }} -
-
- {% endif %} - {% endfor %} -
\ No newline at end of file diff --git a/templates/chat/page.html b/templates/chat/page.html deleted file mode 100644 index 4305b23..0000000 --- a/templates/chat/page.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends 'base.html' %} - -{% block content %} -
-
-
-
- -
- -
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/navbar.html b/templates/navbar.html deleted file mode 100644 index d45c065..0000000 --- a/templates/navbar.html +++ /dev/null @@ -1,19 +0,0 @@ - \ No newline at end of file