API package refactor

This commit is contained in:
Manuel Forcén Muñoz 2025-02-12 19:29:00 +01:00
parent a7cd86962e
commit bc6b57bc54
14 changed files with 474 additions and 416 deletions

27
apis/base.go Normal file
View file

@ -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
}

View file

@ -1,19 +1,19 @@
package main package apis
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm" "gorm.io/gorm"
"net/http" "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() username, _, ok := c.Request.BasicAuth()
if !ok { if !ok {
c.Status(http.StatusUnauthorized) c.Status(http.StatusUnauthorized)
return nil return nil
} }
u := models.User{ u := core.User{
Username: username, Username: username,
} }

262
apis/plans.go Normal file
View file

@ -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(&params)
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(&params)
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(&params)
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)
}

91
apis/polls.go Normal file
View file

@ -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(&params)
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(&params)
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)
}

64
apis/users.go Normal file
View file

@ -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)
}

View file

@ -1,4 +1,4 @@
package errors package core
import "errors" import "errors"

View file

@ -1,4 +1,4 @@
package models package core
type User struct { type User struct {
Username string `gorm:"primaryKey" json:"username"` Username string `gorm:"primaryKey" json:"username"`

View file

@ -1,10 +1,9 @@
package models package core
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"errors" "errors"
. "planner/errors"
"gorm.io/gorm" "gorm.io/gorm"
) )

View file

@ -1,11 +1,10 @@
package models package core
import ( import (
"fmt" "fmt"
"strings" "strings"
"gorm.io/gorm" "gorm.io/gorm"
"planner/errors"
) )
func GetPoll(orm *gorm.DB, user User, id uint) (*Poll, error) { 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 { if !found {
return errors.ErrInvalidOption return ErrInvalidOption
} }
if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil { if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil {

View file

@ -1,4 +1,4 @@
package models package core
import ( import (
"gorm.io/gorm" "gorm.io/gorm"

2
db.go
View file

@ -5,7 +5,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"log" "log"
"os" "os"
. "planner/models" . "planner/core"
) )
func bootstrapDatabase() *gorm.DB { func bootstrapDatabase() *gorm.DB {

11
go.mod
View file

@ -2,6 +2,13 @@ module planner
go 1.23.0 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 ( require (
github.com/bytedance/sonic v1.12.2 // indirect github.com/bytedance/sonic v1.12.2 // indirect
github.com/bytedance/sonic/loader v0.2.0 // 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/cloudwego/iasm v0.2.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect
github.com/gin-contrib/sse v0.1.0 // 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/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // 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/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
@ -33,6 +38,4 @@ require (
golang.org/x/text v0.18.0 // indirect golang.org/x/text v0.18.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.5.6 // indirect
gorm.io/gorm v1.25.12 // indirect
) )

11
go.sum
View file

@ -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 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 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.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/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 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 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-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 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 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 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 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= 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/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 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 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/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 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 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/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 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 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/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.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 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.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.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.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/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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 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/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 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 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 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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/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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View file

@ -1,15 +1,9 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"log" "log"
"net/http" "planner/apis"
"strconv"
. "planner/errors"
"planner/models"
. "planner/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -29,396 +23,6 @@ func main() {
defer db.Close() defer db.Close()
r := gin.Default() r := gin.Default()
apis.BindAPIs(r, orm)
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(&params)
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(&params)
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(&params)
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(&params)
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(&params)
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)
})
r.Run() r.Run()
} }