Init project with vote module

This commit is contained in:
Manuel Forcén Muñoz 2024-11-09 19:35:44 +01:00
commit 98f3c2aedc
11 changed files with 671 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
ui/node_modules
planner
db.sqlite

63
db.go Normal file
View file

@ -0,0 +1,63 @@
package main
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"log"
"os"
. "planner/models"
)
func bootstrapDatabase() *gorm.DB {
fi, err := os.Stat("./db.sqlite")
var db *gorm.DB
if err != nil {
if os.IsNotExist(err) {
db, err := gorm.Open(sqlite.Open("./db.sqlite"), &gorm.Config{})
if err != nil {
log.Fatal(err)
return nil
}
db.AutoMigrate(&User{}, &Plan{}, &Poll{}, &Vote{})
//var tables = [...]struct {
// key string
// query string
//}{
// {key: "users", query: "CREATE TABLE users(username STRING PRIMARY KEY, password STRING)"},
// {key: "plans", query: "CREATE TABLE plans(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, owner STRING, FOREIGN KEY(owner) REFERENCES users(username))"},
// {key: "plan_user_relations", query: "CREATE TABLE plan_user_relations(username STRING, plan INTEGER, PRIMARY KEY(username, plan), FOREIGN KEY username REFERENCES user(username), FOREIGN KEY plan REFERENCES plans(id))"},
// {key: "polls", query: "CREATE TABLE polls(id INTEGER PRIMARY KEY AUTOINCREMENT, plan INTEGER, name STRING, options JSON, FOREIGN KEY plan REFERENCES plans(id))"},
// {key: "votes", query: "CREATE TABLE votes(id INTEGER, poll INTEGER, user STRING, value JSON, FOREIGN KEY poll REFERENCES polls(id), FOREIGN KEY user REFERENCES user(username))"},
//}
//for _, table := range tables {
// _, err = db.Exec(table.query)
// if err != nil {
// log.Fatal("Failed to create " + table.key + " table")
// log.Fatal(err)
// return false
// }
//}
return db
} else {
log.Fatal(err)
return nil
}
}
if !fi.Mode().IsRegular() {
log.Fatal("File is not regular file")
return nil
}
db, err = gorm.Open(sqlite.Open("./db.sqlite"), &gorm.Config{})
if err != nil {
log.Fatal(err)
return nil
}
return db
}

9
errors/errors.go Normal file
View file

@ -0,0 +1,9 @@
package errors
import "errors"
var (
ErrNotMember = errors.New("User is not member of plan")
ErrNotFound = errors.New("Resource is not found")
ErrInvalidOption = errors.New("Option is not a valid one for this poll")
)

26
extract.go Normal file
View file

@ -0,0 +1,26 @@
package main
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"planner/models"
)
func extract_user(orm *gorm.DB, c *gin.Context) *models.User {
username, _, ok := c.Request.BasicAuth()
if !ok {
c.Status(http.StatusUnauthorized)
return nil
}
u := models.User{
Username: username,
}
result := orm.Take(&u)
if result.Error != nil {
c.String(http.StatusNotFound, "Unable to find user "+username)
return nil
}
return &u
}

38
go.mod Normal file
View file

@ -0,0 +1,38 @@
module planner
go 1.23.0
require (
github.com/bytedance/sonic v1.12.2 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
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
github.com/goccy/go-json v0.10.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
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
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.10.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
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
)

87
go.sum Normal file
View file

@ -0,0 +1,87 @@
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
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/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=
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/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=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
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/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=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
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-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
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/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=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
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/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=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
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=

34
models/models.go Normal file
View file

@ -0,0 +1,34 @@
package models
type User struct {
Username string `gorm:"primaryKey" json:"username"`
Password string `json:"password"`
OwnedPlans []Plan `gorm:"foreignKey:Owner;references:Username" json:"-"`
MemberPlans []Plan `gorm:"many2many:user_plans;" json:"-"`
Votes []Vote `gorm:"foreignKey:UsernameID" json:"-"`
}
// CREATE TABLE plans(id INTEGER PRIMARY KEY AUTOINCREMENT, name STRING, owner STRING, FOREIGN KEY(owner) REFERENCES users(username))
// CREATE TABLE plan_user_relations(username STRING, plan INTEGER, PRIMARY KEY(username, plan), FOREIGN KEY username REFERENCES user(username), FOREIGN KEY plan REFERENCES plans(id))
type Plan struct {
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
Name string `json:"name"`
Owner string `json:"owner"`
Members []User `gorm:"many2many:user_plans;" json:"-"`
Polls []Poll `gorm:"foreignKey:PlanID;references:ID" json:"-"`
}
// CREATE TABLE polls(id INTEGER PRIMARY KEY AUTOINCREMENT, plan INTEGER, name STRING, options JSON, FOREIGN KEY plan REFERENCES plans(id))
type Poll struct {
ID uint `gorm:"primaryKey;autoIncrement:true" json:"id"`
PlanID uint `json:"-"`
Options string `json:"options"`
Votes []Vote `gorm:"foreignKey:PollID;references:ID" json:"-"`
}
// CREATE TABLE votes(id INTEGER, poll INTEGER, user STRING, value JSON, FOREIGN KEY poll REFERENCES polls(id), FOREIGN KEY user REFERENCES user(username))
type Vote struct {
PollID uint `gorm:"primaryKey" json:"-"`
UsernameID string `gorm:"primaryKey" json:"username_id"`
Value string `json:"value"`
}

52
models/plans.go Normal file
View file

@ -0,0 +1,52 @@
package models
import (
"errors"
"gorm.io/gorm"
. "planner/errors"
)
func GetPlan(orm *gorm.DB, user User, id uint) (*Plan, error) {
var plan Plan = Plan{
ID: id,
}
result := orm.Take(&plan)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, ErrNotFound
} else if result.Error != nil {
return nil, result.Error
}
if plan.Owner == user.Username {
return &plan, nil
}
isMember, err := plan.IsMember(orm, &user)
if !isMember || err != nil {
return nil, err
}
return &plan, nil
}
func (p *Plan) GetAllUsers(orm *gorm.DB) ([]User, error) {
var users []User
err := orm.Model(p).Association("Members").Find(&users)
return users, err
}
func (p *Plan) IsMember(orm *gorm.DB, u *User) (bool, error) {
var user User
result := orm.
Table("users u").
Select("u.*").
Joins("JOIN user_plans up on u.username=up.user_username AND up.plan_id = ?", p.ID).
Where("u.username = ?", u.Username).
Take(&user).Error
if errors.Is(result, gorm.ErrRecordNotFound) {
return false, ErrNotMember
}
return result == nil, result
}

44
models/polls.go Normal file
View file

@ -0,0 +1,44 @@
package models
import (
"fmt"
"strings"
"gorm.io/gorm"
"planner/errors"
)
func GetPoll(orm *gorm.DB, user User, id uint) (*Poll, error) {
var poll Poll = Poll{
ID: id,
}
if result := orm.Take(&poll); result.Error != nil {
return nil, result.Error
}
fmt.Printf("%+v\n", poll.PlanID)
return &poll, nil
}
func (p *Poll) SetVote(orm *gorm.DB, user User, option string) error {
found := false
options := strings.Split(p.Options, ",")
for _, opt := range options {
if opt == option {
found = true
break
}
}
if !found {
return errors.ErrInvalidOption
}
if res := orm.Create(Vote{PollID: p.ID, UsernameID: user.Username, Value: option}); res.Error != nil {
return res.Error
}
return nil
}

11
models/users.go Normal file
View file

@ -0,0 +1,11 @@
package models
import (
"gorm.io/gorm"
)
func (u *User) GetPlans(orm *gorm.DB) ([]Plan, error) {
var plans []Plan
err := orm.Model(u).Association("MemberPlans").Find(&plans)
return plans, err
}

304
planner.go Normal file
View file

@ -0,0 +1,304 @@
package main
import (
"errors"
"fmt"
"log"
"net/http"
. "planner/errors"
"planner/models"
. "planner/models"
"github.com/gin-gonic/gin"
_ "github.com/mattn/go-sqlite3"
)
func main() {
fmt.Println("Opening database db.sqlite")
orm := bootstrapDatabase()
if orm == nil {
log.Fatal("Failed to init database")
return
}
db, _ := orm.DB()
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("/users", 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("/users", 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.POST("/plans", func(c *gin.Context) {
username, _, ok := c.Request.BasicAuth()
if !ok {
c.Status(http.StatusUnauthorized)
return
}
u := User{
Username: username,
}
if result := orm.Take(&u); result.Error != nil {
c.String(http.StatusNotFound, "Unable to find user "+username)
return
}
var plan_req struct {
Name string `json:"name"`
}
c.Bind(&plan_req)
var plan Plan = Plan{
Name: plan_req.Name,
Owner: u.Username,
Members: []User{
{Username: u.Username},
},
}
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 := extract_user(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 := extract_user(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.POST("/plans/:id/polls", func(c *gin.Context) {
user := extract_user(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 := extract_user(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 := extract_user(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 := extract_user(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 := extract_user(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()
}