diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ecf0e4b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:latest + +LABEL maintainer="Gustavo H. M. Silva " + +WORKDIR /app + +COPY go.mod . +COPY go.sum . +RUN go mod download +COPY . . + +ENV APP_PORT 8080 +ENV APP_IDLE_TIMEOUT 120 +ENV APP_READ_TIMEOUT 5 +ENV APP_WRITE_TIMEOUT 5 + +ENV DB_HOST localhost +ENV DB_USER postgres +ENV DB_PASSWORD postgres +ENV DB_DATABASE collabyt +ENV DB_PORT 5432 +ENV DB_SSL disable +ENV DB_SOURCE postgres + +ENV CACHE_TTL 60 +ENV CACHE_HOST localhost +ENV CACHE_PORT 6379 +ENV CACHE_PASSWORD "" + +RUN go build +CMD ["./Backend"] \ No newline at end of file diff --git a/cache/config.go b/cache/config.go new file mode 100644 index 0000000..497dde7 --- /dev/null +++ b/cache/config.go @@ -0,0 +1,11 @@ +package cache + +import "os" + +var ( + cacheHost = os.Getenv("CACHE_HOST") + cachePort = os.Getenv("CACHE_PORT") + cachePassword = os.Getenv("CACHE_PASSWORD") + cacheDB = 0 + CacheTTL = 0 +) diff --git a/cache/connect.go b/cache/connect.go new file mode 100644 index 0000000..6cb0447 --- /dev/null +++ b/cache/connect.go @@ -0,0 +1,42 @@ +package cache + +import ( + "context" + "fmt" + "os" + "strconv" + + "github.com/go-redis/redis/v8" +) + +var ( + // Cache is the REDIS document database + Cache *redis.Client +) + +// Connect will initiate a connection with the 0 DB on the REDIS server +func Connect() { + ttl, err := loadTTL() + if err != nil { + panic(err) + } + CacheTTL = ttl + rOptions := &redis.Options{ + Addr: fmt.Sprintf("%s:%s", cacheHost, cachePort), + Password: cachePassword, + DB: 0, + } + rClient := redis.NewClient(rOptions) + ctx := context.Background() + _, err = rClient.Ping(ctx).Result() + if err != nil { + panic(err) + } + _ = rClient.FlushDB(ctx).Err() + Cache = rClient +} + +func loadTTL() (int, error) { + ttl, err := strconv.Atoi(os.Getenv("CACHE_TTL")) + return ttl, err +} diff --git a/database/config.go b/database/config.go index 5ef9e5d..9210220 100644 --- a/database/config.go +++ b/database/config.go @@ -1,19 +1,14 @@ package database +import "os" + var ( - // DbHost is the Postgres Host - DbHost = "localhost" - // DbUser is the Postgres username - DbUser = "postgres" - // DbPassword is the password to db - DbPassword = "postgres" - // DbDatabase is the name of the database - DbDatabase = "collabyt" - // DbPort is the post open in the postgres server - DbPort = "5432" + dbHost = os.Getenv("DB_HOST") + dbUser = os.Getenv("DB_USER") + dbPassword = os.Getenv("DB_PASSWORD") + dbDatabase = os.Getenv("DB_DATABASE") + dbPort = os.Getenv("DB_PORT") - // DbSsl is the should ssl be active or not - DbSsl = "disable" - // DbSource is the Source of the database - DbSource = "postgres" + dbSsl = os.Getenv("DB_SSL") + dbSource = os.Getenv("DB_SOURCE") ) diff --git a/database/connect.go b/database/connect.go index d6ba9a3..7a50060 100644 --- a/database/connect.go +++ b/database/connect.go @@ -9,8 +9,8 @@ import ( ) var ( - // DB is the Database connection pool - DB *sql.DB + // Dd is the Database connection pool + Db *sql.DB ) // Connect opens a connection to the postgres database using the environment @@ -18,14 +18,14 @@ var ( func Connect() { dbInfo := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", - DbHost, - DbPort, - DbUser, - DbPassword, - DbDatabase, - DbSsl, + dbHost, + dbPort, + dbUser, + dbPassword, + dbDatabase, + dbSsl, ) - db, err := sql.Open(DbSource, dbInfo) + db, err := sql.Open(dbSource, dbInfo) if err != nil { panic(err) } @@ -33,5 +33,5 @@ func Connect() { if err != nil { panic(err) } - DB = db + Db = db } diff --git a/go.mod b/go.mod index 0af83a8..6939ead 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,12 @@ module github.com/collabyt/Backend go 1.14 require ( + github.com/go-redis/redis/v8 v8.0.0 + github.com/go-redis/redis_rate/v9 v9.0.2 + github.com/google/go-cmp v0.5.2 // indirect github.com/gorilla/mux v1.7.4 github.com/lib/pq v1.3.0 + github.com/onsi/ginkgo v1.14.2 // indirect + github.com/onsi/gomega v1.10.3 // indirect golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e ) diff --git a/go.sum b/go.sum index aefe264..c05a431 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,124 @@ +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-redis/redis/v8 v8.0.0 h1:PC0VsF9sFFd2sko5bu30aEFc8F1TKl6n65o0b8FnCIE= +github.com/go-redis/redis/v8 v8.0.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo= +github.com/go-redis/redis_rate/v9 v9.0.2 h1:Uwj0zTwwODzmL5E3UeN78jnyFxN41itYgkkCfqNYc7w= +github.com/go-redis/redis_rate/v9 v9.0.2/go.mod h1:Vrl8qDpRb7bHcLyr7OXVtKKww4bD8dLL9gmUdA6XClg= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +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/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.opentelemetry.io/otel v0.11.0 h1:IN2tzQa9Gc4ZVKnTaMbPVcHjvzOdg5n9QfnmlqiET7E= +go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925 h1:5XVKs2rlCg8EFyRcvO8/XFwYxh1oKJO1Q3X5vttIf9c= +golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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 v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handler/cookies.go b/handler/cookies.go new file mode 100644 index 0000000..94524bc --- /dev/null +++ b/handler/cookies.go @@ -0,0 +1,72 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/collabyt/Backend/database" + "github.com/collabyt/Backend/model" + "golang.org/x/crypto/bcrypt" +) + +func hasCookie(cook *http.Cookie) ([]byte, error) { + s, err := model.GetSessionBySessionID(database.Db, cook.Value) + if err != nil { + return []byte{}, err + } + var es model.Session + if s == es { + cook.MaxAge = -1 + } + playlist, err := model.GetPlaylistByPublicID(database.Db, cook.Name) + if err != nil { + return []byte{}, err + } + jp, _ := json.Marshal(playlist) + return jp, nil +} + +func noCookie(a model.Auth) (http.Cookie, int, error) { + if a.PublicID == "" { + return http.Cookie{}, + http.StatusUnauthorized, + fmt.Errorf("Invalid Public ID") + } + ps, err := model.GetPlaylistByPublicID(database.Db, a.PublicID) + if err != nil { + return http.Cookie{}, + http.StatusUnauthorized, err + } + err = bcrypt.CompareHashAndPassword( + []byte(ps.Passphrase), + []byte(a.Passphrase), + ) + if err != nil { + return http.Cookie{}, + http.StatusUnauthorized, + fmt.Errorf("Wrong password, access denied") + } + s, err := generateNewSession(12, ps.ID) + if err != nil { + return http.Cookie{}, + http.StatusInternalServerError, + fmt.Errorf("Something wrong happened") + } + + err = model.CreateSession(database.Db, s) + if err != nil { + return http.Cookie{}, + http.StatusInternalServerError, + fmt.Errorf("Could not create session in the database") + } + nc := http.Cookie{ + Name: a.PublicID, + Value: s.SessionID, + Expires: time.Now().Add(time.Hour * 360), + } + return nc, + http.StatusOK, + nil +} diff --git a/handler/createkeyword.go b/handler/createkeyword.go index 9c89d61..9a0fbf2 100644 --- a/handler/createkeyword.go +++ b/handler/createkeyword.go @@ -19,15 +19,12 @@ func CreateKeyword(w http.ResponseWriter, r *http.Request) { return } if len(word.Word) < 2 { - WriteErrorReply( - w, - http.StatusBadRequest, - ) + WriteErrorReply(w, http.StatusBadRequest) return } - word, err = model.CreateKeyword(database.DB, word.Word) + word, err = model.CreateKeyword(database.Db, word.Word) if err != nil { - WriteErrorReply(w, http.StatusBadRequest) + WriteErrorReply(w, http.StatusConflict) return } jsonResponse, _ := json.Marshal(word) diff --git a/handler/createplaylist.go b/handler/createplaylist.go index 43a9113..7667b1d 100644 --- a/handler/createplaylist.go +++ b/handler/createplaylist.go @@ -19,7 +19,7 @@ func CreatePlaylist(w http.ResponseWriter, r *http.Request) { WriteErrorReply(w, http.StatusBadRequest) return } - playlist, err = model.CreatePlaylist(database.DB, playlist) + playlist, err = model.CreatePlaylist(database.Db, playlist) if err != nil { WriteErrorReply(w, http.StatusBadRequest) return diff --git a/handler/createvideoinplaylist.go b/handler/createvideoinplaylist.go index bce5f30..af0c996 100644 --- a/handler/createvideoinplaylist.go +++ b/handler/createvideoinplaylist.go @@ -17,22 +17,19 @@ func CreateVideoInPlaylist(w http.ResponseWriter, r *http.Request) { WriteErrorReply(w, http.StatusBadRequest) return } - playlist, err := fetchPlaylist(database.DB, publicID) + playlist, err := fetchPlaylist(database.Db, publicID) if err != nil { WriteErrorReply(w, http.StatusNotFound) return } if !playlist.IsPublic { - ok, err := validateSession(database.DB, r, playlist) + ok, err := validateSession(database.Db, r, playlist) if err != nil { WriteErrorReply(w, http.StatusForbidden) return } if !ok { - WriteErrorReply( - w, - http.StatusInternalServerError, - ) + WriteErrorReply(w, http.StatusInternalServerError) return } } @@ -43,15 +40,12 @@ func CreateVideoInPlaylist(w http.ResponseWriter, r *http.Request) { return } video.PlaylistID = playlist.ID - video, ok := model.CreateVideoInPlaylist(database.DB, video) + video, ok := model.CreateVideoInPlaylist(database.Db, video) if !ok { - WriteErrorReply( - w, - http.StatusInternalServerError, - ) + WriteErrorReply(w, http.StatusInternalServerError) return } - np, err := model.GetPlaylistByPublicID(database.DB, playlist.PublicID) + np, err := model.GetPlaylistByPublicID(database.Db, playlist.PublicID) np.Passphrase = "" jsonResponse, _ := json.Marshal(np) w.Write(jsonResponse) diff --git a/handler/deauthorizetoplaylist.go b/handler/deauthorizetoplaylist.go index edad009..7a57886 100644 --- a/handler/deauthorizetoplaylist.go +++ b/handler/deauthorizetoplaylist.go @@ -21,7 +21,7 @@ func DeauthorizeToPlaylist(w http.ResponseWriter, r *http.Request) { } cook.MaxAge = -1 http.SetCookie(w, cook) - err = model.DeleteSessionBySessionID(database.DB, cook.Value) + err = model.DeleteSessionBySessionID(database.Db, cook.Value) if err != nil { WriteErrorReply(w, http.StatusInternalServerError) return diff --git a/handler/deletevideo.go b/handler/deletevideo.go index a1ae32f..6462679 100644 --- a/handler/deletevideo.go +++ b/handler/deletevideo.go @@ -17,12 +17,9 @@ func DeleteVideo(w http.ResponseWriter, r *http.Request) { if err != nil { WriteErrorReply(w, http.StatusBadRequest) } - playlist, err := model.GetPlaylistByPublicID(database.DB, publicID) + playlist, err := model.GetPlaylistByPublicID(database.Db, publicID) if err != nil { - WriteErrorReply( - w, - http.StatusNotFound, - ) + WriteErrorReply(w, http.StatusNotFound) return } videoID, err := fetchVars(r, "VideoID") @@ -33,20 +30,19 @@ func DeleteVideo(w http.ResponseWriter, r *http.Request) { v.PlaylistID = playlist.ID v.ID, err = strconv.Atoi(videoID) if err != nil { - WriteErrorReply( - w, - http.StatusBadRequest, - ) + WriteErrorReply(w, http.StatusBadRequest) return } - ok := model.DeleteVideo(database.DB, v) + ok := model.DeleteVideo(database.Db, v) if !ok { - WriteErrorReply( - w, - http.StatusInternalServerError, - ) + WriteErrorReply(w, http.StatusInternalServerError) return } - http.Redirect(w, r, fmt.Sprintf("/api/v1/playlists/%s", playlist.PublicID), http.StatusSeeOther) + http.Redirect( + w, + r, + fmt.Sprintf("/api/v1/playlists/%s", playlist.PublicID), + http.StatusSeeOther, + ) return } diff --git a/handler/fetchplaylist.go b/handler/fetchplaylist.go index 0743c56..57640b1 100644 --- a/handler/fetchplaylist.go +++ b/handler/fetchplaylist.go @@ -10,7 +10,7 @@ import ( func fetchPlaylist(db *sql.DB, publicID string) (model.Playlist, error) { ps, err := model.GetPlaylistByPublicID(db, publicID) if err != nil { - return model.Playlist{}, fmt.Errorf("Could not find a playlist with that ID") + return model.Playlist{}, fmt.Errorf("Could not find playlist") } return ps, nil } diff --git a/handler/generatenewsession.go b/handler/generatenewsession.go new file mode 100644 index 0000000..65d5e23 --- /dev/null +++ b/handler/generatenewsession.go @@ -0,0 +1,21 @@ +package handler + +import ( + "crypto/rand" + "encoding/base64" + + "github.com/collabyt/Backend/model" +) + +func generateNewSession(size int, id int) (model.Session, error) { + randomBytes := make([]byte, size) + _, err := rand.Read(randomBytes) + var es model.Session + if err != nil { + return es, err + } + return model.Session{ + PlaylistID: id, + SessionID: base64.URLEncoding.EncodeToString(randomBytes), + }, nil +} diff --git a/handler/getkeywords.go b/handler/getkeywords.go index 810d1b4..a1a250a 100644 --- a/handler/getkeywords.go +++ b/handler/getkeywords.go @@ -14,13 +14,10 @@ func GetKeywords(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") addressParams := r.URL.Query() if len(addressParams["likewise"]) < 1 || len(addressParams["likewise"][0]) < 2 { - WriteErrorReply( - w, - http.StatusBadRequest, - ) + WriteErrorReply(w, http.StatusBadRequest) return } - wordList, err := model.GetKeywordsByPartialWord(database.DB, addressParams["likewise"][0]) + wordList, err := model.GetKeywordsByPartialWord(database.Db, addressParams["likewise"][0]) if err != nil { WriteErrorReply(w, http.StatusBadRequest) return diff --git a/handler/getplaylistbypublicid.go b/handler/getplaylistbypublicid.go index 57d550d..cdcbc5e 100644 --- a/handler/getplaylistbypublicid.go +++ b/handler/getplaylistbypublicid.go @@ -15,12 +15,9 @@ func GetPlaylistByPublicID(w http.ResponseWriter, r *http.Request) { if err != nil { WriteErrorReply(w, http.StatusBadRequest) } - playlist, err := model.GetPlaylistByPublicID(database.DB, publicID) + playlist, err := model.GetPlaylistByPublicID(database.Db, publicID) if !playlist.IsPublic { - WriteErrorReply( - w, - http.StatusForbidden, - ) + WriteErrorReply(w, http.StatusForbidden) return } if err != nil { diff --git a/handler/getpublicplaylists.go b/handler/getpublicplaylists.go index 6e0a0cc..610435c 100644 --- a/handler/getpublicplaylists.go +++ b/handler/getpublicplaylists.go @@ -22,10 +22,7 @@ func GetPublicPlaylists(w http.ResponseWriter, r *http.Request) { } } if limit > 25 { - WriteErrorReply( - w, - http.StatusBadRequest, - ) + WriteErrorReply(w, http.StatusBadRequest) return } offsetSlc, ok := r.URL.Query()["offset"] @@ -37,7 +34,7 @@ func GetPublicPlaylists(w http.ResponseWriter, r *http.Request) { offset = 0 } } - ps, err := model.GetPublicPlaylistsByLimitAndOffset(database.DB, limit, offset) + ps, err := model.GetPublicPlaylistsByLimitAndOffset(database.Db, limit, offset) if err != nil { WriteErrorReply(w, http.StatusInternalServerError) return diff --git a/handler/requestaccesstoplaylist.go b/handler/requestaccesstoplaylist.go index 78eafff..11f706a 100644 --- a/handler/requestaccesstoplaylist.go +++ b/handler/requestaccesstoplaylist.go @@ -1,16 +1,11 @@ package handler import ( - "crypto/rand" - "encoding/base64" "encoding/json" - "fmt" "net/http" - "time" "github.com/collabyt/Backend/database" "github.com/collabyt/Backend/model" - "golang.org/x/crypto/bcrypt" ) // RequestAccessToPlaylist authorize or deny access to a given playlist. @@ -34,7 +29,7 @@ func RequestAccessToPlaylist(w http.ResponseWriter, r *http.Request) { return } http.SetCookie(w, &newCookie) - playlist, _ := model.GetPlaylistByPublicID(database.DB, newCookie.Name) + playlist, _ := model.GetPlaylistByPublicID(database.Db, newCookie.Name) playlist.Passphrase = "" jasonPlaylist, _ := json.Marshal(playlist) w.Write(jasonPlaylist) @@ -46,76 +41,3 @@ func RequestAccessToPlaylist(w http.ResponseWriter, r *http.Request) { } w.Write(jsonPlaylist) } - -func hasCookie(cook *http.Cookie) ([]byte, error) { - s, err := model.GetSessionBySessionID(database.DB, cook.Value) - if err != nil { - return []byte{}, err - } - var es model.Session - if s == es { - cook.MaxAge = -1 - } - playlist, err := model.GetPlaylistByPublicID(database.DB, cook.Name) - if err != nil { - return []byte{}, err - } - jp, _ := json.Marshal(playlist) - return jp, nil -} - -func noCookie(a model.Auth) (http.Cookie, int, error) { - if a.PublicID == "" { - return http.Cookie{}, - http.StatusUnauthorized, - fmt.Errorf("Invalid Public ID") - } - ps, err := model.GetPlaylistByPublicID(database.DB, a.PublicID) - if err != nil { - return http.Cookie{}, - http.StatusUnauthorized, err - } - err = bcrypt.CompareHashAndPassword( - []byte(ps.Passphrase), - []byte(a.Passphrase), - ) - if err != nil { - return http.Cookie{}, - http.StatusUnauthorized, - fmt.Errorf("Wrong password, access denied") - } - s, err := generateNewSession(12, ps.ID) - if err != nil { - return http.Cookie{}, - http.StatusInternalServerError, - fmt.Errorf("Something wrong happened") - } - - err = model.CreateSession(database.DB, s) - if err != nil { - return http.Cookie{}, - http.StatusInternalServerError, - fmt.Errorf("Could not create session in the database") - } - nc := http.Cookie{ - Name: a.PublicID, - Value: s.SessionID, - Expires: time.Now().Add(time.Hour * 360), - } - return nc, - http.StatusOK, - nil -} - -func generateNewSession(size int, id int) (model.Session, error) { - randomBytes := make([]byte, size) - _, err := rand.Read(randomBytes) - var es model.Session - if err != nil { - return es, err - } - return model.Session{ - PlaylistID: id, - SessionID: base64.URLEncoding.EncodeToString(randomBytes), - }, nil -} diff --git a/handler/errorstdtreatment.go b/handler/writeerrorreply.go similarity index 78% rename from handler/errorstdtreatment.go rename to handler/writeerrorreply.go index 172be52..cf6bb0a 100644 --- a/handler/errorstdtreatment.go +++ b/handler/writeerrorreply.go @@ -12,7 +12,10 @@ import ( func WriteErrorReply(w http.ResponseWriter, httpCode int) { w.WriteHeader(httpCode) errRet, _ := json.Marshal( - model.Error{Description: http.StatusText(httpCode)}, + model.Error{ + ErrorCode: httpCode, + Description: http.StatusText(httpCode), + }, ) w.Write(errRet) } diff --git a/limiter/limiter.go b/limiter/limiter.go index 2a66b07..2ffe03a 100644 --- a/limiter/limiter.go +++ b/limiter/limiter.go @@ -1,57 +1,19 @@ package limiter import ( + "fmt" "net" "net/http" - "sync" - "time" - - "golang.org/x/time/rate" + "github.com/collabyt/Backend/cache" "github.com/collabyt/Backend/handler" + "github.com/go-redis/redis/v8" + "github.com/go-redis/redis_rate/v9" ) -// Visitor is a instance of a user accessing the API -type Visitor struct { - limiter *rate.Limiter - lastSeen time.Time -} - -// Catalog represent a list of visitor and a Mutex -type Catalog struct { - visitors map[string]*Visitor - mu sync.RWMutex -} - -const eraseTimeLimit = 60 - -// NewCatalog initialize the visitors map inside the type Catalog -func NewCatalog(size int) *Catalog { - var cat Catalog - cat.visitors = make(map[string]*Visitor) - return &cat -} - -func getVisitor(cat *Catalog, ip string) *rate.Limiter { - cat.mu.RLock() //read - v, exist := cat.visitors[ip] - cat.mu.RUnlock() - if !exist { - limiter := rate.NewLimiter(1, 3) - cat.mu.Lock() //read - cat.visitors[ip] = &Visitor{limiter, time.Now()} - cat.mu.Unlock() - return limiter - } - cat.mu.Lock() //write - v.lastSeen = time.Now() - cat.mu.Unlock() - return v.limiter -} - // Limit cap the amount of requests possible for a single IP in a given period // of time -func Limit(cat *Catalog, next http.Handler) http.Handler { +func Limit(rClient *redis.Client, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") ip, _, err := net.SplitHostPort(r.RemoteAddr) @@ -59,26 +21,19 @@ func Limit(cat *Catalog, next http.Handler) http.Handler { handler.WriteErrorReply(w, http.StatusInternalServerError) return } - limiter := getVisitor(cat, ip) - if !limiter.Allow() { + ctx := r.Context() + limiter := redis_rate.NewLimiter(cache.Cache) + rRet, err := limiter.Allow(ctx, ip, redis_rate.PerMinute(cache.CacheTTL)) + if err != nil { + handler.WriteErrorReply(w, http.StatusInternalServerError) + return + + } + if rRet.Allowed == 0 { handler.WriteErrorReply(w, http.StatusTooManyRequests) return } + fmt.Println("allowed", rRet.Allowed, "remaining", rRet.Remaining) next.ServeHTTP(w, r) }) } - -// CleanupVisitors remove unused IP's from the visitors -func CleanupVisitors(cat *Catalog) { - for { - time.Sleep(time.Minute) - - cat.mu.Lock() - for ip, v := range cat.visitors { - if time.Since(v.lastSeen) > time.Hour { - delete(cat.visitors, ip) - } - } - cat.mu.Unlock() - } -} diff --git a/main.go b/main.go index bd57d9e..667d6d3 100644 --- a/main.go +++ b/main.go @@ -3,47 +3,43 @@ package main import ( "net/http" - "time" - + "github.com/collabyt/Backend/cache" "github.com/collabyt/Backend/database" "github.com/collabyt/Backend/handler" - "github.com/collabyt/Backend/limiter" + "github.com/collabyt/Backend/startup" "github.com/gorilla/mux" ) func main() { // Database connection pool database.Connect() + // Document database connection client + cache.Connect() // API routes r := mux.NewRouter() // Playlist operations - r.HandleFunc("/api/v1/playlists", handler.CreatePlaylist).Methods("POST") // DONE - r.HandleFunc("/api/v1/playlists/{PublicID}", handler.GetPlaylistByPublicID).Methods("GET") // DONE - r.HandleFunc("/api/v1/playlists", handler.GetPublicPlaylists).Methods("GET") // DONE + r.HandleFunc("/api/v1/playlists", handler.CreatePlaylist).Methods("POST") + r.HandleFunc("/api/v1/playlists/{PublicID}", handler.GetPlaylistByPublicID).Methods("GET") + r.HandleFunc("/api/v1/playlists", handler.GetPublicPlaylists).Methods("GET") // Private Playlist operations - r.HandleFunc("/api/v1/auth/{PublicID}", handler.RequestAccessToPlaylist).Methods("POST") // DONE - r.HandleFunc("/api/v1/exit/{PublicID}", handler.DeauthorizeToPlaylist).Methods("GET") // DONE + r.HandleFunc("/api/v1/auth/{PublicID}", handler.RequestAccessToPlaylist).Methods("POST") + r.HandleFunc("/api/v1/exit/{PublicID}", handler.DeauthorizeToPlaylist).Methods("GET") // Keyword operations - r.HandleFunc("/api/v1/keywords", handler.CreateKeyword).Methods("POST") // DONE - r.HandleFunc("/api/v1/keywords/", handler.GetKeywords).Methods("GET") // DONE + r.HandleFunc("/api/v1/keywords", handler.CreateKeyword).Methods("POST") + r.HandleFunc("/api/v1/keywords", handler.GetKeywords).Methods("GET") // Video operations - r.HandleFunc("/api/v1/playlists/{PublicID}/videos", handler.CreateVideoInPlaylist).Methods("POST") // DONE - r.HandleFunc("/api/v1/playlists/{PublicID}/videos/{VideoID}", handler.DeleteVideo).Methods("DELETE") // DONE + r.HandleFunc("/api/v1/playlists/{PublicID}/videos", handler.CreateVideoInPlaylist).Methods("POST") + r.HandleFunc("/api/v1/playlists/{PublicID}/videos/{VideoID}", handler.DeleteVideo).Methods("DELETE") fs := http.FileServer(http.Dir("./static")) http.Handle("/", fs) - visitors := limiter.NewCatalog(256) - go limiter.CleanupVisitors(visitors) - server := http.Server{ - Addr: ":8080", - Handler: limiter.Limit(visitors, r), - IdleTimeout: 120 * time.Second, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, + server, err := startup.SetupServer(r) + if err != nil { + panic(err) } - err := server.ListenAndServe() + err = server.ListenAndServe() if err != nil { panic(err) } diff --git a/model/error.go b/model/error.go index cd9af65..cf29a12 100644 --- a/model/error.go +++ b/model/error.go @@ -3,5 +3,6 @@ package model // Error represent a instance of a error that ocurred at some point in the // application. type Error struct { - Description string `json:"Description"` + ErrorCode int `json:"code"` + Description string `json:"description"` } diff --git a/model/keyword.go b/model/keyword.go index 7e9b74c..d116fc9 100644 --- a/model/keyword.go +++ b/model/keyword.go @@ -14,11 +14,6 @@ type Keyword struct { //already. If exists, returns the word itself from the database. func CreateKeyword(db *sql.DB, word string) (Keyword, error) { var k Keyword - sRow := db.QueryRow(`SELECT id, word FROM keyword WHERE word = $1;`, word) - err := sRow.Scan(&k.ID, &k.Word) - if err == nil { - return k, nil - } iRow := db.QueryRow( `INSERT INTO public.keyword (word) @@ -27,11 +22,8 @@ func CreateKeyword(db *sql.DB, word string) (Keyword, error) { RETURNING id, word;`, word, ) - err = iRow.Scan(&k.ID, &k.Word) - if err != nil { - return Keyword{}, err - } - return k, nil + err := iRow.Scan(&k.ID, &k.Word) + return k, err } // GetKeywordByID returns a single keyword based in it's id. diff --git a/model/keywords.go b/model/keywords.go index 1ba8143..e493f60 100644 --- a/model/keywords.go +++ b/model/keywords.go @@ -75,6 +75,7 @@ func GetKeywordsByPlaylistID(db *sql.DB, playlistID int) ([]Keyword, error) { if err != nil { return []Keyword{}, err } + defer kRows.Close() var ks []Keyword for kRows.Next() { var k Keyword diff --git a/model/playlist_keyword.go b/model/playlist_keyword.go index b4ea22c..653b4ec 100644 --- a/model/playlist_keyword.go +++ b/model/playlist_keyword.go @@ -17,6 +17,7 @@ func CreateKeywordsRelation(db *sql.DB, playlistID int, wordList []Keyword) erro if err != nil { return err } + defer SQLStatement.Close() _, err = SQLStatement.Exec() if err != nil { return err diff --git a/model/playlists.go b/model/playlists.go index cadecc1..0b1823c 100644 --- a/model/playlists.go +++ b/model/playlists.go @@ -26,6 +26,7 @@ func GetPublicPlaylistsByLimitAndOffset(db *sql.DB, limit int, offset int) ([]Pl if err != nil { return []Playlist{}, err } + defer pRows.Close() var ps []Playlist for pRows.Next() { var p Playlist diff --git a/model/videos.go b/model/videos.go index a86c12b..38d77f9 100644 --- a/model/videos.go +++ b/model/videos.go @@ -17,6 +17,7 @@ func GetVideosByPlaylistID(db *sql.DB, playlistID int) ([]Video, error) { if err != nil { return []Video{}, err } + defer vRows.Close() var vs []Video for vRows.Next() { var v Video diff --git a/startup/startup.go b/startup/startup.go new file mode 100644 index 0000000..e9c11ff --- /dev/null +++ b/startup/startup.go @@ -0,0 +1,37 @@ +package startup + +import ( + "fmt" + "net/http" + "os" + "strconv" + "time" + + "github.com/collabyt/Backend/cache" + "github.com/collabyt/Backend/limiter" + "github.com/gorilla/mux" +) + +func SetupServer(r *mux.Router) (http.Server, error) { + address := os.Getenv("APP_PORT") + handler := limiter.Limit(cache.Cache, r) + idleTimeout, err := strconv.Atoi(os.Getenv("APP_IDLE_TIMEOUT")) + if err != nil { + return http.Server{}, fmt.Errorf("server stup faile: impossible to get %s from system's parameters", "APP_IDLE_TIMEOUT") + } + readTimeout, err := strconv.Atoi(os.Getenv("APP_READ_TIMEOUT")) + if err != nil { + return http.Server{}, fmt.Errorf("server stup faile: impossible to get %s from system's parameters", "APP_READ_TIMEOUT") + } + writeTimeout, err := strconv.Atoi(os.Getenv("APP_WRITE_TIMEOUT")) + if err != nil { + return http.Server{}, fmt.Errorf("server stup faile: impossible to get %s from system's parameters", "APP_WRITE_TIMEOUT") + } + return http.Server{ + Addr: address, + Handler: handler, + IdleTimeout: time.Duration(idleTimeout), + ReadTimeout: time.Duration(readTimeout), + WriteTimeout: time.Duration(writeTimeout), + }, err +}