diff --git a/focus-lists/db.go b/focus-lists/db.go new file mode 100644 index 0000000..3446bcf --- /dev/null +++ b/focus-lists/db.go @@ -0,0 +1,95 @@ +package main + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +var db *sql.DB + +type List struct { + ID int `json:"id"` + ProjectID int `json:"project_id"` + Title string `json:"title"` + Color string `json:"color"` +} + +func InitDB(driver string, connStr string) error { + var err error + db, err = sql.Open(driver, connStr) + if err != nil { + return err + } + + _, err = db.Exec(` + CREATE TABLE IF NOT EXISTS lists ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + project_id INTEGER, + title TEXT, + color TEXT + ); + `) + if err != nil { + return err + } + return nil +} + +func Create(l List) (int, error) { + res, err := db.Exec("INSERT INTO lists (project_id, title, color) VALUES (?, ?, ?)", l.ProjectID, l.Title, l.Color) + if err != nil { + return 0, err + } + + id, err := res.LastInsertId() + if err != nil { + return 0, err + } + + return int(id), nil +} + +func GetAll(projectID int) ([]List, error) { + rows, err := db.Query("SELECT * FROM lists WHERE project_id = ?", projectID) + if err != nil { + return nil, err + } + defer rows.Close() + + var lists []List + for rows.Next() { + var l List + if err := rows.Scan(&l.ID, &l.ProjectID, &l.Title, &l.Color); err != nil { + return nil, err + } + lists = append(lists, l) + } + + if err = rows.Err(); err != nil { + return nil, err + } + + return lists, nil +} + +func Get(id int) (*List, error) { + var l List + + err := db.QueryRow("SELECT * FROM lists WHERE id = ?", id).Scan(&l.ID, &l.ProjectID, &l.Title, &l.Color) + if err != nil { + return nil, err + } + + return &l, nil +} + +func Delete(id int) error { + _, err := db.Exec("DELETE FROM lists WHERE id = ?", id) + return err +} + +func Update(l List) error { + _, err := db.Exec("UPDATE lists SET project_id = ?, title = ?, color = ? WHERE id = ?", l.ProjectID, l.Title, l.Color, l.ID) + return err +} diff --git a/focus-lists/go.mod b/focus-lists/go.mod new file mode 100644 index 0000000..18c30fa --- /dev/null +++ b/focus-lists/go.mod @@ -0,0 +1,21 @@ +module git.bhasher.com/focus/focus-lists + +go 1.21.4 + +require github.com/gofiber/fiber/v2 v2.51.0 + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.50.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.14.0 // indirect +) + +require github.com/mattn/go-sqlite3 v1.14.19 diff --git a/focus-lists/go.sum b/focus-lists/go.sum new file mode 100644 index 0000000..ecd124a --- /dev/null +++ b/focus-lists/go.sum @@ -0,0 +1,41 @@ +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= +github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/focus-lists/main.go b/focus-lists/main.go new file mode 100644 index 0000000..48071ad --- /dev/null +++ b/focus-lists/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "fmt" + "log" + "strconv" + + "github.com/gofiber/fiber/v2" +) + +func main() { + driver := "sqlite3" + connStr := "db.sqlite" + port := "3001" + + if err := InitDB(driver, connStr); err != nil { + log.Fatal(err) + } + + app := fiber.New() + + app.Post("/list", createListHandler) + app.Get("/lists/:board_id", getAllListsHandler) + app.Get("/list/:id", getListHandler) + app.Delete("/list/:id", deleteListHandler) + app.Put("/list/:id", updateListHandler) + + log.Fatal(app.Listen(fmt.Sprintf(":%v", port))) +} + +func createListHandler(c *fiber.Ctx) error { + list := List{} + if err := c.BodyParser(&list); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"}) + } + + id, err := Create(list) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot create list"}) + } + + return c.Status(fiber.StatusCreated).JSON(fiber.Map{"id": id}) +} + +func getAllListsHandler(c *fiber.Ctx) error { + boardID, err := strconv.Atoi(c.Params("board_id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid board ID"}) + } + + lists, err := GetAll(boardID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot retrieve lists"}) + } + + return c.JSON(lists) +} + +func getListHandler(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid list ID"}) + } + + list, err := Get(id) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot retrieve list"}) + } + if list == nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "List not found"}) + } + + return c.JSON(list) +} + +func deleteListHandler(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid list ID"}) + } + + err = Delete(id) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot delete list"}) + } + + return c.SendStatus(fiber.StatusNoContent) +} + +func updateListHandler(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid list ID"}) + } + + list := List{ID: id} + if err := c.BodyParser(&list); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"}) + } + + err = Update(list) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot update list"}) + } + + return c.SendStatus(fiber.StatusOK) +} diff --git a/focus-projects/main.go b/focus-projects/main.go index 7e3866b..d7d6809 100644 --- a/focus-projects/main.go +++ b/focus-projects/main.go @@ -31,9 +31,7 @@ func main() { func getAllProjectsHandler(c *fiber.Ctx) error { projects, err := GetAll() if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Cannot retrieve projects", - }) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Cannot retrieve projects"}) } fmt.Println(projects) @@ -44,21 +42,15 @@ func getAllProjectsHandler(c *fiber.Ctx) error { func getProjectHandler(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Invalid project ID", - }) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid project ID"}) } project, err := Get(id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Error fetching project", - }) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error fetching project"}) } if project == nil { - return c.Status(fiber.StatusNotFound).JSON(fiber.Map{ - "error": "Project not found", - }) + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Project not found"}) } return c.JSON(project) } @@ -66,43 +58,31 @@ func getProjectHandler(c *fiber.Ctx) error { func createProjectHandler(c *fiber.Ctx) error { p := new(Project) if err := c.BodyParser(p); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Error parsing request", - }) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Error parsing request"}) } id, err := Create(*p) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Error creating project", - }) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error creating project"}) } - return c.Status(fiber.StatusCreated).JSON(fiber.Map{ - "id": id, - }) + return c.Status(fiber.StatusCreated).JSON(fiber.Map{"id": id}) } func updateProjectHandler(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Invalid project ID", - }) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid project ID"}) } p := Project{ID: id} if err := c.BodyParser(&p); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Error parsing request", - }) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Error parsing request"}) } err = Update(p) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Error updating project", - }) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error updating project"}) } return c.SendStatus(fiber.StatusOK) @@ -111,17 +91,13 @@ func updateProjectHandler(c *fiber.Ctx) error { func deleteProjectHandler(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "error": "Invalid project ID", - }) + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid project ID"}) } err = Delete(id) if err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Error deleting project", - }) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Error deleting project"}) } - return c.SendStatus(fiber.StatusNoContent) + return c.SendStatus(fiber.StatusOK) } diff --git a/go.work b/go.work new file mode 100644 index 0000000..c153797 --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.21.4 + +use ( + ./focus-projects + ./focus-lists +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..6d49f11 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,2 @@ +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=