Cleaning up examples in main and splitting the code into suitable packages
This commit is contained in:
parent
5cefed405f
commit
469f866554
5 changed files with 120 additions and 29 deletions
|
@ -5,30 +5,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"ttime/internal/config"
|
"ttime/internal/config"
|
||||||
"ttime/internal/database"
|
"ttime/internal/database"
|
||||||
|
"ttime/internal/handlers"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The button state as represented in memory
|
|
||||||
type ButtonState struct {
|
|
||||||
PressCount int `json:"pressCount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is what a handler with a receiver looks like
|
|
||||||
// Keep in mind that concurrent state access is not (usually) safe
|
|
||||||
// And will in practice be guarded by a mutex
|
|
||||||
func (b *ButtonState) pressHandlerGet(c *fiber.Ctx) error {
|
|
||||||
fmt.Println("Get request received")
|
|
||||||
return c.JSON(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *ButtonState) pressHandlerPost(c *fiber.Ctx) error {
|
|
||||||
fmt.Println("Post request received")
|
|
||||||
b.PressCount++
|
|
||||||
return c.JSON(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
conf, err := config.ReadConfigFromFile("config.toml")
|
conf, err := config.ReadConfigFromFile("config.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,21 +22,21 @@ func main() {
|
||||||
str, _ := json.MarshalIndent(conf, "", " ")
|
str, _ := json.MarshalIndent(conf, "", " ")
|
||||||
fmt.Println(string(str))
|
fmt.Println(string(str))
|
||||||
|
|
||||||
database.DbConnect(conf.DbPath)
|
// Connect to the database
|
||||||
|
db := database.DbConnect(conf.DbPath)
|
||||||
|
// Get our global state
|
||||||
|
gs := handlers.NewGlobalState(db)
|
||||||
|
// Create the server
|
||||||
server := fiber.New()
|
server := fiber.New()
|
||||||
|
|
||||||
|
// Mount our static files (Beware of the security implications of this!)
|
||||||
|
// This will likely be replaced by an embedded filesystem in the future
|
||||||
server.Static("/", "./static")
|
server.Static("/", "./static")
|
||||||
|
|
||||||
b := &ButtonState{PressCount: 0}
|
// Register our handlers
|
||||||
server.Get("/api/button", b.pressHandlerGet)
|
server.Post("/api/register", gs.Register)
|
||||||
server.Post("/api/button", b.pressHandlerPost)
|
|
||||||
server.Post("/api/project", func(c *fiber.Ctx) error {
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"message": "Project created",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// Announce the port we are listening on and start the server
|
||||||
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error starting server: ", err)
|
fmt.Println("Error starting server: ", err)
|
||||||
|
|
36
backend/internal/handlers/global_state.go
Normal file
36
backend/internal/handlers/global_state.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ttime/internal/database"
|
||||||
|
"ttime/internal/types"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The actual interface that we will use
|
||||||
|
type GlobalState interface {
|
||||||
|
Register(c *fiber.Ctx) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Constructor"
|
||||||
|
func NewGlobalState(db database.Database) GlobalState {
|
||||||
|
return &GState{Db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The global state, which implements all the handlers
|
||||||
|
type GState struct {
|
||||||
|
Db database.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gs *GState) Register(c *fiber.Ctx) error {
|
||||||
|
u := new(types.User)
|
||||||
|
if err := c.BodyParser(u); err != nil {
|
||||||
|
return c.Status(400).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gs.Db.AddUser(u.Username, u.Password); err != nil {
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(200).SendString("User added")
|
||||||
|
}
|
15
backend/internal/handlers/global_state_test.go
Normal file
15
backend/internal/handlers/global_state_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"ttime/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The actual interface that we will use
|
||||||
|
func TestGlobalState(t *testing.T) {
|
||||||
|
db := database.DbConnect(":memory:")
|
||||||
|
gs := NewGlobalState(db)
|
||||||
|
if gs == nil {
|
||||||
|
t.Error("NewGlobalState returned nil")
|
||||||
|
}
|
||||||
|
}
|
23
backend/internal/types/users.go
Normal file
23
backend/internal/types/users.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// User struct represents a user in the system
|
||||||
|
type User struct {
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user needs to be served over the api, we dont want to send the password
|
||||||
|
// ToPublicUser converts a User to a PublicUser
|
||||||
|
func (u *User) ToPublicUser() (*PublicUser, error) {
|
||||||
|
return &PublicUser{
|
||||||
|
UserId: u.UserId,
|
||||||
|
Username: u.Username,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicUser represents a user that is safe to send over the API (no password)
|
||||||
|
type PublicUser struct {
|
||||||
|
UserId string `json:"userId"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
35
backend/internal/types/users_test.go
Normal file
35
backend/internal/types/users_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// NewUser returns a new User
|
||||||
|
func TestNewUser(t *testing.T) {
|
||||||
|
u := User{
|
||||||
|
UserId: "123",
|
||||||
|
Username: "test",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.UserId != "123" {
|
||||||
|
t.Errorf("Expected user id to be 123, got %s", u.UserId)
|
||||||
|
}
|
||||||
|
if u.Username != "test" {
|
||||||
|
t.Errorf("Expected username to be test, got %s", u.Username)
|
||||||
|
}
|
||||||
|
if u.Password != "password" {
|
||||||
|
t.Errorf("Expected password to be password, got %s", u.Password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToPublicUser(t *testing.T) {
|
||||||
|
u := User{
|
||||||
|
UserId: "123",
|
||||||
|
Username: "test",
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := u.ToPublicUser()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected no error, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue