Init project with vote module
This commit is contained in:
commit
98f3c2aedc
11 changed files with 671 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
ui/node_modules
|
||||||
|
planner
|
||||||
|
db.sqlite
|
||||||
63
db.go
Normal file
63
db.go
Normal 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
9
errors/errors.go
Normal 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
26
extract.go
Normal 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
38
go.mod
Normal 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
87
go.sum
Normal 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
34
models/models.go
Normal 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
52
models/plans.go
Normal 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
44
models/polls.go
Normal 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
11
models/users.go
Normal 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
304
planner.go
Normal 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(¶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.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(¶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 := extract_user(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 := extract_user(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 := extract_user(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 := 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()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue