235 lines
6.1 KiB
Go
235 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/flosch/pongo2"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/fiber/v2/middleware/logger"
|
|
"github.com/gofiber/template/django/v3"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var mongoClient *mongo.Client
|
|
var userTmpl *pongo2.Template
|
|
var botTmpl *pongo2.Template
|
|
|
|
type Message struct {
|
|
ID primitive.ObjectID `bson:"_id"`
|
|
Content string `bson:"message"`
|
|
Role string `bson:"role"`
|
|
Date time.Time `bson:"date"`
|
|
}
|
|
|
|
type Conversation struct {
|
|
ID string
|
|
Messages []Message
|
|
}
|
|
|
|
type User struct {
|
|
ID string `bson:"_id"`
|
|
Username string `bson:"username"`
|
|
OAth2Token string `bson:"oauth2token"`
|
|
IsSub bool `bson:"isSub"`
|
|
}
|
|
|
|
type CurrentSession struct {
|
|
ID string
|
|
CurrentConversationID string
|
|
CurrentUserID string
|
|
}
|
|
|
|
func connectToMongoDB(uri string) {
|
|
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
|
|
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
|
|
client, err := mongo.Connect(context.TODO(), opts)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
mongoClient = client
|
|
|
|
if err := mongoClient.Ping(context.TODO(), nil); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
botTmpl = pongo2.Must(pongo2.FromFile("views/partials/message-bot.html"))
|
|
userTmpl = pongo2.Must(pongo2.FromFile("views/partials/message-user.html"))
|
|
|
|
// Import HTML using django engine/template
|
|
engine := django.New("./views", ".html")
|
|
|
|
if engine == nil {
|
|
panic("Failed to create django engine")
|
|
}
|
|
|
|
// Create new Fiber instance. Can use any framework. I use fiber for speed and simplicity
|
|
app := fiber.New(fiber.Config{
|
|
Views: engine,
|
|
AppName: "JADE 2.0",
|
|
EnablePrintRoutes: true,
|
|
})
|
|
|
|
// Initialize
|
|
go connectToMongoDB("mongodb://localhost:27017")
|
|
|
|
// Add default logger
|
|
app.Use(logger.New())
|
|
|
|
// Add static files
|
|
app.Static("/", "./static")
|
|
|
|
// Add routes
|
|
app.Get("/", indexHandler)
|
|
app.Get("/isMongoDBConnected", isMongoDBConnectedHandler)
|
|
|
|
app.Get("/chat", chatPageHandler) // Complete chat page
|
|
app.Put("/chat", addMessageHandler) // Add message
|
|
app.Delete("/chat", deleteMessageHandler) // Delete message
|
|
app.Get("/loadChat", generateChatHTML) // Load chat
|
|
|
|
app.Get("/generateOpenai", addOpenaiMessage)
|
|
|
|
app.Get("/test-button", testButtonHandler)
|
|
|
|
// Start server
|
|
app.Listen(":3000")
|
|
}
|
|
|
|
func indexHandler(c *fiber.Ctx) error {
|
|
return c.Render("welcome", fiber.Map{}, "layouts/main")
|
|
}
|
|
|
|
func chatPageHandler(c *fiber.Ctx) error {
|
|
return c.Render("chat", fiber.Map{
|
|
"Messages": []Message{},
|
|
}, "layouts/main")
|
|
}
|
|
|
|
func testButtonHandler(c *fiber.Ctx) error {
|
|
return c.Render("partials/test-button", fiber.Map{})
|
|
}
|
|
|
|
func addMessageHandler(c *fiber.Ctx) error {
|
|
message := c.FormValue("message")
|
|
lastMessageAsked = message
|
|
|
|
// Add bot message if there is no error
|
|
userOut, err := userTmpl.Execute(pongo2.Context{"Content": message})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
botOut, err := botTmpl.Execute(pongo2.Context{"Content": "Waiting...", "IsPlaceholder": true})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
collection := mongoClient.Database("chat").Collection("messages")
|
|
collection.InsertOne(context.Background(), bson.M{"message": message, "role": "user", "date": time.Now()})
|
|
|
|
return c.SendString(userOut + botOut)
|
|
}
|
|
|
|
func deleteMessageHandler(c *fiber.Ctx) error {
|
|
messageId := c.FormValue("messageId")
|
|
|
|
// Convert the string ID to a MongoDB ObjectID
|
|
objID, err := primitive.ObjectIDFromHex(messageId)
|
|
if err != nil {
|
|
// If the conversion fails, return an error response
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
"error": "Invalid message ID format",
|
|
})
|
|
}
|
|
|
|
// Delete messages in the database. This one and all the following ones
|
|
collection := mongoClient.Database("chat").Collection("messages")
|
|
|
|
// Get the date of the message
|
|
var message Message
|
|
err = collection.FindOne(context.TODO(), bson.M{"_id": objID}).Decode(&message)
|
|
if err != nil {
|
|
if err == mongo.ErrNoDocuments {
|
|
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
|
"error": "Message not found",
|
|
})
|
|
}
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"error": "Failed to find message",
|
|
})
|
|
}
|
|
|
|
// Delete the message and all messages with a date after the specified date
|
|
filter := bson.M{
|
|
"$or": []bson.M{
|
|
{"_id": objID},
|
|
{"date": bson.M{"$gt": message.Date}},
|
|
},
|
|
}
|
|
_, err = collection.DeleteMany(context.TODO(), filter)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"error": "Failed to delete messages",
|
|
})
|
|
}
|
|
|
|
return c.SendString("")
|
|
}
|
|
|
|
func generateChatHTML(c *fiber.Ctx) error {
|
|
// Get the messages from the database
|
|
collection := mongoClient.Database("chat").Collection("messages")
|
|
|
|
// Get all messages
|
|
cursor, err := collection.Find(context.TODO(), bson.D{})
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
"error": "Failed to find messages",
|
|
})
|
|
}
|
|
|
|
// 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",
|
|
})
|
|
}
|
|
|
|
htmlString := "<div class='columns is-centered'><div class='column is-half' id='chat-messages'>"
|
|
|
|
for message := range Messages {
|
|
if Messages[message].Role == "user" {
|
|
userOut, err := userTmpl.Execute(pongo2.Context{"Content": Messages[message].Content})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
htmlString += userOut
|
|
} else {
|
|
botOut, err := botTmpl.Execute(pongo2.Context{"Content": Messages[message].Content})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
htmlString += botOut
|
|
}
|
|
}
|
|
|
|
htmlString += "</div></div>"
|
|
|
|
// Render the HTML template with the messages
|
|
return c.SendString(htmlString)
|
|
}
|
|
|
|
func isMongoDBConnectedHandler(c *fiber.Ctx) error {
|
|
if mongoClient != nil {
|
|
return c.SendString("<h1>Connected</h1>")
|
|
}
|
|
return c.SendString("<h1 hx-get='/isMongoDBConnected' hx-trigger='every 1s' hx-swap='outerHTM'>Not connected</h1>")
|
|
}
|