diff --git a/apis/base.go b/apis/base.go new file mode 100644 index 0000000..bbf34da --- /dev/null +++ b/apis/base.go @@ -0,0 +1,27 @@ +package apis + +import ( + "errors" + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +var db *gorm.DB + +func BindAPIs(r *gin.Engine, cfg_db *gorm.DB) error { + if cfg_db == nil { + return errors.New("Database is null") + } + db = cfg_db + r.GET("/", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "pong", + }) + }) + bindPlanAPIs(r) + bindPollAPIs(r) + bindUserAPIs(r) + return nil +} diff --git a/extract.go b/apis/extract.go similarity index 75% rename from extract.go rename to apis/extract.go index 2b5e709..7ed4f7d 100644 --- a/extract.go +++ b/apis/extract.go @@ -1,19 +1,19 @@ -package main +package apis import ( "github.com/gin-gonic/gin" "gorm.io/gorm" "net/http" - "planner/models" + "planner/core" ) -func ExtractUser(orm *gorm.DB, c *gin.Context) *models.User { +func extractUser(orm *gorm.DB, c *gin.Context) *core.User { username, _, ok := c.Request.BasicAuth() if !ok { c.Status(http.StatusUnauthorized) return nil } - u := models.User{ + u := core.User{ Username: username, } diff --git a/apis/plans.go b/apis/plans.go new file mode 100644 index 0000000..0664313 --- /dev/null +++ b/apis/plans.go @@ -0,0 +1,262 @@ +package apis + +import ( + "errors" + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + . "planner/core" +) + +func createPlan(c *gin.Context) { + u := extractUser(db, c) + if u == nil { + return + } + + var plan_req struct { + Name string `json:"name" form:"name"` + } + c.Bind(&plan_req) + var plan Plan = Plan{ + Name: plan_req.Name, + Owner: u.Username, + Members: []Member{ + { + UserID: u.Username, + Type: "user", + Status: "ready", + JoinCode: "owner", + }, + }, + } + result := db.Create(&plan) + + if result.Error != nil { + c.JSON(http.StatusInternalServerError, result.Error) + } else { + c.JSON(http.StatusOK, plan_req) + } +} + +func listPlans(c *gin.Context) { + u := extractUser(db, c) + if u == nil { + return + } + plans, err := u.GetPlans(db) + if err == nil { + c.JSON(http.StatusOK, plans) + } else { + c.String(http.StatusInternalServerError, err.Error()) + } +} + +func getPlan(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var params struct { + Id uint `uri:"id"` + } + bind_result := c.BindUri(¶ms) + if bind_result != nil { + return + } + + plan, err := GetPlan(db, *user, params.Id) + + if err == nil { + c.JSON(http.StatusOK, plan) + } else if errors.Is(err, ErrNotMember) { + c.Status(http.StatusForbidden) + } else if errors.Is(err, ErrNotFound) { + c.Status(http.StatusNotFound) + } else { + c.String(http.StatusInternalServerError, err.Error()) + } +} + +func listPlanMembers(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + plan_id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.Status(http.StatusBadRequest) + return + } + + plan, err := GetPlan(db, *user, uint(plan_id)) + members, err := plan.GetAllUsers(db) + + if err == nil { + c.JSON(http.StatusOK, members) + } else if errors.Is(err, ErrNotMember) { + c.Status(http.StatusForbidden) + } else if errors.Is(err, ErrNotFound) { + c.Status(http.StatusNotFound) + } else { + c.String(http.StatusInternalServerError, err.Error()) + } +} + +func addPlanMember(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + plan_id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.Status(http.StatusBadRequest) + return + } + + plan, err := GetPlan(db, *user, uint(plan_id)) + + var new_member Member + if err := c.ShouldBind(&new_member); err != nil { + c.String(http.StatusInternalServerError, err.Error()) + return + } + + err = plan.AddMember(db, &new_member) + + if err == nil { + c.JSON(http.StatusOK, new_member) + } else if errors.Is(err, ErrNotMember) { + c.Status(http.StatusForbidden) + } else if errors.Is(err, ErrNotFound) { + c.Status(http.StatusNotFound) + } else { + c.String(http.StatusInternalServerError, err.Error()) + } +} + +func joinPlan(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + plan_id, err := strconv.Atoi(c.Param("id")) + if err != nil { + c.Status(http.StatusBadRequest) + return + } + plan, err := GetPlan(db, *user, uint(plan_id)) + if err != nil { + c.Status(http.StatusInternalServerError) + return + } + + member, err := plan.GetMember(db, user) + var query struct { + JoinCode string `fdb:"code"` + } + if c.ShouldBindQuery(&query) != nil || query.JoinCode == "" { + c.Status(http.StatusBadRequest) + return + } + if member.Status != "pending" { + c.String(http.StatusConflict, "User is not pending") + return + } + if member.JoinCode == query.JoinCode { + member.Status = "ready" + err := db.Model(&member).Update("status", member.Status).Error + if err != nil { + c.String(http.StatusInternalServerError, err.Error()) + return + } + } else { + c.String(http.StatusConflict, "Invalid join code") + return + } + c.Status(http.StatusOK) +} + +func createPlanPoll(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var params struct { + Id uint `uri:"id"` + } + bind_result := c.BindUri(¶ms) + if bind_result != nil { + return + } + + plan, err := GetPlan(db, *user, params.Id) + + if errors.Is(err, ErrNotFound) { + c.Status(http.StatusNotFound) + return + } else if errors.Is(err, ErrNotMember) { + c.Status(http.StatusForbidden) + return + } else if err != nil { + c.String(http.StatusInternalServerError, err.Error()) + return + } + + var poll_opts struct { + Options string `json:"options"` + } + bind_result = c.Bind(&poll_opts) + if bind_result != nil { + fmt.Println(bind_result) + return + } + + poll := Poll{ + PlanID: plan.ID, + Options: poll_opts.Options, + } + db.Create(&poll) + + c.JSON(http.StatusCreated, poll) +} + +func listPlanPolls(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var params struct { + Id uint `uri:"id"` + } + + bind_result := c.BindUri(¶ms) + if bind_result != nil { + c.Status(http.StatusBadRequest) + return + } + + var polls []Poll + db.Where("plan_id = ?", params.Id).Find(&polls) + c.JSON(http.StatusOK, polls) +} + +func bindPlanAPIs(r *gin.Engine) { + r.POST("/plans", createPlan) + r.GET("/plans", listPlans) + r.GET("/plans/:id", getPlan) + r.GET("/plans/:id/members", listPlanMembers) + r.POST("/plans/:id/members", addPlanMember) + r.GET("/plans/:id/join", joinPlan) + r.POST("/plans/:id/polls", createPlanPoll) + r.GET("/plans/:id/polls", listPlanPolls) +} diff --git a/apis/polls.go b/apis/polls.go new file mode 100644 index 0000000..93d43d6 --- /dev/null +++ b/apis/polls.go @@ -0,0 +1,91 @@ +package apis + +import ( + "fmt" + "net/http" + "planner/core" + + "github.com/gin-gonic/gin" +) + +func getPoll(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var params struct { + PollId uint `uri:"poll_id"` + } + + bind_result := c.BindUri(¶ms) + if bind_result != nil { + fmt.Println(bind_result) + return + } + fmt.Println(params) + + poll, _ := core.GetPoll(db, *user, params.PollId) + c.JSON(http.StatusOK, poll) +} + +func getPollVotes(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var params struct { + PollId uint `uri:"poll_id"` + } + + bind_result := c.BindUri(¶ms) + if bind_result != nil { + fmt.Println(bind_result) + return + } + + var votes []core.Vote + db.Where("poll_id = ?", params.PollId).Find(&votes) + c.JSON(http.StatusOK, &votes) +} + +func pollVote(c *gin.Context) { + user := extractUser(db, c) + if user == nil { + return + } + + var path_params struct { + PollId uint `uri:"poll_id"` + } + + bind_result := c.BindUri(&path_params) + if bind_result != nil { + fmt.Println(bind_result) + return + } + + poll, err := core.GetPoll(db, *user, path_params.PollId) + if err != nil { + c.String(http.StatusInternalServerError, err.Error()) + return + } + + var vote_params struct { + Vote string `json:"vote"` + } + c.Bind(&vote_params) + + if err := poll.SetVote(db, *user, vote_params.Vote); err != nil { + c.String(http.StatusBadRequest, err.Error()) + } + + c.Status(http.StatusOK) +} + +func bindPollAPIs(r *gin.Engine) { + r.GET("/polls/:poll_id", getPoll) + r.GET("/polls/:poll_id/votes", getPollVotes) + r.POST("/polls/:poll_id/votes", pollVote) +} diff --git a/apis/users.go b/apis/users.go new file mode 100644 index 0000000..16ad82a --- /dev/null +++ b/apis/users.go @@ -0,0 +1,64 @@ +package apis + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + + . "planner/core" +) + +func getUserByName(c *gin.Context) { + var q struct { + Name string `fdb:"name"` + } + if c.ShouldBind(&q) == nil { + user := User{ + Username: q.Name, + } + db.Take(&user) + fmt.Println(user) + c.JSON(http.StatusOK, user) + } +} + +func createUser(c *gin.Context) { + var u User + if c.ShouldBind(&u) == nil { + db.Create(&u) + c.Status(http.StatusCreated) + } else { + fmt.Print("Could not bind model") + c.String(http.StatusInternalServerError, "Unable to bind user") + } +} + +func login(c *gin.Context) { + var q struct { + Username string `json:"username" form:"username"` + Password string `json:"password" form:"password"` + } + if c.ShouldBind(&q) == nil { + if q.Username == "" { + c.String(http.StatusBadRequest, "Login data is null") + } else { + user := User{ + Username: q.Username, + } + db.Take(&user) + if user.Password == q.Password { + c.JSON(http.StatusOK, map[string]string{"username": user.Username}) + } else { + c.Status(http.StatusForbidden) + } + } + } else { + c.String(http.StatusBadRequest, "Unable to bind data") + } +} +func bindUserAPIs(r *gin.Engine) { + r.POST("/login", login) + r.GET("/user", getUserByName) + r.POST("/user", createUser) +} diff --git a/errors/errors.go b/core/errors.go similarity index 93% rename from errors/errors.go rename to core/errors.go index 0832adf..fef9ca3 100644 --- a/errors/errors.go +++ b/core/errors.go @@ -1,4 +1,4 @@ -package errors +package core import "errors" diff --git a/models/models.go b/core/models.go similarity index 99% rename from models/models.go rename to core/models.go index fa7e415..87afa0a 100644 --- a/models/models.go +++ b/core/models.go @@ -1,4 +1,4 @@ -package models +package core type User struct { Username string `gorm:"primaryKey" json:"username"` diff --git a/models/plans.go b/core/plans.go similarity index 98% rename from models/plans.go rename to core/plans.go index cae2bc4..2a96533 100644 --- a/models/plans.go +++ b/core/plans.go @@ -1,10 +1,9 @@ -package models +package core import ( "crypto/rand" "encoding/base64" "errors" - . "planner/errors" "gorm.io/gorm" ) diff --git a/models/polls.go b/core/polls.go similarity index 90% rename from models/polls.go rename to core/polls.go index 0484c6c..564d0b5 100644 --- a/models/polls.go +++ b/core/polls.go @@ -1,11 +1,10 @@ -package models +package core import ( "fmt" "strings" "gorm.io/gorm" - "planner/errors" ) func GetPoll(orm *gorm.DB, user User, id uint) (*Poll, error) { @@ -33,7 +32,7 @@ func (p *Poll) SetVote(orm *gorm.DB, user User, option string) error { } } if !found { - return errors.ErrInvalidOption + return ErrInvalidOption } if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil { diff --git a/models/users.go b/core/users.go similarity index 96% rename from models/users.go rename to core/users.go index a4e72cb..c6a95a7 100644 --- a/models/users.go +++ b/core/users.go @@ -1,4 +1,4 @@ -package models +package core import ( "gorm.io/gorm" diff --git a/db.go b/db.go index db40d4c..1f49809 100644 --- a/db.go +++ b/db.go @@ -5,7 +5,7 @@ import ( "gorm.io/gorm" "log" "os" - . "planner/models" + . "planner/core" ) func bootstrapDatabase() *gorm.DB { diff --git a/go.mod b/go.mod index 96690db..581ef33 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,13 @@ module planner go 1.23.0 +require ( + github.com/gin-gonic/gin v1.10.0 + github.com/mattn/go-sqlite3 v1.14.23 + gorm.io/driver/sqlite v1.5.6 + gorm.io/gorm v1.25.12 +) + require ( github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect @@ -9,7 +16,6 @@ require ( github.com/cloudwego/iasm v0.2.0 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.10.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.22.0 // indirect @@ -20,7 +26,6 @@ require ( github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.23 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect @@ -33,6 +38,4 @@ require ( golang.org/x/text v0.18.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/driver/sqlite v1.5.6 // indirect - gorm.io/gorm v1.25.12 // indirect ) diff --git a/go.sum b/go.sum index db68b34..45e3183 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= @@ -15,6 +16,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -23,6 +26,8 @@ github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4 github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= @@ -47,6 +52,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -56,6 +62,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= @@ -73,8 +80,11 @@ golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -84,4 +94,3 @@ gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDa gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/planner.go b/planner.go index ecbddfb..f30a018 100644 --- a/planner.go +++ b/planner.go @@ -1,15 +1,9 @@ package main import ( - "errors" "fmt" "log" - "net/http" - "strconv" - - . "planner/errors" - "planner/models" - . "planner/models" + "planner/apis" "github.com/gin-gonic/gin" _ "github.com/mattn/go-sqlite3" @@ -29,396 +23,6 @@ func main() { defer db.Close() r := gin.Default() - - r.GET("/", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{ - "message": "pong", - }) - }) - - r.POST("/login", func(c *gin.Context) { - var q struct { - Username string `json:"username" form:"username"` - Password string `json:"password" form:"password"` - } - if c.ShouldBind(&q) == nil { - if q.Username == "" { - c.String(http.StatusBadRequest, "Login data is null") - } else { - user := User{ - Username: q.Username, - } - orm.Take(&user) - if user.Password == q.Password { - c.JSON(http.StatusOK, map[string]string{"username": user.Username}) - } else { - c.Status(http.StatusForbidden) - } - } - } else { - c.String(http.StatusBadRequest, "Unable to bind data") - } - }) - - r.GET("/user", func(c *gin.Context) { - var q struct { - Name string `form:"name"` - } - if c.ShouldBind(&q) == nil { - user := User{ - Username: q.Name, - } - orm.Take(&user) - fmt.Println(user) - c.JSON(http.StatusOK, user) - } - }) - - r.POST("/user", func(c *gin.Context) { - var u User - if c.ShouldBind(&u) == nil { - orm.Create(&u) - c.Status(http.StatusCreated) - } else { - fmt.Print("Could not bind model") - } - }) - - r.GET("/users/:id", func(c *gin.Context) { - u := ExtractUser(orm, c) - if u == nil { - return - } - - requested := c.Param("id") - if requested == "" { - c.Status(http.StatusBadRequest) - return - } - - other := User{Username: requested} - res := orm.Take(&other) - if res.Error != nil { - c.String(http.StatusInternalServerError, res.Error.Error()) - return - } - c.JSON(http.StatusOK, other) - }) - - r.POST("/plans", func(c *gin.Context) { - u := ExtractUser(orm, c) - if u == nil { - return - } - - var plan_req struct { - Name string `json:"name" form:"name"` - } - c.Bind(&plan_req) - var plan Plan = Plan{ - Name: plan_req.Name, - Owner: u.Username, - Members: []Member{ - { - UserID: u.Username, - Type: "user", - Status: "ready", - JoinCode: "owner", - }, - }, - } - result := orm.Create(&plan) - - if result.Error != nil { - c.JSON(http.StatusInternalServerError, result.Error) - } else { - c.JSON(http.StatusOK, plan_req) - } - }) - - r.GET("/plans", func(c *gin.Context) { - u := ExtractUser(orm, c) - if u == nil { - return - } - plans, err := u.GetPlans(orm) - if err == nil { - c.JSON(http.StatusOK, plans) - } else { - c.String(http.StatusInternalServerError, err.Error()) - } - }) - - r.GET("/plans/:id", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var params struct { - Id uint `uri:"id"` - } - bind_result := c.BindUri(¶ms) - if bind_result != nil { - return - } - - plan, err := GetPlan(orm, *user, params.Id) - - if err == nil { - c.JSON(http.StatusOK, plan) - } else if errors.Is(err, ErrNotMember) { - c.Status(http.StatusForbidden) - } else if errors.Is(err, ErrNotFound) { - c.Status(http.StatusNotFound) - } else { - c.String(http.StatusInternalServerError, err.Error()) - } - }) - - r.GET("/plans/:id/members", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - plan_id, err := strconv.Atoi(c.Param("id")) - if err != nil { - c.Status(http.StatusBadRequest) - return - } - - plan, err := GetPlan(orm, *user, uint(plan_id)) - members, err := plan.GetAllUsers(orm) - - if err == nil { - c.JSON(http.StatusOK, members) - } else if errors.Is(err, ErrNotMember) { - c.Status(http.StatusForbidden) - } else if errors.Is(err, ErrNotFound) { - c.Status(http.StatusNotFound) - } else { - c.String(http.StatusInternalServerError, err.Error()) - } - }) - - r.POST("/plans/:id/members", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - plan_id, err := strconv.Atoi(c.Param("id")) - if err != nil { - c.Status(http.StatusBadRequest) - return - } - - plan, err := GetPlan(orm, *user, uint(plan_id)) - - var new_member Member - if err := c.ShouldBind(&new_member); err != nil { - c.String(http.StatusInternalServerError, err.Error()) - return - } - - err = plan.AddMember(orm, &new_member) - - if err == nil { - c.JSON(http.StatusOK, new_member) - } else if errors.Is(err, ErrNotMember) { - c.Status(http.StatusForbidden) - } else if errors.Is(err, ErrNotFound) { - c.Status(http.StatusNotFound) - } else { - c.String(http.StatusInternalServerError, err.Error()) - } - }) - - r.GET("/plans/:id/join", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - plan_id, err := strconv.Atoi(c.Param("id")) - if err != nil { - c.Status(http.StatusBadRequest) - return - } - plan, err := GetPlan(orm, *user, uint(plan_id)) - if err != nil { - c.Status(http.StatusInternalServerError) - return - } - - member, err := plan.GetMember(orm, user) - var query struct { - JoinCode string `form:"code"` - } - if c.ShouldBindQuery(&query) != nil || query.JoinCode == "" { - c.Status(http.StatusBadRequest) - return - } - if member.Status != "pending" { - c.String(http.StatusConflict, "User is not pending") - return - } - if member.JoinCode == query.JoinCode { - member.Status = "ready" - err := orm.Model(&member).Update("status", member.Status).Error - if err != nil { - c.String(http.StatusInternalServerError, err.Error()) - return - } - } else { - c.String(http.StatusConflict, "Invalid join code") - return - } - c.Status(http.StatusOK) - }) - - r.POST("/plans/:id/polls", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var params struct { - Id uint `uri:"id"` - } - bind_result := c.BindUri(¶ms) - if bind_result != nil { - return - } - - plan, err := GetPlan(orm, *user, params.Id) - - if errors.Is(err, ErrNotFound) { - c.Status(http.StatusNotFound) - return - } else if errors.Is(err, ErrNotMember) { - c.Status(http.StatusForbidden) - return - } else if err != nil { - c.String(http.StatusInternalServerError, err.Error()) - return - } - - var poll_opts struct { - Options string `json:"options"` - } - bind_result = c.Bind(&poll_opts) - if bind_result != nil { - fmt.Println(bind_result) - return - } - - poll := Poll{ - PlanID: plan.ID, - Options: poll_opts.Options, - } - orm.Create(&poll) - - c.JSON(http.StatusCreated, poll) - }) - - r.GET("/plans/:id/polls", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var params struct { - Id uint `uri:"id"` - } - - bind_result := c.BindUri(¶ms) - if bind_result != nil { - c.Status(http.StatusBadRequest) - return - } - - var polls []Poll - orm.Where("plan_id = ?", params.Id).Find(&polls) - c.JSON(http.StatusOK, polls) - }) - - r.GET("/polls/:poll_id", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var params struct { - PollId uint `uri:"poll_id"` - } - - bind_result := c.BindUri(¶ms) - if bind_result != nil { - fmt.Println(bind_result) - return - } - fmt.Println(params) - - poll, _ := models.GetPoll(orm, *user, params.PollId) - c.JSON(http.StatusOK, poll) - }) - - r.GET("/polls/:poll_id/votes", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var params struct { - PollId uint `uri:"poll_id"` - } - - bind_result := c.BindUri(¶ms) - if bind_result != nil { - fmt.Println(bind_result) - return - } - - var votes []Vote - orm.Where("poll_id = ?", params.PollId).Find(&votes) - c.JSON(http.StatusOK, &votes) - }) - - r.POST("/polls/:poll_id/votes", func(c *gin.Context) { - user := ExtractUser(orm, c) - if user == nil { - return - } - - var path_params struct { - PollId uint `uri:"poll_id"` - } - - bind_result := c.BindUri(&path_params) - if bind_result != nil { - fmt.Println(bind_result) - return - } - - poll, err := models.GetPoll(orm, *user, path_params.PollId) - if err != nil { - c.String(http.StatusInternalServerError, err.Error()) - return - } - - var vote_params struct { - Vote string `json:"vote"` - } - c.Bind(&vote_params) - - if err := poll.SetVote(orm, *user, vote_params.Vote); err != nil { - c.String(http.StatusBadRequest, err.Error()) - } - - c.Status(http.StatusOK) - }) - + apis.BindAPIs(r, orm) r.Run() }