package main import ( "database/sql" "fmt" "log" "net/http" "os" "strconv" "github.com/gin-gonic/gin" _ "github.com/glebarez/go-sqlite" ) type User struct { ID int `json:"id"` Username string `json:"username"` Password string `json:"password"` } type Channel struct { ID int `json:"id"` Name string `json:"name"` } type Message struct { ID int `json:"id"` ChannelID int `json:"channel_id"` UserID int `json:"user_id"` UserName string `json:"user_name"` Text string `json:"text"` } func main() { // Get the working directory wd, err := os.Getwd() if err != nil { log.Fatal(err) } // Print the working directory fmt.Println("Working directory:", wd) // Open the SQLite database file db, err := sql.Open("sqlite", wd+"/database.db") defer func(db *sql.DB) { err := db.Close() if err != nil { log.Fatal(err) } }(db) // Create the Gin router r := gin.Default() if err != nil { log.Fatal(err) } // Creation endpoints r.POST("/users", func(c *gin.Context) { createUser(c, db) }) r.POST("/channels", func(c *gin.Context) { createChannel(c, db) }) r.POST("/messages", func(c *gin.Context) { createMessage(c, db) }) // Listing endpoints r.GET("/channels", func(c *gin.Context) { listChannels(c, db) }) r.GET("/messages", func(c *gin.Context) { listMessages(c, db) }) // Login endpoint r.POST("/login", func(c *gin.Context) { login(c, db) }) err = r.Run(":8080") if err != nil { log.Fatal(err) } } // User creation endpoint func createUser(c *gin.Context, db *sql.DB) { // Parse JSON request body into User struct var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Insert user into database result, err := db.Exec("INSERT INTO users (username, password) VALUES (?, ?)", user.Username, user.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Get ID of newly inserted user id, err := result.LastInsertId() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Return ID of newly inserted user c.JSON(http.StatusOK, gin.H{"id": id}) } // Login endpoint func login(c *gin.Context, db *sql.DB) { // Parse JSON request body into User struct var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Query database for user row := db.QueryRow("SELECT id FROM users WHERE username = ? AND password = ?", user.Username, user.Password) // Get ID of user var id int err := row.Scan(&id) if err != nil { // Check if user was not found if err == sql.ErrNoRows { c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"}) return } // Return error if other error occurred c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) } // Return ID of user c.JSON(http.StatusOK, gin.H{"id": id}) } // Channel creation endpoint func createChannel(c *gin.Context, db *sql.DB) { // Parse JSON request body into Channel struct var channel Channel if err := c.ShouldBindJSON(&channel); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Insert channel into database result, err := db.Exec("INSERT INTO channels (name) VALUES (?)", channel.Name) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Get ID of newly inserted channel id, err := result.LastInsertId() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Return ID of newly inserted channel c.JSON(http.StatusOK, gin.H{"id": id}) } // Channel listing endpoint func listChannels(c *gin.Context, db *sql.DB) { // Query database for channels rows, err := db.Query("SELECT id, name FROM channels") if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Create slice of channels var channels []Channel // Iterate over rows for rows.Next() { // Create new channel var channel Channel // Scan row into channel err := rows.Scan(&channel.ID, &channel.Name) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Append channel to slice channels = append(channels, channel) } // Return slice of channels c.JSON(http.StatusOK, channels) } // Message creation endpoint func createMessage(c *gin.Context, db *sql.DB) { // Parse JSON request body into Message struct var message Message if err := c.ShouldBindJSON(&message); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Insert message into database result, err := db.Exec("INSERT INTO messages (channel_id, user_id, message) VALUES (?, ?, ?)", message.ChannelID, message.UserID, message.Text) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Get ID of newly inserted message id, err := result.LastInsertId() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Return ID of newly inserted message c.JSON(http.StatusOK, gin.H{"id": id}) } // Message listing endpoint func listMessages(c *gin.Context, db *sql.DB) { // Parse channel ID from URL channelID, err := strconv.Atoi(c.Query("channelID")) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Parse optional limit query parameter from URL limit, err := strconv.Atoi(c.Query("limit")) if err != nil { // Set limit to 100 if not provided limit = 100 } // Parse last message ID query parameter from URL. This is used to get messages after a certain message. lastMessageID, err := strconv.Atoi(c.Query("lastMessageID")) if err != nil { // Set last message ID to 0 if not provided lastMessageID = 0 } // Query database for messages rows, err := db.Query("SELECT m.id, channel_id, user_id, u.username AS user_name, message FROM messages m LEFT JOIN users u ON u.id = m.user_id WHERE channel_id = ? AND m.id > ? ORDER BY m.id ASC LIMIT ?", channelID, lastMessageID, limit) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Create slice of messages var messages []Message // Iterate over rows for rows.Next() { // Create new message var message Message // Scan row into message err := rows.Scan(&message.ID, &message.ChannelID, &message.UserID, &message.UserName, &message.Text) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Append message to slice messages = append(messages, message) } // Return slice of messages c.JSON(http.StatusOK, messages) }