From f320274ad4ce2d0991acd402b499a0f19db20e2d Mon Sep 17 00:00:00 2001 From: Emmanuel E Date: Fri, 6 Jan 2023 20:25:17 +0500 Subject: [PATCH 01/40] Remove main branch code to put clean code here --- .env.default | 3 - .gitignore | 32 ---- Dockerfile | 12 -- README.md | 130 ------------- controllers/challengeController.go | 138 -------------- controllers/userController.go | 286 ----------------------------- db/db.go | 31 ---- docker-compose.yml | 26 --- go.mod | 28 --- go.sum | 183 ------------------ handlers/config.go | 25 --- handlers/logs.go | 15 -- handlers/passwords.go | 13 -- handlers/response.go | 137 -------------- handlers/verifyjwt.go | 64 ------- heroku.yml | 4 - main.go | 26 --- models/models.go | 87 --------- nginx/dev.conf.d/nginx.conf | 21 --- routes/routes.go | 45 ----- uploaded/.gitkeep | 0 validators/validators.go | 58 ------ 22 files changed, 1364 deletions(-) delete mode 100644 .env.default delete mode 100644 .gitignore delete mode 100644 Dockerfile delete mode 100644 README.md delete mode 100644 controllers/challengeController.go delete mode 100644 controllers/userController.go delete mode 100644 db/db.go delete mode 100644 docker-compose.yml delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 handlers/config.go delete mode 100644 handlers/logs.go delete mode 100644 handlers/passwords.go delete mode 100644 handlers/response.go delete mode 100644 handlers/verifyjwt.go delete mode 100644 heroku.yml delete mode 100644 main.go delete mode 100644 models/models.go delete mode 100644 nginx/dev.conf.d/nginx.conf delete mode 100644 routes/routes.go delete mode 100644 uploaded/.gitkeep delete mode 100644 validators/validators.go diff --git a/.env.default b/.env.default deleted file mode 100644 index 3adbee8..0000000 --- a/.env.default +++ /dev/null @@ -1,3 +0,0 @@ -PORT=8080 -MONGO_URL=mongodb://mongo:27100/db -JWT_SECRET=secret \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0f87e70..0000000 --- a/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -main -gin-bin - -.env - -# exclude everything -uploaded/* - -# exception to the rule -!uploaded/.gitkeep -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -vendor/ - -# MongoDB volume for docker -data/ - -# Jetbrains product -.idea/ - -.DS_Store diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index bf9f6ab..0000000 --- a/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:latest - -ENV GO111MODULE=on -ENV PORT=8080 -WORKDIR /app -COPY go.mod /app -COPY go.sum /app - -RUN go mod download -RUN go get github.com/githubnemo/CompileDaemon -COPY . /app -ENTRYPOINT CompileDaemon --build="go build -o main" --command=./main \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index ed1848a..0000000 --- a/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# Go-Mongodb-REST-boilerplate - - -![Build](https://github.com/gaquarius/challenge-platform-api/workflows/Go/badge.svg) - - -This repo can be used as a starting point for backend development with Golang. It comes bundled with Docker. The development environment uses `docker-compose` to start dependent services like mongo. - -A few things to note in the project: -* **[Github Actions Workflows](https://github.com/gaquarius/challenge-platform-api/tree/main/.github/workflows)** - Pre-configured Github Actions to run automated builds and publish image to Github Packages -* **[Dockerfile](https://github.com/gaquarius/challenge-platform-api/blob/main/Dockerfile)** - Dockerfile to generate docker builds. -* **[docker-compose](https://github.com/gaquarius/challenge-platform-api/blob/main/docker-compose.yml)** - Docker compose script to start service in production mode. -* **[Containerized Mongo for development](#development)** - Starts a local mongo container with data persistence across runs. -* **[Mongo Driver](https://go.mongodb.org/mongo-driver)** - MongoDB supported driver for Go. -* **[Gorilla Mux](https://go.mongodb.org/mongo-driver)** - HTTP request multiplexer. -* **[jwt-go](https://github.com/dgrijalva/jwt-go)** - Implementation of JWT Tokens. -* **[Validator](https://gopkg.in/go-playground/validator.v9)** - Package validator implements value validations for structs. - -* **[.env file for configuration](#environment)** - Change server config like app port, mongo url etc -* **[File Uploads](https://golang.org/pkg/io/)** - io package provides interfaces to I/O primitives. -* **[httptest](#testing)** - Utilities for HTTP testing. - -## Installation - -#### 1. Clone this repo - -``` -$ git clone git@github.com:umangraval/Go-Mongodb-REST-boilerplate.git your-app-name -$ cd your-app-name -``` - -#### 2. Install dependencies - -``` -$ go mod vendor -``` - -## Development - -### Start dev server -Starting the dev server also starts MongoDB as a service in a docker container using the compose script at `docker-compose.yml`. - -``` -$ go run main.go routes.go -``` -Running the above commands results in -* 🌏 **API Server** running at `http://localhost:8080` - -* ⛁ **MongoDB** running at `mongodb://localhost:27017/db` - -## Packaging and Deployment -#### 1. Build and run without Docker - -``` -$ go build -``` -#### 2. Run Tests - -``` -$ cd tests -$ go test -``` -#### 3. Run with docker - -``` -$ docker build -t api-server . -$ docker run -t -i -p 8080:8080 api-server -``` - -#### 4. Run with docker-compose - -``` -$ docker-compose up -``` - - ---- - -## Environment -To edit environment variables, create a file with name `.env` and copy the contents from `.env.default` to start with. - -| Var Name | Type | Default | Description | -|---|---|---|---| -| JWT_SECRET | string | `secret` |JWT secret to verify | -| PORT | number | `8080` | Port to run the API server on | -| MONGO_URL | string | `mongodb://localhost:27017/db` | URL for MongoDB | - - - - -### Directory Structure - -``` -+-- controllers -| +-- personController.go -+-- db -| +-- db.go -+-- handlers -| +-- config.go -| +-- logs.go -| +-- response.go -| +-- verifyJWT.go -+-- models -| +-- models.go -+-- validators -| +-- validators.go -+-- tests -| +-- api_test.go -+-- routes -| +-- routes.go -+-- uploaded -+-- vendor -+-- nginx -| +-- dev.conf.d -| | +-- nginx.conf -+-- .env -+-- .env.default -+-- .gitignore -+-- docker-compose.yml -+-- Dockerfile -+-- go.mod -+-- go.sum -+-- main.go -+-- README.md -``` \ No newline at end of file diff --git a/controllers/challengeController.go b/controllers/challengeController.go deleted file mode 100644 index 41c96de..0000000 --- a/controllers/challengeController.go +++ /dev/null @@ -1,138 +0,0 @@ -package controllers - -import ( - "context" - "encoding/json" - "log" - "net/http" - "strings" - "time" - - middlewares "github.com/gaquarius/challenge-platform-api/handlers" - "github.com/gaquarius/challenge-platform-api/models" - "github.com/gorilla/mux" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -// ListChallenge -> List all the challenges -var ListChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - var challenges []*models.Challenge - collection := client.Database("challenge").Collection("challenges") - cursor, err := collection.Find(context.TODO(), bson.D{}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - for cursor.Next(context.TODO()) { - var challenge models.Challenge - err := cursor.Decode(&challenge) - if err != nil { - log.Fatal(err) - } - - challenges = append(challenges, &challenge) - } - - if err := cursor.Err(); err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - middlewares.SuccessChallengeArrRespond(challenges, rw) -}) - -// GetChallengs -> Get challenges for specific user -var GetChallenges = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - var challenges []*models.Challenge - - collection := client.Database("challenge").Collection("challenges") - cursor, err := collection.Find(context.TODO(), bson.D{primitive.E{Key: "coordinator", Value: params["username"]}}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - for cursor.Next(context.TODO()) { - var challenge models.Challenge - err := cursor.Decode(&challenge) - if err != nil { - log.Fatal(err) - } - challenges = append(challenges, &challenge) - } - - if err := cursor.Err(); err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - middlewares.SuccessChallengeArrRespond(challenges, rw) -}) - -// CreateChallenge -> Create a challenge -var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - var challenge models.Challenge - err := json.NewDecoder(r.Body).Decode(&challenge) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - challenge.CreatedAt = time.Now() - challenge.UpdatedAt = time.Now() - collection := client.Database("challenge").Collection("challenges") - result, err := collection.InsertOne(context.TODO(), challenge) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - res, _ := json.Marshal(result.InsertedID) - middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) -}) - -// GetChallenge -> Get a challenge by id -var GetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id, _ := primitive.ObjectIDFromHex(params["id"]) - var challenge models.Challenge - - collection := client.Database("challenge").Collection("challenges") - err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - middlewares.SuccessRespond(challenge, rw) -}) - -var UpdateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id, _ := primitive.ObjectIDFromHex(params["id"]) - var challenge models.Challenge - - err := json.NewDecoder(r.Body).Decode(&challenge) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - collection := client.Database("challenge").Collection("challenges") - res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: challenge}}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - if res.MatchedCount == 0 { - middlewares.ErrorResponse("Challenge does not exist", rw) - return - } - - middlewares.SuccessResponse("Updated", rw) -}) - -var DeleteChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - -}) diff --git a/controllers/userController.go b/controllers/userController.go deleted file mode 100644 index 395206a..0000000 --- a/controllers/userController.go +++ /dev/null @@ -1,286 +0,0 @@ -package controllers - -import ( - "context" - "encoding/json" - "io" - "log" - "net/http" - "os" - "strings" - - "github.com/dgrijalva/jwt-go" - "github.com/gaquarius/challenge-platform-api/db" - middlewares "github.com/gaquarius/challenge-platform-api/handlers" - "github.com/gaquarius/challenge-platform-api/models" - "github.com/gaquarius/challenge-platform-api/validators" - "github.com/gorilla/mux" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" -) - -var client = db.Dbconnect() - -// RegisterUser -> Register User with Menmonic and username -var RegisterUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - var user models.User - err := json.NewDecoder(r.Body).Decode(&user) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - collection := client.Database("challenge").Collection("users") - var existingUser models.User - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) - if err == nil { - middlewares.ErrorResponse("Username is already taken.", rw) - return - } - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "identity", Value: user.Identity}}).Decode(&existingUser) - if err == nil { - middlewares.ErrorResponse("Identity is already in use.", rw) - return - } - passwordHash, err := middlewares.HashPassword(user.Password) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - user.Password = passwordHash - result, err := collection.InsertOne(r.Context(), user) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - res, _ := json.Marshal(result.InsertedID) - middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) -}) - -// LoginUser -> Let the user login with identity and password -var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - var user models.User - err := json.NewDecoder(r.Body).Decode(&user) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - collection := client.Database("challenge").Collection("users") - var existingUser models.User - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) - if err != nil { - middlewares.ErrorResponse("User doesn't exist", rw) - return - } - isPasswordMatch := middlewares.CheckPasswordHash(user.Password, existingUser.Password) - if !isPasswordMatch { - middlewares.ErrorResponse("Password doesn't match", rw) - return - } - token, err := middlewares.GenerateJWT(user.Username) - if err != nil { - middlewares.ErrorResponse("Failed to generate JWT", rw) - return - } - middlewares.SuccessResponse(string(token), rw) -}) - -// GetMe -> Get user details from Authorization token -var GetMe = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - props, _ := r.Context().Value("props").(jwt.MapClaims) - var user models.User - collection := client.Database("challenge").Collection("users") - err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: props["username"]}}).Decode(&user) - if err != nil { - middlewares.AuthorizationResponse("Malformed token", rw) - return - } - - user.Password = "" - middlewares.SuccessRespond(user, rw) -}) - -// GetUser -> Get user details from username -var GetUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - var user models.User - - collection := client.Database("challenge").Collection("users") - err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: params["username"]}}).Decode(&user) - if err != nil { - middlewares.ErrorResponse("User doesn't exist", rw) - return - } - - user.Password = "" - middlewares.SuccessRespond(user, rw) -}) - -// UpdateUser -> Update user details from username -var UpdateUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - props, _ := r.Context().Value("props").(jwt.MapClaims) - var user models.User - - collection := client.Database("challenge").Collection("users") - err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: props["username"]}}).Decode(&user) - if err != nil { - middlewares.AuthorizationResponse("Malformed token", rw) - return - } - - var newUser models.User - err = json.NewDecoder(r.Body).Decode(&newUser) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - - res, err := collection.UpdateOne(r.Context(), bson.D{primitive.E{Key: "_id", Value: user.ID}}, bson.D{ - primitive.E{ - Key: "$set", - Value: bson.D{ - primitive.E{Key: "username", Value: newUser.Username}, - primitive.E{Key: "bio", Value: newUser.Bio}, - }, - }, - }) - - if err != nil { - middlewares.ErrorResponse("Username is already taken.", rw) - return - } - if res.MatchedCount == 0 { - middlewares.ErrorResponse("User doesn't exist", rw) - return - } - - token, err := middlewares.GenerateJWT(newUser.Username) - if err != nil { - middlewares.ErrorResponse("Failed to generate JWT", rw) - return - } - middlewares.SuccessResponse(string(token), rw) -}) - -// CreatePersonEndpoint -> create person -var CreatePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - var person models.Person - err := json.NewDecoder(request.Body).Decode(&person) - if err != nil { - middlewares.ServerErrResponse(err.Error(), response) - return - } - if ok, errors := validators.ValidateInputs(person); !ok { - middlewares.ValidationResponse(errors, response) - return - } - collection := client.Database("golang").Collection("people") - result, err := collection.InsertOne(context.TODO(), person) - if err != nil { - middlewares.ServerErrResponse(err.Error(), response) - return - } - res, _ := json.Marshal(result.InsertedID) - middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), response) -}) - -// GetPeopleEndpoint -> get people -var GetPeopleEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - var people []*models.Person - - collection := client.Database("golang").Collection("people") - cursor, err := collection.Find(context.TODO(), bson.D{{}}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), response) - return - } - for cursor.Next(context.TODO()) { - var person models.Person - err := cursor.Decode(&person) - if err != nil { - log.Fatal(err) - } - - people = append(people, &person) - } - if err := cursor.Err(); err != nil { - middlewares.ServerErrResponse(err.Error(), response) - return - } - middlewares.SuccessArrRespond(people, response) -}) - -// GetPersonEndpoint -> get person by id -var GetPersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - params := mux.Vars(request) - id, _ := primitive.ObjectIDFromHex(params["id"]) - var person models.Person - - collection := client.Database("golang").Collection("people") - err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&person) - if err != nil { - middlewares.ErrorResponse("Person does not exist", response) - return - } - middlewares.SuccessRespond(person, response) -}) - -// DeletePersonEndpoint -> delete person by id -var DeletePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - params := mux.Vars(request) - id, _ := primitive.ObjectIDFromHex(params["id"]) - var person models.Person - - collection := client.Database("golang").Collection("people") - err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&person) - if err != nil { - middlewares.ErrorResponse("Person does not exist", response) - return - } - _, derr := collection.DeleteOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}) - if derr != nil { - middlewares.ServerErrResponse(derr.Error(), response) - return - } - middlewares.SuccessResponse("Deleted", response) -}) - -// UpdatePersonEndpoint -> update person by id -var UpdatePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - params := mux.Vars(request) - id, _ := primitive.ObjectIDFromHex(params["id"]) - type fname struct { - Firstname string `json:"firstname"` - } - var fir fname - json.NewDecoder(request.Body).Decode(&fir) - collection := client.Database("golang").Collection("people") - res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "firstname", Value: fir.Firstname}}}}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), response) - return - } - if res.MatchedCount == 0 { - middlewares.ErrorResponse("Person does not exist", response) - return - } - middlewares.SuccessResponse("Updated", response) -}) - -// UploadFileEndpoint -> upload file -var UploadFileEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { - file, handler, err := request.FormFile("file") - // fileName := request.FormValue("file_name") - if err != nil { - panic(err) - } - defer file.Close() - - f, err := os.OpenFile("uploaded/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - panic(err) - } - defer f.Close() - _, _ = io.Copy(f, file) - - middlewares.SuccessResponse("Uploaded Successfully", response) -}) diff --git a/db/db.go b/db/db.go deleted file mode 100644 index 0f9fde8..0000000 --- a/db/db.go +++ /dev/null @@ -1,31 +0,0 @@ -package db - -import ( - "context" - "log" - - "github.com/fatih/color" - middlewares "github.com/gaquarius/challenge-platform-api/handlers" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" -) - -var client *mongo.Client - -// Dbconnect -> connects mongo -func Dbconnect() *mongo.Client { - clientOptions := options.Client().ApplyURI(middlewares.DotEnvVariable("MONGO_URL")) - client, err := mongo.Connect(context.TODO(), clientOptions) - if err != nil { - log.Fatal("⛒ Connection Failed to Database") - log.Fatal(err) - } - // Check the connection - err = client.Ping(context.TODO(), nil) - if err != nil { - log.Fatal("⛒ Connection Failed to Database") - log.Fatal(err) - } - color.Green("⛁ Connected to Database") - return client -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index aa70b85..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: '3.3' -services: - api: - build: . - ports: - - '3100:8080' - volumes: - - .:/app - depends_on: - - 'mongo' - web: - container_name: 'web' - image: 'nginx:latest' - ports: - - '80:80' - volumes: - - ./nginx/dev.conf.d:/etc/nginx/conf.d - depends_on: - - 'api' - mongo: - image: 'mongo:latest' - container_name: 'mongo' - ports: - - '27100:27017' - volumes: - - ./data/dev/mongo:/data/db diff --git a/go.mod b/go.mod deleted file mode 100644 index 8bd6b94..0000000 --- a/go.mod +++ /dev/null @@ -1,28 +0,0 @@ -module github.com/gaquarius/challenge-platform-api - -// +heroku goVersion go1.16 -go 1.15 - -require ( - github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee // indirect - github.com/aws/aws-sdk-go v1.35.19 // indirect - github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 // indirect - github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/fatih/color v1.10.0 - github.com/go-playground/universal-translator v0.17.0 // indirect - github.com/golang/snappy v0.0.2 // indirect - github.com/gorilla/mux v1.8.0 - github.com/joho/godotenv v1.3.0 - github.com/klauspost/compress v1.11.2 // indirect - github.com/leodido/go-urn v1.2.0 // indirect - github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/rs/cors v1.7.0 - go.mongodb.org/mongo-driver v1.4.2 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect - golang.org/x/text v0.3.4 // indirect - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v9 v9.31.0 - gopkg.in/urfave/cli.v1 v1.20.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 0497dd4..0000000 --- a/go.sum +++ /dev/null @@ -1,183 +0,0 @@ -github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee h1:LgokYDTCpaZBHtl/oGwLxNCr3kM5Qt+Z7mInv4MqFNM= -github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go v1.35.19 h1:vdIqQnOIqTNtvnOdt9r3Bf/FiCJ7KV/7O2BIj4TPx2w= -github.com/aws/aws-sdk-go v1.35.19/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= -github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 h1:ihrIKrLQzm6Q6NJHBMemvaIGTFxgxQUEkn2AjN0Aulw= -github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4/go.mod h1:X7wHz0C25Lga6CnJ4WAQNbUQ9P/8eWSNv8qIO71YkSM= -github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 h1:ysuVNDVE4LIky6I+6JlgAKG+wBNKMpVv3m3neVpvFVw= -github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974/go.mod h1:UBYuwaH3dMw91EZ7tGVaFF6GDj5j46S7zqB9lZPIe58= -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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= -github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= -github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -go.mongodb.org/mongo-driver v1.4.2 h1:WlnEglfTg/PfPq4WXs2Vkl/5ICC6hoG8+r+LraPmGk4= -go.mongodb.org/mongo-driver v1.4.2/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -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/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -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= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= -gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= -gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/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/handlers/config.go b/handlers/config.go deleted file mode 100644 index 1d5d8e4..0000000 --- a/handlers/config.go +++ /dev/null @@ -1,25 +0,0 @@ -package middlewares - -import ( - "log" - "os" - "path" - - "github.com/joho/godotenv" -) - -// DotEnvVariable -> get .env -func DotEnvVariable(key string) string { - - // load .env file - wd, _ := os.Getwd() - log.Println(path.Join(wd, "/.env")) - - err := godotenv.Load() - - if err != nil { - log.Printf("Error loading .env file %s", err) - } - - return os.Getenv(key) -} diff --git a/handlers/logs.go b/handlers/logs.go deleted file mode 100644 index 92fee21..0000000 --- a/handlers/logs.go +++ /dev/null @@ -1,15 +0,0 @@ -package middlewares - -import ( - "net/http" - - "github.com/fatih/color" -) - -// LogRequest -> logs req info -func LogRequest(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - color.Yellow("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) - handler.ServeHTTP(w, r) - }) -} diff --git a/handlers/passwords.go b/handlers/passwords.go deleted file mode 100644 index 42bc3cd..0000000 --- a/handlers/passwords.go +++ /dev/null @@ -1,13 +0,0 @@ -package middlewares - -import "golang.org/x/crypto/bcrypt" - -func HashPassword(password string) (string, error) { - bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) - return string(bytes), err -} - -func CheckPasswordHash(password, hash string) bool { - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} diff --git a/handlers/response.go b/handlers/response.go deleted file mode 100644 index b121203..0000000 --- a/handlers/response.go +++ /dev/null @@ -1,137 +0,0 @@ -package middlewares - -import ( - "encoding/json" - "net/http" - - "github.com/gaquarius/challenge-platform-api/models" -) - -// AuthorizationResponse -> response authorize -func AuthorizationResponse(msg string, writer http.ResponseWriter) { - type errdata struct { - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &errdata{Statuscode: 401, Message: msg} - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusUnauthorized) - json.NewEncoder(writer).Encode(temp) -} - -// SuccessArrRespond -> response formatter -func SuccessArrRespond(fields []*models.Person, writer http.ResponseWriter) { - // var fields["status"] := "success" - _, err := json.Marshal(fields) - type data struct { - People []*models.Person `json:"data"` - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &data{People: fields, Statuscode: 200, Message: "success"} - if err != nil { - ServerErrResponse(err.Error(), writer) - } - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusOK) - json.NewEncoder(writer).Encode(temp) -} - -// SuccessArrRespond -> response formatter -func SuccessChallengeArrRespond(fields []*models.Challenge, writer http.ResponseWriter) { - // var fields["status"] := "success" - _, err := json.Marshal(fields) - type data struct { - Challenges []*models.Challenge `json:"data"` - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &data{Challenges: fields, Statuscode: 200, Message: "success"} - if err != nil { - ServerErrResponse(err.Error(), writer) - } - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusOK) - json.NewEncoder(writer).Encode(temp) -} - -// SuccessRespond -> response formatter -func SuccessRespond(fields interface{}, writer http.ResponseWriter) { - _, err := json.Marshal(fields) - type data struct { - Person interface{} `json:"data"` - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &data{Person: fields, Statuscode: 200, Message: "success"} - if err != nil { - ServerErrResponse(err.Error(), writer) - } - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusOK) - json.NewEncoder(writer).Encode(temp) -} - -// SuccessResponse -> success formatter -func SuccessResponse(msg string, writer http.ResponseWriter) { - type errdata struct { - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &errdata{Statuscode: 200, Message: msg} - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusOK) - json.NewEncoder(writer).Encode(temp) -} - -// ErrorResponse -> error formatter -func ErrorResponse(error string, writer http.ResponseWriter) { - type errdata struct { - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &errdata{Statuscode: 400, Message: error} - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusBadRequest) - json.NewEncoder(writer).Encode(temp) -} - -// ServerErrResponse -> server error formatter -func ServerErrResponse(error string, writer http.ResponseWriter) { - type servererrdata struct { - Statuscode int `json:"status"` - Message string `json:"msg"` - } - temp := &servererrdata{Statuscode: 500, Message: error} - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusInternalServerError) - json.NewEncoder(writer).Encode(temp) -} - -// ValidationResponse -> user input validation -func ValidationResponse(fields map[string][]string, writer http.ResponseWriter) { - //Create a new map and fill it - response := make(map[string]interface{}) - response["errors"] = fields - response["status"] = 422 - response["msg"] = "validation error" - - //Send header, status code and output to writer - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(http.StatusUnprocessableEntity) - json.NewEncoder(writer).Encode(response) -} diff --git a/handlers/verifyjwt.go b/handlers/verifyjwt.go deleted file mode 100644 index e603d9a..0000000 --- a/handlers/verifyjwt.go +++ /dev/null @@ -1,64 +0,0 @@ -package middlewares - -import ( - "context" - "fmt" - "net/http" - "strings" - "time" - - jwt "github.com/dgrijalva/jwt-go" -) - -var JWT_SECRET = []byte(DotEnvVariable("JWT_SECRET")) - -type Claims struct { - Username string `json:"username"` - jwt.StandardClaims -} - -// IsAuthorized -> verify jwt header -func IsAuthorized(next http.Handler) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - authHeader := strings.Split(r.Header.Get("Authorization"), "Bearer ") - - if len(authHeader) != 2 { - AuthorizationResponse("Malformed JWT token", w) - } else { - jwtToken := authHeader[1] - token, _ := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) - } - return JWT_SECRET, nil - }) - - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - ctx := context.WithValue(r.Context(), "props", claims) - next.ServeHTTP(w, r.WithContext(ctx)) - } else { - AuthorizationResponse("Unauthorized", w) - } - } - }) -} - -// GenerateJWT -> generate jwt -func GenerateJWT(username string) (string, error) { - claims := &Claims{ - Username: username, - StandardClaims: jwt.StandardClaims{ - ExpiresAt: time.Now().Add(2 * time.Hour).Unix(), - }, - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - - tokenString, err := token.SignedString(JWT_SECRET) - - if err != nil { - return "", err - } - - return tokenString, nil -} diff --git a/heroku.yml b/heroku.yml deleted file mode 100644 index 1a4592f..0000000 --- a/heroku.yml +++ /dev/null @@ -1,4 +0,0 @@ -build: - docker: - web: Dockerfile - \ No newline at end of file diff --git a/main.go b/main.go deleted file mode 100644 index 8592f20..0000000 --- a/main.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "log" - "net/http" - - "github.com/fatih/color" - middlewares "github.com/gaquarius/challenge-platform-api/handlers" - "github.com/gaquarius/challenge-platform-api/routes" - "github.com/rs/cors" -) - -func main() { - port := middlewares.DotEnvVariable("PORT") - color.Cyan("🌏 Server running on localhost:" + port) - - log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) - router := routes.Routes() - c := cors.New(cors.Options{ - AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, - AllowedHeaders: []string{"Content-Type", "Origin", "Accept", "*"}, - }) - - handler := c.Handler(router) - http.ListenAndServe(":"+port, middlewares.LogRequest(handler)) -} diff --git a/models/models.go b/models/models.go deleted file mode 100644 index 835f13f..0000000 --- a/models/models.go +++ /dev/null @@ -1,87 +0,0 @@ -package models - -import ( - "time" - - "go.mongodb.org/mongo-driver/bson/primitive" -) - -// Person Model -type Person struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - Firstname string `json:"firstname,omitempty" bson:"firstname,omitempty" validate:"required,alpha"` - Lastname string `json:"lastname,omitempty" bson:"lastname,omitempty" validate:"required,alpha"` -} - -// User Model -type User struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - Username string `json:"username,omitempty" bson:"username,omitempty"` - Role string `json:"role,omitempty" bson:"role,omitempty"` - Bio string `json:"bio,omitempty" bson:"bio,omitempty"` - Avatar string `json:"avatar,omitempty" bson:"avatar,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Password string `json:"password,omitempty" bson:"password,omitempty"` -} - -type ChallengeStatus string - -const ( - Open ChallengeStatus = "open" - InvitationOnly ChallengeStatus = "invites only" - Private ChallengeStatus = "private" - Draft ChallengeStatus = "draft" -) - -// Challenge Model -type Challenge struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` - EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` - Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` - Goal string `json:"goal,omitempty" bson:"goal,omitempty"` - GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` - GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` - Category []string `json:"category,omitempty" bson:"category,omitempty"` - Name string `json:"name,omitempty" bson:"name,omitempty"` - Description string `json:"description,omitempty" bson:"description,omitempty"` - Content string `json:"content,omitempty" bson:"content,omitempty"` - HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` - Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` -} - -type EscrowStatus string - -const ( - Pending EscrowStatus = "pending" - Paid EscrowStatus = "paid" -) - -type Escrow struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - Amount float32 `json:"amount,omitempty" bson:"amount,omitempty"` - Challenge primitive.ObjectID `json:"challenge,omitempty" bson:"challenge,omitempty"` - Status EscrowStatus `json:"status,omitempty" bson:"status,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` -} - -type ActivityType string - -const ( - Joined ActivityType = "joined" - Won ActivityType = "won" - Lost ActivityType = "lost" - Played ActivityType = "played" -) - -type Activity struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - Challenge primitive.ObjectID `json:"challenge,omitempty" bson:"challenge,omitempty"` - Participant string `json:"participant,omitempty" bson:"participant,omitempty"` - Type ActivityType `json:"type,omitempty" bson:"type,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` -} diff --git a/nginx/dev.conf.d/nginx.conf b/nginx/dev.conf.d/nginx.conf deleted file mode 100644 index 4513e86..0000000 --- a/nginx/dev.conf.d/nginx.conf +++ /dev/null @@ -1,21 +0,0 @@ -upstream go_server { - # will resolve to the correct address - server api:8080; -} - -# upstream frontend { -# # will resolve to the correct address -# server frontend:3000; -# } - -server { - listen 80; - server_name localhost; - - location / { - proxy_pass http://go_server; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - proxy_redirect off; - } -} \ No newline at end of file diff --git a/routes/routes.go b/routes/routes.go deleted file mode 100644 index 9d39e99..0000000 --- a/routes/routes.go +++ /dev/null @@ -1,45 +0,0 @@ -package routes - -import ( - "net/http" - - "github.com/gaquarius/challenge-platform-api/controllers" - middlewares "github.com/gaquarius/challenge-platform-api/handlers" - "github.com/gorilla/mux" -) - -// Routes -> define endpoints -func Routes() *mux.Router { - router := mux.NewRouter() - - api := router.PathPrefix("/api/v1").Subrouter() - - // User API routes - - user := api.PathPrefix("/user").Subrouter() - user.HandleFunc("/register", controllers.RegisterUser).Methods("POST") - user.HandleFunc("/login", controllers.LoginUser).Methods("POST") - user.HandleFunc("/me", middlewares.IsAuthorized(controllers.GetMe)).Methods("GET") - user.HandleFunc("/me", middlewares.IsAuthorized(controllers.UpdateUser)).Methods("PUT") - user.HandleFunc("/{username}", controllers.GetUser).Methods("GET") - - // Challenge API routes - - challenge := api.PathPrefix("/challenge").Subrouter() - challenge.HandleFunc("/", middlewares.IsAuthorized(controllers.ListChallenge)).Methods("GET") - challenge.HandleFunc("/", middlewares.IsAuthorized(controllers.CreateChallenge)).Methods("POST") - challenge.HandleFunc("/user/{username}", controllers.GetChallenges).Methods("GET") - challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.GetChallenge)).Methods("GET") - challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.UpdateChallenge)).Methods("PUT") - challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") - - api.HandleFunc("/person", controllers.CreatePersonEndpoint).Methods("POST") - api.HandleFunc("/people", middlewares.IsAuthorized(controllers.GetPeopleEndpoint)).Methods("GET") - api.HandleFunc("/person/{id}", controllers.GetPersonEndpoint).Methods("GET") - api.HandleFunc("/person/{id}", controllers.DeletePersonEndpoint).Methods("DELETE") - api.HandleFunc("/person/{id}", controllers.UpdatePersonEndpoint).Methods("PUT") - - router.HandleFunc("/upload", controllers.UploadFileEndpoint).Methods("POST") - router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./uploaded/")))) - return router -} diff --git a/uploaded/.gitkeep b/uploaded/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/validators/validators.go b/validators/validators.go deleted file mode 100644 index 7711f8e..0000000 --- a/validators/validators.go +++ /dev/null @@ -1,58 +0,0 @@ -package validators - -import ( - "reflect" - "strings" - - "gopkg.in/go-playground/validator.v9" -) - -var validate *validator.Validate - -// ValidateInputs -> sanitize user inputs -func ValidateInputs(dataSet interface{}) (bool, map[string][]string) { - validate = validator.New() - err := validate.Struct(dataSet) - - if err != nil { - - //Validation syntax is invalid - if err, ok := err.(*validator.InvalidValidationError); ok { - panic(err) - } - - //Validation errors occurred - errors := make(map[string][]string) - //Use reflector to reverse engineer struct - reflected := reflect.ValueOf(dataSet) - for _, err := range err.(validator.ValidationErrors) { - - // Attempt to find field by name and get json tag name - field, _ := reflected.Type().FieldByName(err.StructField()) - var name string - - //If json tag doesn't exist, use lower case of name - if name = field.Tag.Get("json"); name == "" { - name = strings.ToLower(err.StructField()) - } - - switch err.Tag() { - case "required": - errors[name] = append(errors[name], "The "+name+" is required") - break - case "alpha": - errors[name] = append(errors[name], "The "+name+" should contain only letters") - break - // case "eqfield": - // errors[name] = append(errors[name], "The "+name+" should be equal to the "+err.Param()) - // break - default: - errors[name] = append(errors[name], "The "+name+" is invalid") - break - } - } - - return false, errors - } - return true, nil -} From 1e3467ed7f6f0a77d9818d36c9a7c2f6f219e3e0 Mon Sep 17 00:00:00 2001 From: Emmanuel E Date: Fri, 6 Jan 2023 20:35:06 +0500 Subject: [PATCH 02/40] New fresh code added to dev branch --- .env.default | 3 + .gitignore | 32 ++++ Dockerfile | 12 ++ README.md | 130 +++++++++++++ controllers/challengeController.go | 154 +++++++++++++++ controllers/userController.go | 292 +++++++++++++++++++++++++++++ db/db.go | 32 ++++ docker-compose.yml | 26 +++ go.mod | 28 +++ go.sum | 183 ++++++++++++++++++ handlers/config.go | 25 +++ handlers/logs.go | 15 ++ handlers/passwords.go | 13 ++ handlers/response.go | 137 ++++++++++++++ handlers/verifyjwt.go | 67 +++++++ heroku.yml | 4 + main.go | 26 +++ models/models.go | 91 +++++++++ nginx/dev.conf.d/nginx.conf | 21 +++ routes/routes.go | 45 +++++ uploaded/.gitkeep | 0 validators/validators.go | 58 ++++++ 22 files changed, 1394 insertions(+) create mode 100644 .env.default create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 controllers/challengeController.go create mode 100644 controllers/userController.go create mode 100644 db/db.go create mode 100644 docker-compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handlers/config.go create mode 100644 handlers/logs.go create mode 100644 handlers/passwords.go create mode 100644 handlers/response.go create mode 100644 handlers/verifyjwt.go create mode 100644 heroku.yml create mode 100644 main.go create mode 100644 models/models.go create mode 100644 nginx/dev.conf.d/nginx.conf create mode 100644 routes/routes.go create mode 100644 uploaded/.gitkeep create mode 100644 validators/validators.go diff --git a/.env.default b/.env.default new file mode 100644 index 0000000..3adbee8 --- /dev/null +++ b/.env.default @@ -0,0 +1,3 @@ +PORT=8080 +MONGO_URL=mongodb://mongo:27100/db +JWT_SECRET=secret \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f87e70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +main +gin-bin + +.env + +# exclude everything +uploaded/* + +# exception to the rule +!uploaded/.gitkeep +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ + +# MongoDB volume for docker +data/ + +# Jetbrains product +.idea/ + +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15dcd3b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:latest + +ENV GO111MODULE=on +ENV PORT=8080 +WORKDIR /app +COPY go.mod /app +COPY go.sum /app + +RUN go mod download +RUN go install -mod=mod github.com/githubnemo/CompileDaemon +COPY . /app +ENTRYPOINT CompileDaemon --build="go build -o main" --command=./main \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed1848a --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# Go-Mongodb-REST-boilerplate + + +![Build](https://github.com/gaquarius/challenge-platform-api/workflows/Go/badge.svg) + + +This repo can be used as a starting point for backend development with Golang. It comes bundled with Docker. The development environment uses `docker-compose` to start dependent services like mongo. + +A few things to note in the project: +* **[Github Actions Workflows](https://github.com/gaquarius/challenge-platform-api/tree/main/.github/workflows)** - Pre-configured Github Actions to run automated builds and publish image to Github Packages +* **[Dockerfile](https://github.com/gaquarius/challenge-platform-api/blob/main/Dockerfile)** - Dockerfile to generate docker builds. +* **[docker-compose](https://github.com/gaquarius/challenge-platform-api/blob/main/docker-compose.yml)** - Docker compose script to start service in production mode. +* **[Containerized Mongo for development](#development)** - Starts a local mongo container with data persistence across runs. +* **[Mongo Driver](https://go.mongodb.org/mongo-driver)** - MongoDB supported driver for Go. +* **[Gorilla Mux](https://go.mongodb.org/mongo-driver)** - HTTP request multiplexer. +* **[jwt-go](https://github.com/dgrijalva/jwt-go)** - Implementation of JWT Tokens. +* **[Validator](https://gopkg.in/go-playground/validator.v9)** - Package validator implements value validations for structs. + +* **[.env file for configuration](#environment)** - Change server config like app port, mongo url etc +* **[File Uploads](https://golang.org/pkg/io/)** - io package provides interfaces to I/O primitives. +* **[httptest](#testing)** - Utilities for HTTP testing. + +## Installation + +#### 1. Clone this repo + +``` +$ git clone git@github.com:umangraval/Go-Mongodb-REST-boilerplate.git your-app-name +$ cd your-app-name +``` + +#### 2. Install dependencies + +``` +$ go mod vendor +``` + +## Development + +### Start dev server +Starting the dev server also starts MongoDB as a service in a docker container using the compose script at `docker-compose.yml`. + +``` +$ go run main.go routes.go +``` +Running the above commands results in +* 🌏 **API Server** running at `http://localhost:8080` + +* ⛁ **MongoDB** running at `mongodb://localhost:27017/db` + +## Packaging and Deployment +#### 1. Build and run without Docker + +``` +$ go build +``` +#### 2. Run Tests + +``` +$ cd tests +$ go test +``` +#### 3. Run with docker + +``` +$ docker build -t api-server . +$ docker run -t -i -p 8080:8080 api-server +``` + +#### 4. Run with docker-compose + +``` +$ docker-compose up +``` + + +--- + +## Environment +To edit environment variables, create a file with name `.env` and copy the contents from `.env.default` to start with. + +| Var Name | Type | Default | Description | +|---|---|---|---| +| JWT_SECRET | string | `secret` |JWT secret to verify | +| PORT | number | `8080` | Port to run the API server on | +| MONGO_URL | string | `mongodb://localhost:27017/db` | URL for MongoDB | + + + + +### Directory Structure + +``` ++-- controllers +| +-- personController.go ++-- db +| +-- db.go ++-- handlers +| +-- config.go +| +-- logs.go +| +-- response.go +| +-- verifyJWT.go ++-- models +| +-- models.go ++-- validators +| +-- validators.go ++-- tests +| +-- api_test.go ++-- routes +| +-- routes.go ++-- uploaded ++-- vendor ++-- nginx +| +-- dev.conf.d +| | +-- nginx.conf ++-- .env ++-- .env.default ++-- .gitignore ++-- docker-compose.yml ++-- Dockerfile ++-- go.mod ++-- go.sum ++-- main.go ++-- README.md +``` \ No newline at end of file diff --git a/controllers/challengeController.go b/controllers/challengeController.go new file mode 100644 index 0000000..c99aaf5 --- /dev/null +++ b/controllers/challengeController.go @@ -0,0 +1,154 @@ +package controllers + +import ( + "context" + "encoding/json" + "log" + "net/http" + "strings" + "time" + + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/chattertechno/challenge-platform-api/models" + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// ListChallenge -> List all the challenges +var ListChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var challenges []*models.Challenge + collection := client.Database("challenge").Collection("challenges") + cursor, err := collection.Find(context.TODO(), bson.D{}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + for cursor.Next(context.TODO()) { + var challenge models.Challenge + err := cursor.Decode(&challenge) + if err != nil { + log.Fatal(err) + } + + challenges = append(challenges, &challenge) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + middlewares.SuccessChallengeArrRespond(challenges, rw) +}) + +// GetChallengs -> Get challenges for specific user +var GetChallenges = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + var challenges []*models.Challenge + + collection := client.Database("challenge").Collection("challenges") + cursor, err := collection.Find(context.TODO(), bson.D{primitive.E{Key: "coordinator", Value: params["username"]}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + for cursor.Next(context.TODO()) { + var challenge models.Challenge + err := cursor.Decode(&challenge) + if err != nil { + log.Fatal(err) + } + challenges = append(challenges, &challenge) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + middlewares.SuccessChallengeArrRespond(challenges, rw) +}) + +// CreateChallenge -> Create a challenge +var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var challenge models.Challenge + err := json.NewDecoder(r.Body).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + challenge.CreatedAt = time.Now() + challenge.UpdatedAt = time.Now() + collection := client.Database("challenge").Collection("challenges") + result, err := collection.InsertOne(context.TODO(), challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + res, _ := json.Marshal(result.InsertedID) + middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) +}) + +// GetChallenge -> Get a challenge by id +var GetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id, _ := primitive.ObjectIDFromHex(params["id"]) + var challenge models.Challenge + + collection := client.Database("challenge").Collection("challenges") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + middlewares.SuccessRespond(challenge, rw) +}) + +var UpdateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id, _ := primitive.ObjectIDFromHex(params["id"]) + var challenge models.Challenge + + err := json.NewDecoder(r.Body).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + collection := client.Database("challenge").Collection("challenges") + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: challenge}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if res.MatchedCount == 0 { + middlewares.ErrorResponse("Challenge does not exist", rw) + return + } + + middlewares.SuccessResponse("Updated", rw) +}) + +var DeleteChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + +}) + +// TO-DO USER TO JOIN THE CHALLENGE +var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + // params := mux.Vars(r) + // id, _ := primitive.ObjectIDFromHex(params["id"]) + // var challenge models.Challenge + + // err := json.NewDecoder(r.Body).Decode(&challenge) + // if err != nil { + // middlewares.ServerErrResponse(err.Error(), rw) + // return + // } + // collection := client.Database("challenge").Collection("challenges") + // res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: challenge}}) + +}) diff --git a/controllers/userController.go b/controllers/userController.go new file mode 100644 index 0000000..1cee164 --- /dev/null +++ b/controllers/userController.go @@ -0,0 +1,292 @@ +package controllers + +import ( + "context" + "encoding/json" + "io" + "log" + "net/http" + "os" + "strings" + + "github.com/chattertechno/challenge-platform-api/db" + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/chattertechno/challenge-platform-api/models" + "github.com/chattertechno/challenge-platform-api/validators" + "github.com/dgrijalva/jwt-go" + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +var client = db.Dbconnect() + +// RegisterUser -> Register User with Menmonic and username +var RegisterUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var user models.User + err := json.NewDecoder(r.Body).Decode(&user) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + collection := client.Database("challenge").Collection("users") + var existingUser models.User + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) + if err == nil { + middlewares.ErrorResponse("Username is already taken.", rw) + return + } + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "identity", Value: user.Identity}}).Decode(&existingUser) + if err == nil { + middlewares.ErrorResponse("Identity is already in use.", rw) + return + } + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "mnemonic", Value: user.Mnemonic}}).Decode(&existingUser) + if err == nil { + middlewares.ErrorResponse("Mnemonic Invalid", rw) + return + } + passwordHash, err := middlewares.HashPassword(user.Password) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + user.Password = passwordHash + result, err := collection.InsertOne(r.Context(), user) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + res, _ := json.Marshal(result.InsertedID) + middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) +}) + +// LoginUser -> Let the user login with identity and password +var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var user models.User + err := json.NewDecoder(r.Body).Decode(&user) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + collection := client.Database("challenge").Collection("users") + var existingUser models.User + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) + + if err != nil { + middlewares.ErrorResponse("User doesn't exist", rw) + return + } + isPasswordMatch := middlewares.CheckPasswordHash(user.Password, existingUser.Password) + if !isPasswordMatch { + middlewares.ErrorResponse("Password doesn't match", rw) + return + } + token, err := middlewares.GenerateJWT(user.Username) + if err != nil { + middlewares.ErrorResponse("Failed to generate JWT", rw) + return + } + middlewares.SuccessResponse(string(token), rw) +}) + +// GetMe -> Get user details from Authorization token +var GetMe = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + props, _ := r.Context().Value("props").(jwt.MapClaims) + var user models.User + collection := client.Database("challenge").Collection("users") + err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: props["username"]}}).Decode(&user) + if err != nil { + middlewares.AuthorizationResponse("Malformed token", rw) + return + } + + user.Password = "" + middlewares.SuccessRespond(user, rw) +}) + +// GetUser -> Get user details from username +var GetUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + var user models.User + + collection := client.Database("challenge").Collection("users") + err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: params["username"]}}).Decode(&user) + if err != nil { + middlewares.ErrorResponse("User doesn't exist", rw) + return + } + + user.Password = "" + middlewares.SuccessRespond(user, rw) +}) + +// UpdateUser -> Update user details from username +var UpdateUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + props, _ := r.Context().Value("props").(jwt.MapClaims) + var user models.User + + collection := client.Database("challenge").Collection("users") + err := collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: props["username"]}}).Decode(&user) + if err != nil { + middlewares.AuthorizationResponse("Malformed token", rw) + return + } + + var newUser models.User + err = json.NewDecoder(r.Body).Decode(&newUser) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + res, err := collection.UpdateOne(r.Context(), bson.D{primitive.E{Key: "_id", Value: user.ID}}, bson.D{ + primitive.E{ + Key: "$set", + Value: bson.D{ + primitive.E{Key: "username", Value: newUser.Username}, + primitive.E{Key: "bio", Value: newUser.Bio}, + }, + }, + }) + + if err != nil { + middlewares.ErrorResponse("Username is already taken.", rw) + return + } + if res.MatchedCount == 0 { + middlewares.ErrorResponse("User doesn't exist", rw) + return + } + + token, err := middlewares.GenerateJWT(newUser.Username) + if err != nil { + middlewares.ErrorResponse("Failed to generate JWT", rw) + return + } + middlewares.SuccessResponse(string(token), rw) +}) + +// CreatePersonEndpoint -> create person +var CreatePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + var person models.Person + err := json.NewDecoder(request.Body).Decode(&person) + if err != nil { + middlewares.ServerErrResponse(err.Error(), response) + return + } + if ok, errors := validators.ValidateInputs(person); !ok { + middlewares.ValidationResponse(errors, response) + return + } + collection := client.Database("golang").Collection("people") + result, err := collection.InsertOne(context.TODO(), person) + if err != nil { + middlewares.ServerErrResponse(err.Error(), response) + return + } + res, _ := json.Marshal(result.InsertedID) + middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), response) +}) + +// GetPeopleEndpoint -> get people +var GetPeopleEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + var people []*models.Person + + collection := client.Database("golang").Collection("people") + cursor, err := collection.Find(context.TODO(), bson.D{{}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), response) + return + } + for cursor.Next(context.TODO()) { + var person models.Person + err := cursor.Decode(&person) + if err != nil { + log.Fatal(err) + } + + people = append(people, &person) + } + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), response) + return + } + middlewares.SuccessArrRespond(people, response) +}) + +// GetPersonEndpoint -> get person by id +var GetPersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + params := mux.Vars(request) + id, _ := primitive.ObjectIDFromHex(params["id"]) + var person models.Person + + collection := client.Database("golang").Collection("people") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&person) + if err != nil { + middlewares.ErrorResponse("Person does not exist", response) + return + } + middlewares.SuccessRespond(person, response) +}) + +// DeletePersonEndpoint -> delete person by id +var DeletePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + params := mux.Vars(request) + id, _ := primitive.ObjectIDFromHex(params["id"]) + var person models.Person + + collection := client.Database("golang").Collection("people") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&person) + if err != nil { + middlewares.ErrorResponse("Person does not exist", response) + return + } + _, derr := collection.DeleteOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}) + if derr != nil { + middlewares.ServerErrResponse(derr.Error(), response) + return + } + middlewares.SuccessResponse("Deleted", response) +}) + +// UpdatePersonEndpoint -> update person by id +var UpdatePersonEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + params := mux.Vars(request) + id, _ := primitive.ObjectIDFromHex(params["id"]) + type fname struct { + Firstname string `json:"firstname"` + } + var fir fname + json.NewDecoder(request.Body).Decode(&fir) + collection := client.Database("golang").Collection("people") + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "firstname", Value: fir.Firstname}}}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), response) + return + } + if res.MatchedCount == 0 { + middlewares.ErrorResponse("Person does not exist", response) + return + } + middlewares.SuccessResponse("Updated", response) +}) + +// UploadFileEndpoint -> upload file +var UploadFileEndpoint = http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) { + file, handler, err := request.FormFile("file") + // fileName := request.FormValue("file_name") + if err != nil { + panic(err) + } + defer file.Close() + + f, err := os.OpenFile("uploaded/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + panic(err) + } + defer f.Close() + _, _ = io.Copy(f, file) + + middlewares.SuccessResponse("Uploaded Successfully", response) +}) diff --git a/db/db.go b/db/db.go new file mode 100644 index 0000000..7738958 --- /dev/null +++ b/db/db.go @@ -0,0 +1,32 @@ +package db + +import ( + "context" + "log" + + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/fatih/color" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var client *mongo.Client + +// Dbconnect -> connects mongo +func Dbconnect() *mongo.Client { + + clientOptions := options.Client().ApplyURI(middlewares.DotEnvVariable("MONGO_URL")) + client, err := mongo.Connect(context.TODO(), clientOptions) + if err != nil { + log.Fatal("⛒ Connection Failed to Database 1") + log.Fatal(err) + } + // Check the connection + err = client.Ping(context.TODO(), nil) + if err != nil { + log.Fatal("⛒ Connection Failed to Database 2") + log.Fatal(err) + } + color.Green("⛁ Connected to Database") + return client +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..aa70b85 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +version: '3.3' +services: + api: + build: . + ports: + - '3100:8080' + volumes: + - .:/app + depends_on: + - 'mongo' + web: + container_name: 'web' + image: 'nginx:latest' + ports: + - '80:80' + volumes: + - ./nginx/dev.conf.d:/etc/nginx/conf.d + depends_on: + - 'api' + mongo: + image: 'mongo:latest' + container_name: 'mongo' + ports: + - '27100:27017' + volumes: + - ./data/dev/mongo:/data/db diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2193009 --- /dev/null +++ b/go.mod @@ -0,0 +1,28 @@ +module github.com/chattertechno/challenge-platform-api + +// +heroku goVersion go1.16 +go 1.15 + +require ( + github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee // indirect + github.com/aws/aws-sdk-go v1.35.19 // indirect + github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 // indirect + github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/fatih/color v1.10.0 + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/golang/snappy v0.0.2 // indirect + github.com/gorilla/mux v1.8.0 + github.com/joho/godotenv v1.3.0 + github.com/klauspost/compress v1.11.2 // indirect + github.com/leodido/go-urn v1.2.0 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/rs/cors v1.7.0 + go.mongodb.org/mongo-driver v1.4.2 + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect + golang.org/x/text v0.3.4 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v9 v9.31.0 + gopkg.in/urfave/cli.v1 v1.20.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0497dd4 --- /dev/null +++ b/go.sum @@ -0,0 +1,183 @@ +github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee h1:LgokYDTCpaZBHtl/oGwLxNCr3kM5Qt+Z7mInv4MqFNM= +github.com/0xAX/notificator v0.0.0-20210731104411-c42e3d4a43ee/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.35.19 h1:vdIqQnOIqTNtvnOdt9r3Bf/FiCJ7KV/7O2BIj4TPx2w= +github.com/aws/aws-sdk-go v1.35.19/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= +github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 h1:ihrIKrLQzm6Q6NJHBMemvaIGTFxgxQUEkn2AjN0Aulw= +github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4/go.mod h1:X7wHz0C25Lga6CnJ4WAQNbUQ9P/8eWSNv8qIO71YkSM= +github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 h1:ysuVNDVE4LIky6I+6JlgAKG+wBNKMpVv3m3neVpvFVw= +github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974/go.mod h1:UBYuwaH3dMw91EZ7tGVaFF6GDj5j46S7zqB9lZPIe58= +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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.11.2 h1:MiK62aErc3gIiVEtyzKfeOHgW7atJb5g/KNX5m3c2nQ= +github.com/klauspost/compress v1.11.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc h1:n+nNi93yXLkJvKwXNP9d55HC7lGK4H/SRcwB5IaUZLo= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +go.mongodb.org/mongo-driver v1.4.2 h1:WlnEglfTg/PfPq4WXs2Vkl/5ICC6hoG8+r+LraPmGk4= +go.mongodb.org/mongo-driver v1.4.2/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= +gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/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/handlers/config.go b/handlers/config.go new file mode 100644 index 0000000..1d5d8e4 --- /dev/null +++ b/handlers/config.go @@ -0,0 +1,25 @@ +package middlewares + +import ( + "log" + "os" + "path" + + "github.com/joho/godotenv" +) + +// DotEnvVariable -> get .env +func DotEnvVariable(key string) string { + + // load .env file + wd, _ := os.Getwd() + log.Println(path.Join(wd, "/.env")) + + err := godotenv.Load() + + if err != nil { + log.Printf("Error loading .env file %s", err) + } + + return os.Getenv(key) +} diff --git a/handlers/logs.go b/handlers/logs.go new file mode 100644 index 0000000..92fee21 --- /dev/null +++ b/handlers/logs.go @@ -0,0 +1,15 @@ +package middlewares + +import ( + "net/http" + + "github.com/fatih/color" +) + +// LogRequest -> logs req info +func LogRequest(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + color.Yellow("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) + handler.ServeHTTP(w, r) + }) +} diff --git a/handlers/passwords.go b/handlers/passwords.go new file mode 100644 index 0000000..42bc3cd --- /dev/null +++ b/handlers/passwords.go @@ -0,0 +1,13 @@ +package middlewares + +import "golang.org/x/crypto/bcrypt" + +func HashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) + return string(bytes), err +} + +func CheckPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} diff --git a/handlers/response.go b/handlers/response.go new file mode 100644 index 0000000..f623eab --- /dev/null +++ b/handlers/response.go @@ -0,0 +1,137 @@ +package middlewares + +import ( + "encoding/json" + "net/http" + + "github.com/chattertechno/challenge-platform-api/models" +) + +// AuthorizationResponse -> response authorize +func AuthorizationResponse(msg string, writer http.ResponseWriter) { + type errdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &errdata{Statuscode: 401, Message: msg} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(writer).Encode(temp) +} + +// SuccessArrRespond -> response formatter +func SuccessArrRespond(fields []*models.Person, writer http.ResponseWriter) { + // var fields["status"] := "success" + _, err := json.Marshal(fields) + type data struct { + People []*models.Person `json:"data"` + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &data{People: fields, Statuscode: 200, Message: "success"} + if err != nil { + ServerErrResponse(err.Error(), writer) + } + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + +// SuccessArrRespond -> response formatter +func SuccessChallengeArrRespond(fields []*models.Challenge, writer http.ResponseWriter) { + // var fields["status"] := "success" + _, err := json.Marshal(fields) + type data struct { + Challenges []*models.Challenge `json:"data"` + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &data{Challenges: fields, Statuscode: 200, Message: "success"} + if err != nil { + ServerErrResponse(err.Error(), writer) + } + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + +// SuccessRespond -> response formatter +func SuccessRespond(fields interface{}, writer http.ResponseWriter) { + _, err := json.Marshal(fields) + type data struct { + Person interface{} `json:"data"` + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &data{Person: fields, Statuscode: 200, Message: "success"} + if err != nil { + ServerErrResponse(err.Error(), writer) + } + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + +// SuccessResponse -> success formatter +func SuccessResponse(msg string, writer http.ResponseWriter) { + type errdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &errdata{Statuscode: 200, Message: msg} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + +// ErrorResponse -> error formatter +func ErrorResponse(error string, writer http.ResponseWriter) { + type errdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &errdata{Statuscode: 400, Message: error} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusBadRequest) + json.NewEncoder(writer).Encode(temp) +} + +// ServerErrResponse -> server error formatter +func ServerErrResponse(error string, writer http.ResponseWriter) { + type servererrdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &servererrdata{Statuscode: 500, Message: error} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(writer).Encode(temp) +} + +// ValidationResponse -> user input validation +func ValidationResponse(fields map[string][]string, writer http.ResponseWriter) { + //Create a new map and fill it + response := make(map[string]interface{}) + response["errors"] = fields + response["status"] = 422 + response["msg"] = "validation error" + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusUnprocessableEntity) + json.NewEncoder(writer).Encode(response) +} diff --git a/handlers/verifyjwt.go b/handlers/verifyjwt.go new file mode 100644 index 0000000..73e5265 --- /dev/null +++ b/handlers/verifyjwt.go @@ -0,0 +1,67 @@ +package middlewares + +import ( + "context" + "fmt" + "net/http" + "strings" + "time" + + jwt "github.com/dgrijalva/jwt-go" +) + +var JWT_SECRET = []byte(DotEnvVariable("JWT_SECRET")) + +type Claims struct { + Username string `json:"username"` + jwt.StandardClaims +} + +// IsAuthorized -> verify jwt header +func IsAuthorized(next http.Handler) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authHeader := strings.Split(r.Header.Get("Authorization"), "Bearer ") + //fmt.Println(authHeader) + + if len(authHeader) != 2 { + AuthorizationResponse("Malformed JWT token", w) + } else { + jwtToken := authHeader[1] + token, _ := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return JWT_SECRET, nil + }) + + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + ctx := context.WithValue(r.Context(), "props", claims) + next.ServeHTTP(w, r.WithContext(ctx)) + } else { + AuthorizationResponse("Unauthorized", w) + } + } + }) +} + +// GenerateJWT -> generate jwt +func GenerateJWT(username string) (string, error) { + //fmt.Println(`this is user`, username) + claims := &Claims{ + Username: username, + StandardClaims: jwt.StandardClaims{ + ExpiresAt: time.Now().Add(2 * time.Hour).Unix(), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + tokenString, err := token.SignedString(JWT_SECRET) + //fmt.Println(`this is it`, tokenString) + + if err != nil { + return "", err + } + + return tokenString, nil +} diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 0000000..1a4592f --- /dev/null +++ b/heroku.yml @@ -0,0 +1,4 @@ +build: + docker: + web: Dockerfile + \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..171fbba --- /dev/null +++ b/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "log" + "net/http" + + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/chattertechno/challenge-platform-api/routes" + "github.com/fatih/color" + "github.com/rs/cors" +) + +func main() { + port := middlewares.DotEnvVariable("PORT") + color.Cyan("🌏 Server running on localhost:" + port) + + log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) + router := routes.Routes() + c := cors.New(cors.Options{ + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"}, + AllowedHeaders: []string{"Content-Type", "Origin", "Accept", "*"}, + }) + + handler := c.Handler(router) + http.ListenAndServe(":"+port, middlewares.LogRequest(handler)) +} diff --git a/models/models.go b/models/models.go new file mode 100644 index 0000000..2bccda8 --- /dev/null +++ b/models/models.go @@ -0,0 +1,91 @@ +package models + +import ( + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" +) + +// Person Model +type Person struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Firstname string `json:"firstname,omitempty" bson:"firstname,omitempty" validate:"required,alpha"` + Lastname string `json:"lastname,omitempty" bson:"lastname,omitempty" validate:"required,alpha"` +} + +// User Model +type User struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Username string `json:"username,omitempty" bson:"username,omitempty"` + Role string `json:"role,omitempty" bson:"role,omitempty"` + Bio string `json:"bio,omitempty" bson:"bio,omitempty"` + Avatar string `json:"avatar,omitempty" bson:"avatar,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` + Password string `json:"password,omitempty" bson:"password,omitempty"` +} + +type ChallengeStatus string + +const ( + Open ChallengeStatus = "open" + InvitationOnly ChallengeStatus = "invites only" + Private ChallengeStatus = "private" + Draft ChallengeStatus = "draft" +) + +// Challenge Model +type Challenge struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` + Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` + Goal string `json:"goal,omitempty" bson:"goal,omitempty"` + GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` + GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` + Category []string `json:"category,omitempty" bson:"category,omitempty"` + Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Description string `json:"description,omitempty" bson:"description,omitempty"` + Content string `json:"content,omitempty" bson:"content,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` + HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` +} + +type EscrowStatus string + +const ( + Pending EscrowStatus = "pending" + Paid EscrowStatus = "paid" +) + +type Escrow struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Amount float32 `json:"amount,omitempty" bson:"amount,omitempty"` + Challenge primitive.ObjectID `json:"challenge,omitempty" bson:"challenge,omitempty"` + Status EscrowStatus `json:"status,omitempty" bson:"status,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` +} + +type ActivityType string + +const ( + Joined ActivityType = "joined" + Won ActivityType = "won" + Lost ActivityType = "lost" + Played ActivityType = "played" +) + +type Activity struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Challenge primitive.ObjectID `json:"challenge,omitempty" bson:"challenge,omitempty"` + Participant string `json:"participant,omitempty" bson:"participant,omitempty"` + Type ActivityType `json:"type,omitempty" bson:"type,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` +} diff --git a/nginx/dev.conf.d/nginx.conf b/nginx/dev.conf.d/nginx.conf new file mode 100644 index 0000000..4513e86 --- /dev/null +++ b/nginx/dev.conf.d/nginx.conf @@ -0,0 +1,21 @@ +upstream go_server { + # will resolve to the correct address + server api:8080; +} + +# upstream frontend { +# # will resolve to the correct address +# server frontend:3000; +# } + +server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://go_server; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_redirect off; + } +} \ No newline at end of file diff --git a/routes/routes.go b/routes/routes.go new file mode 100644 index 0000000..2bbf8a6 --- /dev/null +++ b/routes/routes.go @@ -0,0 +1,45 @@ +package routes + +import ( + "net/http" + + "github.com/chattertechno/challenge-platform-api/controllers" + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/gorilla/mux" +) + +// Routes -> define endpoints +func Routes() *mux.Router { + router := mux.NewRouter() + + api := router.PathPrefix("/api/v1").Subrouter() + + // User API routes + + user := api.PathPrefix("/user").Subrouter() + user.HandleFunc("/register", controllers.RegisterUser).Methods("POST") + user.HandleFunc("/login", controllers.LoginUser).Methods("POST") + user.HandleFunc("/me", middlewares.IsAuthorized(controllers.GetMe)).Methods("GET") + user.HandleFunc("/me", middlewares.IsAuthorized(controllers.UpdateUser)).Methods("PUT") + user.HandleFunc("/{username}", controllers.GetUser).Methods("GET") + + // Challenge API routes + + challenge := api.PathPrefix("/challenge").Subrouter() + challenge.HandleFunc("/", middlewares.IsAuthorized(controllers.ListChallenge)).Methods("GET") + challenge.HandleFunc("/", middlewares.IsAuthorized(controllers.CreateChallenge)).Methods("POST") + challenge.HandleFunc("/user/{username}", controllers.GetChallenges).Methods("GET") + challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.GetChallenge)).Methods("GET") + challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.UpdateChallenge)).Methods("PUT") + challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") + + api.HandleFunc("/person", controllers.CreatePersonEndpoint).Methods("POST") + api.HandleFunc("/people", middlewares.IsAuthorized(controllers.GetPeopleEndpoint)).Methods("GET") + api.HandleFunc("/person/{id}", controllers.GetPersonEndpoint).Methods("GET") + api.HandleFunc("/person/{id}", controllers.DeletePersonEndpoint).Methods("DELETE") + api.HandleFunc("/person/{id}", controllers.UpdatePersonEndpoint).Methods("PUT") + + router.HandleFunc("/upload", controllers.UploadFileEndpoint).Methods("POST") + router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./uploaded/")))) + return router +} diff --git a/uploaded/.gitkeep b/uploaded/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/validators/validators.go b/validators/validators.go new file mode 100644 index 0000000..7711f8e --- /dev/null +++ b/validators/validators.go @@ -0,0 +1,58 @@ +package validators + +import ( + "reflect" + "strings" + + "gopkg.in/go-playground/validator.v9" +) + +var validate *validator.Validate + +// ValidateInputs -> sanitize user inputs +func ValidateInputs(dataSet interface{}) (bool, map[string][]string) { + validate = validator.New() + err := validate.Struct(dataSet) + + if err != nil { + + //Validation syntax is invalid + if err, ok := err.(*validator.InvalidValidationError); ok { + panic(err) + } + + //Validation errors occurred + errors := make(map[string][]string) + //Use reflector to reverse engineer struct + reflected := reflect.ValueOf(dataSet) + for _, err := range err.(validator.ValidationErrors) { + + // Attempt to find field by name and get json tag name + field, _ := reflected.Type().FieldByName(err.StructField()) + var name string + + //If json tag doesn't exist, use lower case of name + if name = field.Tag.Get("json"); name == "" { + name = strings.ToLower(err.StructField()) + } + + switch err.Tag() { + case "required": + errors[name] = append(errors[name], "The "+name+" is required") + break + case "alpha": + errors[name] = append(errors[name], "The "+name+" should contain only letters") + break + // case "eqfield": + // errors[name] = append(errors[name], "The "+name+" should be equal to the "+err.Param()) + // break + default: + errors[name] = append(errors[name], "The "+name+" is invalid") + break + } + } + + return false, errors + } + return true, nil +} From bd89bddef8f412b038782dfdae44690593376c94 Mon Sep 17 00:00:00 2001 From: Emmanuel E Date: Fri, 6 Jan 2023 20:55:18 +0500 Subject: [PATCH 03/40] Deploying to Heroku --- Procfile | 1 + 1 file changed, 1 insertion(+) create mode 100644 Procfile diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..85926b9 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: challenge-platform-api From 90f09f927c2dded749c045cb375521de45a91430 Mon Sep 17 00:00:00 2001 From: Emmanuel E Date: Fri, 6 Jan 2023 21:25:16 +0500 Subject: [PATCH 04/40] env variable --- .env | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..6be3795 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +PORT=8080 +MONGO_URL=mongodb+srv://developer:Daffodils1@cluster0.mxnvpgv.mongodb.net/?retryWrites=true&w=majority +JWT_SECRET=secret From 4c2ca93c9f3b28140b2cdd91952ce1ad2938650c Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Tue, 24 Jan 2023 20:38:34 +0530 Subject: [PATCH 05/40] Update build.yml --- .github/workflows/build.yml | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6baac5..d2135b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,36 +1,28 @@ -name: Build +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go on: push: - branches: [ main ] + branches: [ "main" ] pull_request: - branches: [ main ] + branches: [ "main" ] jobs: build: - name: Build runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - - name: Set up Go 1.x - uses: actions/setup-go@v2 + - name: Set up Go + uses: actions/setup-go@v3 with: - go-version: ^1.13 - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi + go-version: 1.19 - name: Build - run: go build -v . + run: go build -v ./... # - name: Test -# run: go test -v . +# run: go test -v ./... From edab13f1f3526d586948fa6a0d99aebf435f2119 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Tue, 24 Jan 2023 20:40:47 +0530 Subject: [PATCH 06/40] Update build.yml --- .github/workflows/build.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2135b6..a648c84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,4 @@ -# This workflow will build a golang project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go - -name: Go +name: Heroku on: push: @@ -24,5 +21,5 @@ jobs: - name: Build run: go build -v ./... -# - name: Test -# run: go test -v ./... + - name: Test + run: go test -v ./... From 19d73aa12d4f0931506b1e8955f8923bdcbd3158 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Tue, 24 Jan 2023 20:43:38 +0530 Subject: [PATCH 07/40] Update build.yml --- .github/workflows/build.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a648c84..b56157e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,18 @@ jobs: uses: actions/setup-go@v3 with: go-version: 1.19 - + + - uses: actions/cache@v3 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- - name: Build run: go build -v ./... From 066ee5df0560b953782848523a33d38df070cb1b Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Tue, 24 Jan 2023 21:20:18 +0530 Subject: [PATCH 08/40] Update and rename build.yml to heroku-deploy-QA.yml --- .../{build.yml => heroku-deploy-QA.yml} | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) rename .github/workflows/{build.yml => heroku-deploy-QA.yml} (56%) diff --git a/.github/workflows/build.yml b/.github/workflows/heroku-deploy-QA.yml similarity index 56% rename from .github/workflows/build.yml rename to .github/workflows/heroku-deploy-QA.yml index b56157e..fad070f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/heroku-deploy-QA.yml @@ -1,24 +1,25 @@ name: Heroku on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - + push: +# branches: +# - QA + workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v3 with: go-version: 1.19 - - uses: actions/cache@v3 + - name: Cache + uses: actions/cache@v3 with: # In order: # * Module download cache @@ -29,8 +30,17 @@ jobs: key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- + - name: Build run: go build -v ./... - name: Test run: go test -v ./... + + - name: Deploy API -> Prod + uses: akhileshns/heroku-deploy@v3.12.12 + with: + heroku_api_key: ${{secrets.HEROKU_API_KEY}} + heroku_app_name: "challenge-api-qa" + heroku_email: ${{secrets.HEROKU_EMAIL}} + branch: main From 42db47a8182c99a893d776cc281bc23ca9cbaca5 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:05:47 +0530 Subject: [PATCH 09/40] Update heroku-deploy-QA.yml --- .github/workflows/heroku-deploy-QA.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/heroku-deploy-QA.yml b/.github/workflows/heroku-deploy-QA.yml index fad070f..f9b426b 100644 --- a/.github/workflows/heroku-deploy-QA.yml +++ b/.github/workflows/heroku-deploy-QA.yml @@ -1,9 +1,9 @@ -name: Heroku +name: QA - Heroku Deploy on: push: -# branches: -# - QA + branches: + - QA workflow_dispatch: jobs: @@ -37,10 +37,10 @@ jobs: - name: Test run: go test -v ./... - - name: Deploy API -> Prod + - name: Deploy API -> QA uses: akhileshns/heroku-deploy@v3.12.12 with: heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_app_name: "challenge-api-qa" heroku_email: ${{secrets.HEROKU_EMAIL}} - branch: main + branch: QA From 460bcec01de299e537f1c15eeec50c0deda7b104 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:07:24 +0530 Subject: [PATCH 10/40] Create main.yml --- .github/workflows/main.yml | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2176449 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,47 @@ +name: Prod - Heroku Deploy + +on: + pull_request: + branches: + - main + types: close + workflow_dispatch: +jobs: + + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.19 + + - name: Cache + uses: actions/cache@v3 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... + + - name: Deploy API -> Prod + uses: akhileshns/heroku-deploy@v3.12.12 + with: + heroku_api_key: ${{secrets.HEROKU_API_KEY}} + heroku_app_name: "challenge-api" + heroku_email: ${{secrets.HEROKU_EMAIL}} + branch: main From febf42ab40521ad8ec4b1451081f1f6d761ae71b Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:08:02 +0530 Subject: [PATCH 11/40] Rename main.yml to heroku-deploy-Prod.yml --- .github/workflows/{main.yml => heroku-deploy-Prod.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{main.yml => heroku-deploy-Prod.yml} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/heroku-deploy-Prod.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/heroku-deploy-Prod.yml From 243d97c79d86665a2dc77c7fb204d5f107941f26 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Sat, 28 Jan 2023 02:03:06 +0530 Subject: [PATCH 12/40] Update heroku-deploy-QA.yml --- .github/workflows/heroku-deploy-QA.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/heroku-deploy-QA.yml b/.github/workflows/heroku-deploy-QA.yml index f9b426b..9b3d0a6 100644 --- a/.github/workflows/heroku-deploy-QA.yml +++ b/.github/workflows/heroku-deploy-QA.yml @@ -1,13 +1,16 @@ name: QA - Heroku Deploy on: + # Job Trigger automatically if any code push/PR to QA branch push: branches: - QA + # Enabled Manual Trigger workflow_dispatch: + jobs: - - build: + build-deploy: + name: Deploy runs-on: ubuntu-latest steps: - name: Checkout @@ -44,3 +47,4 @@ jobs: heroku_app_name: "challenge-api-qa" heroku_email: ${{secrets.HEROKU_EMAIL}} branch: QA +# env_file: .env From 0b27f2019f6fac18bd2643bb442302872ccf79e7 Mon Sep 17 00:00:00 2001 From: Emmanuel E <49331376+TalentedSunday@users.noreply.github.com> Date: Sat, 28 Jan 2023 02:03:49 +0530 Subject: [PATCH 13/40] Update heroku-deploy-Prod.yml --- .github/workflows/heroku-deploy-Prod.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/heroku-deploy-Prod.yml b/.github/workflows/heroku-deploy-Prod.yml index 2176449..322320d 100644 --- a/.github/workflows/heroku-deploy-Prod.yml +++ b/.github/workflows/heroku-deploy-Prod.yml @@ -1,14 +1,17 @@ name: Prod - Heroku Deploy on: + # Job Trigger automatically if any PR is closed for main branch pull_request: branches: - - main - types: close + - dev + types: [ closed ] + # Enabled Manual Trigger workflow_dispatch: jobs: - build: + build-deploy: + name: Deploy runs-on: ubuntu-latest steps: - name: Checkout @@ -44,4 +47,5 @@ jobs: heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_app_name: "challenge-api" heroku_email: ${{secrets.HEROKU_EMAIL}} - branch: main + branch: dev +# env_file: .env.default From 704a8397d0f43451733b1b30f59c5cd9c206edca Mon Sep 17 00:00:00 2001 From: Emmanuel E Date: Tue, 31 Jan 2023 21:29:50 +0500 Subject: [PATCH 14/40] .env removed --- .env | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 6be3795..0000000 --- a/.env +++ /dev/null @@ -1,3 +0,0 @@ -PORT=8080 -MONGO_URL=mongodb+srv://developer:Daffodils1@cluster0.mxnvpgv.mongodb.net/?retryWrites=true&w=majority -JWT_SECRET=secret From ed2b61a11d635e2b5ecf00350cfd99ac46d01774 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Wed, 22 Feb 2023 21:59:58 +0500 Subject: [PATCH 15/40] feat(join,unjoin): Add join and unjoin endpoints --- controllers/challengeController.go | 100 +++++++++++++++++++++++++---- controllers/userController.go | 4 +- handlers/response.go | 14 ++++ handlers/verifyjwt.go | 5 +- models/models.go | 37 +++++------ routes/routes.go | 2 + 6 files changed, 128 insertions(+), 34 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index c99aaf5..347230f 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -10,6 +10,7 @@ import ( middlewares "github.com/chattertechno/challenge-platform-api/handlers" "github.com/chattertechno/challenge-platform-api/models" + "github.com/dgrijalva/jwt-go" "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" @@ -137,18 +138,93 @@ var DeleteChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ }) -// TO-DO USER TO JOIN THE CHALLENGE var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - // params := mux.Vars(r) - // id, _ := primitive.ObjectIDFromHex(params["id"]) - // var challenge models.Challenge - - // err := json.NewDecoder(r.Body).Decode(&challenge) - // if err != nil { - // middlewares.ServerErrResponse(err.Error(), rw) - // return - // } - // collection := client.Database("challenge").Collection("challenges") - // res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: challenge}}) + params := mux.Vars(r) + id, _ := primitive.ObjectIDFromHex(params["id"]) + + var challenge models.Challenge + + collection := client.Database("challenge").Collection("challenges") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + props, _ := r.Context().Value("props").(jwt.MapClaims) + + if challenge.Status == "private" { + if challenge.Coordinator == props["username"] || challenge.RecipientAddress == props["identity"] { + + challenge.Participants = append(challenge.Participants, challenge.Identity) + + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if res.MatchedCount == 0 { + middlewares.ErrorResponse("challenge does not exist", rw) + return + } + + middlewares.SuccessRespond(params["id"], rw) + return + } + middlewares.ForbiddenResponse("you have no access for this challenge", rw) + return + } + + challenge.Participants = append(challenge.Participants, challenge.Identity) + + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if res.MatchedCount == 0 { + middlewares.ErrorResponse("challenge does not exist", rw) + return + } + + middlewares.SuccessRespond(params["id"], rw) +}) + +var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + id, _ := primitive.ObjectIDFromHex(params["id"]) + + var challenge models.Challenge + + collection := client.Database("challenge").Collection("challenges") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + props, _ := r.Context().Value("props").(jwt.MapClaims) + + identity := props["identity"] + for i, v := range challenge.Participants { + if v == identity { + challenge.Participants = append(challenge.Participants[:i], challenge.Participants[i+1:]...) + break + } + } + + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if res.MatchedCount == 0 { + middlewares.ErrorResponse("challenge does not exist", rw) + return + } + middlewares.SuccessResponse("unjoin challenge successfully", rw) }) diff --git a/controllers/userController.go b/controllers/userController.go index 1cee164..a02a28d 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -82,7 +82,7 @@ var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { middlewares.ErrorResponse("Password doesn't match", rw) return } - token, err := middlewares.GenerateJWT(user.Username) + token, err := middlewares.GenerateJWT(user.Username, existingUser.Identity) if err != nil { middlewares.ErrorResponse("Failed to generate JWT", rw) return @@ -159,7 +159,7 @@ var UpdateUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) return } - token, err := middlewares.GenerateJWT(newUser.Username) + token, err := middlewares.GenerateJWT(newUser.Username, user.Identity) if err != nil { middlewares.ErrorResponse("Failed to generate JWT", rw) return diff --git a/handlers/response.go b/handlers/response.go index f623eab..e73110f 100644 --- a/handlers/response.go +++ b/handlers/response.go @@ -108,6 +108,20 @@ func ErrorResponse(error string, writer http.ResponseWriter) { json.NewEncoder(writer).Encode(temp) } +// ForbiddenResponse -> error formatter +func ForbiddenResponse(msg string, writer http.ResponseWriter) { + type errdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &errdata{Statuscode: http.StatusForbidden, Message: msg} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusUnauthorized) + json.NewEncoder(writer).Encode(temp) +} + // ServerErrResponse -> server error formatter func ServerErrResponse(error string, writer http.ResponseWriter) { type servererrdata struct { diff --git a/handlers/verifyjwt.go b/handlers/verifyjwt.go index 73e5265..029fd03 100644 --- a/handlers/verifyjwt.go +++ b/handlers/verifyjwt.go @@ -14,6 +14,7 @@ var JWT_SECRET = []byte(DotEnvVariable("JWT_SECRET")) type Claims struct { Username string `json:"username"` + Identity string `json:"identity"` jwt.StandardClaims } @@ -45,10 +46,10 @@ func IsAuthorized(next http.Handler) http.HandlerFunc { } // GenerateJWT -> generate jwt -func GenerateJWT(username string) (string, error) { - //fmt.Println(`this is user`, username) +func GenerateJWT(username, identity string) (string, error) { claims := &Claims{ Username: username, + Identity: identity, StandardClaims: jwt.StandardClaims{ ExpiresAt: time.Now().Add(2 * time.Hour).Unix(), }, diff --git a/models/models.go b/models/models.go index 2bccda8..3c68293 100644 --- a/models/models.go +++ b/models/models.go @@ -36,24 +36,25 @@ const ( // Challenge Model type Challenge struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` - EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` - Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` - Goal string `json:"goal,omitempty" bson:"goal,omitempty"` - GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` - GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` - Category []string `json:"category,omitempty" bson:"category,omitempty"` - Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` - Name string `json:"name,omitempty" bson:"name,omitempty"` - Description string `json:"description,omitempty" bson:"description,omitempty"` - Content string `json:"content,omitempty" bson:"content,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` - HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` - Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` + Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` + Goal string `json:"goal,omitempty" bson:"goal,omitempty"` + GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` + GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` + Category []string `json:"category,omitempty" bson:"category,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Description string `json:"description,omitempty" bson:"description,omitempty"` + Content string `json:"content,omitempty" bson:"content,omitempty"` + HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` + RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` + Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } type EscrowStatus string diff --git a/routes/routes.go b/routes/routes.go index 2bbf8a6..b77293b 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -32,6 +32,8 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.GetChallenge)).Methods("GET") challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.UpdateChallenge)).Methods("PUT") challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") + challenge.HandleFunc("/{id}/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") + challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") api.HandleFunc("/person", controllers.CreatePersonEndpoint).Methods("POST") api.HandleFunc("/people", middlewares.IsAuthorized(controllers.GetPeopleEndpoint)).Methods("GET") From 0ad26aa364e55fbb4c89d38acc1ff95605f43510 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Wed, 1 Mar 2023 01:29:55 +0500 Subject: [PATCH 16/40] fix join challenge function and also add check on unjoin function --- controllers/challengeController.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 347230f..6d8ccfc 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -152,11 +152,12 @@ var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Reques } props, _ := r.Context().Value("props").(jwt.MapClaims) + identity := props["identity"].(string) if challenge.Status == "private" { - if challenge.Coordinator == props["username"] || challenge.RecipientAddress == props["identity"] { + if challenge.Coordinator == props["username"] || challenge.RecipientAddress == identity { - challenge.Participants = append(challenge.Participants, challenge.Identity) + challenge.Participants = append(challenge.Participants, identity) res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) if err != nil { @@ -175,8 +176,7 @@ var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Reques middlewares.ForbiddenResponse("you have no access for this challenge", rw) return } - - challenge.Participants = append(challenge.Participants, challenge.Identity) + challenge.Participants = append(challenge.Participants, identity) res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) if err != nil { @@ -206,14 +206,20 @@ var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ } props, _ := r.Context().Value("props").(jwt.MapClaims) + identity := props["identity"].(string) - identity := props["identity"] + var check bool = false for i, v := range challenge.Participants { if v == identity { challenge.Participants = append(challenge.Participants[:i], challenge.Participants[i+1:]...) + check = true break } } + if !check { + middlewares.ErrorResponse("you have already leave this challenge", rw) + return + } res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) if err != nil { From db42492740786a1ad1fcccc198b846410c2ad85a Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sun, 5 Mar 2023 22:40:53 +0500 Subject: [PATCH 17/40] Add mnemonic in challenge and also apply validation --- controllers/challengeController.go | 6 ++++++ models/models.go | 1 + 2 files changed, 7 insertions(+) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 6d8ccfc..a2b454a 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -84,6 +84,12 @@ var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ challenge.CreatedAt = time.Now() challenge.UpdatedAt = time.Now() collection := client.Database("challenge").Collection("challenges") + var existingChallenge models.Challenge + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "mnemonic", Value: challenge.Mnemonic}}).Decode(&existingChallenge) + if err == nil { + middlewares.ErrorResponse("Mnemonic Invalid", rw) + return + } result, err := collection.InsertOne(context.TODO(), challenge) if err != nil { middlewares.ServerErrResponse(err.Error(), rw) diff --git a/models/models.go b/models/models.go index 3c68293..0ce1f51 100644 --- a/models/models.go +++ b/models/models.go @@ -46,6 +46,7 @@ type Challenge struct { Category []string `json:"category,omitempty" bson:"category,omitempty"` Name string `json:"name,omitempty" bson:"name,omitempty"` Description string `json:"description,omitempty" bson:"description,omitempty"` + Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` Content string `json:"content,omitempty" bson:"content,omitempty"` HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` From 99b56b7f7a99e8bd244b516e2870f432d94e1b0f Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 24 Mar 2023 20:29:17 +0500 Subject: [PATCH 18/40] add bet and steps endpoints --- controllers/betController.go | 75 +++++++++++++++++++++++++++++++ controllers/stepsController.go | 80 ++++++++++++++++++++++++++++++++++ models/models.go | 20 +++++++++ routes/routes.go | 9 ++++ 4 files changed, 184 insertions(+) create mode 100644 controllers/betController.go create mode 100644 controllers/stepsController.go diff --git a/controllers/betController.go b/controllers/betController.go new file mode 100644 index 0000000..d7217c8 --- /dev/null +++ b/controllers/betController.go @@ -0,0 +1,75 @@ +package controllers + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "time" + + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/chattertechno/challenge-platform-api/models" + "github.com/dgrijalva/jwt-go" + "github.com/gorilla/mux" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +var AddBetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var bet models.Bet + err := json.NewDecoder(r.Body).Decode(&bet) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + ChallengeID, _ := primitive.ObjectIDFromHex(bet.ChallengeID) + + var challenge models.Challenge + collection := client.Database("challenge").Collection("challenges") + err = collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: ChallengeID}}).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(fmt.Sprintf("challenge %v not found", bet.ChallengeID), rw) + return + } + + props, _ := r.Context().Value("props").(jwt.MapClaims) + identity := props["identity"].(string) + bet.Identity = identity + bet.CreatedAt = time.Now() + + betCollection := client.Database("challenge").Collection("bets") + result, err := betCollection.InsertOne(context.TODO(), bet) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + middlewares.SuccessRespond(result, rw) +}) + +var GetAllBetsForChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + var bets []*models.Bet + + betCollection := client.Database("challenge").Collection("bets") + + cursor, err := betCollection.Find(context.TODO(), bson.D{primitive.E{Key: "challenge_id", Value: params["id"]}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for cursor.Next(context.TODO()) { + var bet models.Bet + err := cursor.Decode(&bet) + if err != nil { + log.Fatal(err) + } + bets = append(bets, &bet) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + middlewares.SuccessRespond(bets, rw) +}) diff --git a/controllers/stepsController.go b/controllers/stepsController.go new file mode 100644 index 0000000..86a450d --- /dev/null +++ b/controllers/stepsController.go @@ -0,0 +1,80 @@ +package controllers + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + middlewares "github.com/chattertechno/challenge-platform-api/handlers" + "github.com/chattertechno/challenge-platform-api/models" + "github.com/dgrijalva/jwt-go" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +var AddStepsChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var steps models.Steps + err := json.NewDecoder(r.Body).Decode(&steps) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + ChallengeID, _ := primitive.ObjectIDFromHex(steps.ChallengeID) + + var challenge models.Challenge + collection := client.Database("challenge").Collection("challenges") + err = collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: ChallengeID}}).Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(fmt.Sprintf("challenge %v not found", steps.ChallengeID), rw) + return + } + + stepsCollection := client.Database("challenge").Collection("stepsDetails") + + props, _ := r.Context().Value("props").(jwt.MapClaims) + identity := props["identity"].(string) + steps.Identity = identity + steps.CreatedAt = time.Now() + + filter := bson.M{ + "challenge_id": steps.ChallengeID, + "identity": steps.Identity, + } + var existedSteps models.Steps + err = stepsCollection.FindOne(context.TODO(), filter).Decode(&existedSteps) + if err != nil && err != mongo.ErrNoDocuments { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + if err == mongo.ErrNoDocuments { + res, err := stepsCollection.InsertOne(context.TODO(), steps) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + middlewares.SuccessRespond(res, rw) + return + } + if steps.StepsCount != 0 { + existedSteps.StepsCount = steps.StepsCount + } + if len(steps.StepsDistance) > 0 { + existedSteps.StepsDistance = steps.StepsDistance + } + if steps.MinimumStepsCount != 0 { + existedSteps.MinimumStepsCount = steps.MinimumStepsCount + } + if len(steps.MinimumStepsDistance) > 0 { + existedSteps.MinimumStepsDistance = steps.MinimumStepsDistance + } + + _, err = stepsCollection.ReplaceOne(context.TODO(), bson.M{"_id": existedSteps.ID}, existedSteps) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + middlewares.SuccessRespond(fmt.Sprintf("steps updated at %v", existedSteps.ID.Hex()), rw) +}) diff --git a/models/models.go b/models/models.go index 0ce1f51..2c02f46 100644 --- a/models/models.go +++ b/models/models.go @@ -91,3 +91,23 @@ type Activity struct { CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } + +type Bet struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Amount float64 `json:"amount,omitempty" bson:"amount,omitempty"` + ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` +} + +type Steps struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + StepsCount int64 `json:"steps_count,omitempty" bson:"steps_count,omitempty"` + StepsDistance string `json:"steps_distance,omitempty" bson:"steps_distance,omitempty"` + MinimumStepsCount int64 `json:"minimum_steps_count,omitempty" bson:"minimum_steps_count,omitempty"` + MinimumStepsDistance string `json:"minimum_steps_distance,omitempty" bson:"minimum_steps_distance,omitempty"` + ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` +} diff --git a/routes/routes.go b/routes/routes.go index b77293b..f7506f2 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -35,6 +35,15 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") + // Challenge bet routes + bet := challenge.PathPrefix("/bet").Subrouter() + bet.HandleFunc("/add/", middlewares.IsAuthorized(controllers.AddBetChallenge)).Methods("POST") + bet.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.GetAllBetsForChallenge)).Methods("GET") + + // User steps routes + steps := challenge.PathPrefix("/user/steps").Subrouter() + steps.HandleFunc("/add/", middlewares.IsAuthorized(controllers.AddStepsChallenge)).Methods("PUT") + api.HandleFunc("/person", controllers.CreatePersonEndpoint).Methods("POST") api.HandleFunc("/people", middlewares.IsAuthorized(controllers.GetPeopleEndpoint)).Methods("GET") api.HandleFunc("/person/{id}", controllers.GetPersonEndpoint).Methods("GET") From a6edfa115c3cdec674122a853a9f0b2846e7b88e Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 25 Mar 2023 02:36:53 +0500 Subject: [PATCH 19/40] complete reward endpoint --- controllers/challengeController.go | 123 +++++++++++++++++++++++++++++ models/models.go | 6 ++ routes/routes.go | 1 + 3 files changed, 130 insertions(+) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index a2b454a..6de9195 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -5,6 +5,7 @@ import ( "encoding/json" "log" "net/http" + "strconv" "strings" "time" @@ -14,6 +15,7 @@ import ( "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) // ListChallenge -> List all the challenges @@ -240,3 +242,124 @@ var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ middlewares.SuccessResponse("unjoin challenge successfully", rw) }) + +// ChallengeWinner -> Get all the winners of challenge +var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + var challenges *models.Challenge + + id, _ := primitive.ObjectIDFromHex(params["id"]) + + collection := client.Database("challenge").Collection("challenges") + err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenges) + if err != nil { + if err == mongo.ErrNoDocuments { + middlewares.ServerErrResponse("challenge not found", rw) + return + } + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + var steps []*models.Steps + + stepsCollection := client.Database("challenge").Collection("stepsDetails") + cursor, err := stepsCollection.Find(context.TODO(), bson.D{primitive.E{Key: "challenge_id", Value: params["id"]}}) + if err != nil { + if err == mongo.ErrNoDocuments { + middlewares.ServerErrResponse("users steps record not found for this challenge", rw) + return + } + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for cursor.Next(context.TODO()) { + var step models.Steps + err := cursor.Decode(&step) + if err != nil { + log.Fatal(err) + } + steps = append(steps, &step) + } + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + var winnerRecord []*models.Steps + + if challenges.Goal == "distance" { + floatGoalThershold, err := strconv.ParseFloat(challenges.GoalThreshold, 32) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + } + for _, v := range steps { + distance := strings.Split(v.StepsDistance, " ") + floatUserDistance, err := strconv.ParseFloat(distance[0], 32) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + } + if floatUserDistance > floatGoalThershold { + winnerRecord = append(winnerRecord, v) + } + } + } else if challenges.Goal == "count" { + intGoalThershold, err := strconv.ParseInt(challenges.GoalThreshold, 10, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + } + for _, v := range steps { + if v.StepsCount > intGoalThershold { + winnerRecord = append(winnerRecord, v) + } + } + } + + var bets []*models.Bet + + betCollection := client.Database("challenge").Collection("bets") + + cursor, err = betCollection.Find(context.TODO(), bson.D{primitive.E{Key: "challenge_id", Value: params["id"]}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for cursor.Next(context.TODO()) { + var bet models.Bet + err := cursor.Decode(&bet) + if err != nil { + log.Fatal(err) + } + bets = append(bets, &bet) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + var totalAmount float64 + + for _, bet := range bets { + totalAmount = totalAmount + bet.Amount + } + var count int64 + var winners []models.WinnerResponse + for _, winner := range winnerRecord { + for _, user := range bets { + var win models.WinnerResponse + if user.Identity == winner.Identity { + win.ChallengeID = user.ChallengeID + win.Identity = user.Identity + // win.Amount = averageAmount + count++ + winners = append(winners, win) + } + } + } + averageAmount := totalAmount / float64(count) + for i := range winners { + winners[i].Amount = averageAmount + } + + middlewares.SuccessRespond(winners, rw) +}) diff --git a/models/models.go b/models/models.go index 2c02f46..7de97f4 100644 --- a/models/models.go +++ b/models/models.go @@ -111,3 +111,9 @@ type Steps struct { ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` } + +type WinnerResponse struct { + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` + Amount float64 `json:"amount,omitempty" bson:"amount,omitempty"` +} diff --git a/routes/routes.go b/routes/routes.go index f7506f2..7e31183 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -34,6 +34,7 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") challenge.HandleFunc("/{id}/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") + challenge.HandleFunc("/{id}/winner/", middlewares.IsAuthorized(controllers.ChallengeWinner)).Methods("GET") // Challenge bet routes bet := challenge.PathPrefix("/bet").Subrouter() From 22c5f92c9c51e5c9873772510de6a43ddcbf1d5b Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Mon, 27 Mar 2023 20:15:19 +0500 Subject: [PATCH 20/40] add new field of min bet amount on create challenge --- controllers/betController.go | 5 +++++ models/models.go | 1 + 2 files changed, 6 insertions(+) diff --git a/controllers/betController.go b/controllers/betController.go index d7217c8..cdfd27d 100644 --- a/controllers/betController.go +++ b/controllers/betController.go @@ -33,6 +33,11 @@ var AddBetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ return } + if challenge.MinBetAmount > bet.Amount { + middlewares.ErrorResponse(fmt.Sprintf("bet must be greater than %v", challenge.MinBetAmount), rw) + return + } + props, _ := r.Context().Value("props").(jwt.MapClaims) identity := props["identity"].(string) bet.Identity = identity diff --git a/models/models.go b/models/models.go index 7de97f4..e3b054b 100644 --- a/models/models.go +++ b/models/models.go @@ -53,6 +53,7 @@ type Challenge struct { Identity string `json:"identity,omitempty" bson:"identity,omitempty"` Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` + MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` From e2a0a3db1bede049212a44418c5a977e9ca40afc Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Tue, 4 Apr 2023 20:59:36 +0500 Subject: [PATCH 21/40] modify winner endpoint and also modify update step endpoint --- controllers/challengeController.go | 130 ++++++++++++++++++----------- controllers/stepsController.go | 3 + handlers/response.go | 19 +++++ models/models.go | 1 + 4 files changed, 102 insertions(+), 51 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 6de9195..3d1d402 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -286,35 +286,6 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ return } - var winnerRecord []*models.Steps - - if challenges.Goal == "distance" { - floatGoalThershold, err := strconv.ParseFloat(challenges.GoalThreshold, 32) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - } - for _, v := range steps { - distance := strings.Split(v.StepsDistance, " ") - floatUserDistance, err := strconv.ParseFloat(distance[0], 32) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - } - if floatUserDistance > floatGoalThershold { - winnerRecord = append(winnerRecord, v) - } - } - } else if challenges.Goal == "count" { - intGoalThershold, err := strconv.ParseInt(challenges.GoalThreshold, 10, 64) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - } - for _, v := range steps { - if v.StepsCount > intGoalThershold { - winnerRecord = append(winnerRecord, v) - } - } - } - var bets []*models.Bet betCollection := client.Database("challenge").Collection("bets") @@ -337,29 +308,86 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ middlewares.ServerErrResponse(err.Error(), rw) return } - var totalAmount float64 - - for _, bet := range bets { - totalAmount = totalAmount + bet.Amount - } - var count int64 - var winners []models.WinnerResponse - for _, winner := range winnerRecord { - for _, user := range bets { - var win models.WinnerResponse - if user.Identity == winner.Identity { - win.ChallengeID = user.ChallengeID - win.Identity = user.Identity - // win.Amount = averageAmount - count++ - winners = append(winners, win) - } - } + + checkChallengeWins := false + + goalThreshold, err := strconv.ParseFloat(challenges.GoalThreshold, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return } - averageAmount := totalAmount / float64(count) - for i := range winners { - winners[i].Amount = averageAmount + + for _, value := range steps { + stepsDistance, err := strconv.ParseFloat(value.StepsDistance, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + if goalThreshold < stepsDistance { + checkChallengeWins = true + break + } + } - middlewares.SuccessRespond(winners, rw) + var winnerRecord []*models.Steps + + if checkChallengeWins { + + if challenges.Goal == "distance" { + floatGoalThershold, err := strconv.ParseFloat(challenges.GoalThreshold, 32) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for _, v := range steps { + floatUserDistance, err := strconv.ParseFloat(v.StepsDistance, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + if floatUserDistance > floatGoalThershold { + winnerRecord = append(winnerRecord, v) + } + } + } else if challenges.Goal == "count" { + intGoalThershold, err := strconv.ParseInt(challenges.GoalThreshold, 10, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for _, v := range steps { + if v.StepsCount > intGoalThershold { + winnerRecord = append(winnerRecord, v) + } + } + } + + var totalAmount float64 + + for _, bet := range bets { + totalAmount = totalAmount + bet.Amount + } + var count int64 + var winners []models.WinnerResponse + for _, winner := range winnerRecord { + for _, user := range bets { + var win models.WinnerResponse + if user.Identity == winner.Identity { + win.ChallengeID = user.ChallengeID + win.Identity = user.Identity + // win.Amount = averageAmount + count++ + winners = append(winners, win) + } + } + } + averageAmount := totalAmount / float64(count) + for i := range winners { + winners[i].Amount = averageAmount + } + middlewares.SuccessRespondWithCustomMessage(winners, "challenge winners", rw) + return + } + middlewares.SuccessRespondWithCustomMessage(bets, "no winner", rw) }) diff --git a/controllers/stepsController.go b/controllers/stepsController.go index 86a450d..1d89d8c 100644 --- a/controllers/stepsController.go +++ b/controllers/stepsController.go @@ -70,6 +70,9 @@ var AddStepsChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Re if len(steps.MinimumStepsDistance) > 0 { existedSteps.MinimumStepsDistance = steps.MinimumStepsDistance } + if len(steps.Distance) > 0 { + existedSteps.Distance = steps.Distance + } _, err = stepsCollection.ReplaceOne(context.TODO(), bson.M{"_id": existedSteps.ID}, existedSteps) if err != nil { diff --git a/handlers/response.go b/handlers/response.go index e73110f..ab308a3 100644 --- a/handlers/response.go +++ b/handlers/response.go @@ -80,6 +80,25 @@ func SuccessRespond(fields interface{}, writer http.ResponseWriter) { json.NewEncoder(writer).Encode(temp) } +// SuccessRespond -> response formatter +func SuccessRespondWithCustomMessage(fields interface{}, msg string, writer http.ResponseWriter) { + _, err := json.Marshal(fields) + type data struct { + Person interface{} `json:"data"` + Statuscode int `json:"status"` + Message string `json:"msg"` + } + temp := &data{Person: fields, Statuscode: 200, Message: msg} + if err != nil { + ServerErrResponse(err.Error(), writer) + } + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + // SuccessResponse -> success formatter func SuccessResponse(msg string, writer http.ResponseWriter) { type errdata struct { diff --git a/models/models.go b/models/models.go index e3b054b..50d4a72 100644 --- a/models/models.go +++ b/models/models.go @@ -107,6 +107,7 @@ type Steps struct { Identity string `json:"identity,omitempty" bson:"identity,omitempty"` StepsCount int64 `json:"steps_count,omitempty" bson:"steps_count,omitempty"` StepsDistance string `json:"steps_distance,omitempty" bson:"steps_distance,omitempty"` + Distance string `json:"distance,omitempty" bson:"distance,omitempty"` MinimumStepsCount int64 `json:"minimum_steps_count,omitempty" bson:"minimum_steps_count,omitempty"` MinimumStepsDistance string `json:"minimum_steps_distance,omitempty" bson:"minimum_steps_distance,omitempty"` ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` From 2b12abe13874e9274c558e221f9b2c44a9e562c3 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 20 May 2023 15:39:31 +0500 Subject: [PATCH 22/40] update list challenge api --- controllers/betController.go | 2 +- controllers/challengeController.go | 9 ++++++--- controllers/stepsController.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/controllers/betController.go b/controllers/betController.go index cdfd27d..afac450 100644 --- a/controllers/betController.go +++ b/controllers/betController.go @@ -41,7 +41,7 @@ var AddBetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ props, _ := r.Context().Value("props").(jwt.MapClaims) identity := props["identity"].(string) bet.Identity = identity - bet.CreatedAt = time.Now() + bet.CreatedAt = time.Now().UTC() betCollection := client.Database("challenge").Collection("bets") result, err := betCollection.InsertOne(context.TODO(), bet) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 3d1d402..e5de970 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -16,13 +16,16 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" ) // ListChallenge -> List all the challenges var ListChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { var challenges []*models.Challenge + opts := options.Find().SetSort(bson.D{primitive.E{Key: "created_at", Value: -1}}) + collection := client.Database("challenge").Collection("challenges") - cursor, err := collection.Find(context.TODO(), bson.D{}) + cursor, err := collection.Find(context.TODO(), bson.D{}, opts) if err != nil { middlewares.ServerErrResponse(err.Error(), rw) return @@ -83,8 +86,8 @@ var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ middlewares.ServerErrResponse(err.Error(), rw) return } - challenge.CreatedAt = time.Now() - challenge.UpdatedAt = time.Now() + challenge.CreatedAt = time.Now().UTC() + challenge.UpdatedAt = time.Now().UTC() collection := client.Database("challenge").Collection("challenges") var existingChallenge models.Challenge err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "mnemonic", Value: challenge.Mnemonic}}).Decode(&existingChallenge) diff --git a/controllers/stepsController.go b/controllers/stepsController.go index 1d89d8c..1871e9d 100644 --- a/controllers/stepsController.go +++ b/controllers/stepsController.go @@ -37,7 +37,7 @@ var AddStepsChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Re props, _ := r.Context().Value("props").(jwt.MapClaims) identity := props["identity"].(string) steps.Identity = identity - steps.CreatedAt = time.Now() + steps.CreatedAt = time.Now().UTC() filter := bson.M{ "challenge_id": steps.ChallengeID, From d24674e7e75e0e392da6e579b3bc394f8abdbf7c Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 20 May 2023 16:15:34 +0500 Subject: [PATCH 23/40] update --- handlers/verifyjwt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/verifyjwt.go b/handlers/verifyjwt.go index 029fd03..404926a 100644 --- a/handlers/verifyjwt.go +++ b/handlers/verifyjwt.go @@ -51,7 +51,7 @@ func GenerateJWT(username, identity string) (string, error) { Username: username, Identity: identity, StandardClaims: jwt.StandardClaims{ - ExpiresAt: time.Now().Add(2 * time.Hour).Unix(), + ExpiresAt: time.Now().Add(24 * time.Hour).Unix(), }, } From f2074a23ba798bb6d1b46a0104df5f01d205993e Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 20 May 2023 16:27:45 +0500 Subject: [PATCH 24/40] add bet in challenge --- models/models.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/models.go b/models/models.go index 50d4a72..7e56aa9 100644 --- a/models/models.go +++ b/models/models.go @@ -43,6 +43,7 @@ type Challenge struct { Goal string `json:"goal,omitempty" bson:"goal,omitempty"` GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` + AddBet string `json:"add_bet,omitempty" bson:"add_bet,omitempty"` Category []string `json:"category,omitempty" bson:"category,omitempty"` Name string `json:"name,omitempty" bson:"name,omitempty"` Description string `json:"description,omitempty" bson:"description,omitempty"` From 80325cdcd29e8e7bd683a297a96ffc6db988e5ed Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 15 Jul 2023 14:48:26 +0500 Subject: [PATCH 25/40] update signup and token --- controllers/userController.go | 4 ++-- handlers/verifyjwt.go | 12 +++++++----- models/models.go | 17 +++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/controllers/userController.go b/controllers/userController.go index a02a28d..fb2f084 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -82,7 +82,7 @@ var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { middlewares.ErrorResponse("Password doesn't match", rw) return } - token, err := middlewares.GenerateJWT(user.Username, existingUser.Identity) + token, err := middlewares.GenerateJWT(user.Username, existingUser.Identity, existingUser.PrivateKey) if err != nil { middlewares.ErrorResponse("Failed to generate JWT", rw) return @@ -159,7 +159,7 @@ var UpdateUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) return } - token, err := middlewares.GenerateJWT(newUser.Username, user.Identity) + token, err := middlewares.GenerateJWT(newUser.Username, user.Identity, user.PrivateKey) if err != nil { middlewares.ErrorResponse("Failed to generate JWT", rw) return diff --git a/handlers/verifyjwt.go b/handlers/verifyjwt.go index 404926a..b68410f 100644 --- a/handlers/verifyjwt.go +++ b/handlers/verifyjwt.go @@ -13,8 +13,9 @@ import ( var JWT_SECRET = []byte(DotEnvVariable("JWT_SECRET")) type Claims struct { - Username string `json:"username"` - Identity string `json:"identity"` + Username string `json:"username"` + Identity string `json:"identity"` + PrivateKey string `json:"private_key"` jwt.StandardClaims } @@ -46,10 +47,11 @@ func IsAuthorized(next http.Handler) http.HandlerFunc { } // GenerateJWT -> generate jwt -func GenerateJWT(username, identity string) (string, error) { +func GenerateJWT(username, identity, privateKey string) (string, error) { claims := &Claims{ - Username: username, - Identity: identity, + Username: username, + Identity: identity, + PrivateKey: privateKey, StandardClaims: jwt.StandardClaims{ ExpiresAt: time.Now().Add(24 * time.Hour).Unix(), }, diff --git a/models/models.go b/models/models.go index 7e56aa9..2bd4427 100644 --- a/models/models.go +++ b/models/models.go @@ -15,14 +15,15 @@ type Person struct { // User Model type User struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - Username string `json:"username,omitempty" bson:"username,omitempty"` - Role string `json:"role,omitempty" bson:"role,omitempty"` - Bio string `json:"bio,omitempty" bson:"bio,omitempty"` - Avatar string `json:"avatar,omitempty" bson:"avatar,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` - Password string `json:"password,omitempty" bson:"password,omitempty"` + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Username string `json:"username,omitempty" bson:"username,omitempty"` + Role string `json:"role,omitempty" bson:"role,omitempty"` + Bio string `json:"bio,omitempty" bson:"bio,omitempty"` + Avatar string `json:"avatar,omitempty" bson:"avatar,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` + PrivateKey string `json:"private_key,omitempty" bson:"private_key,omitempty"` + Password string `json:"password,omitempty" bson:"password,omitempty"` } type ChallengeStatus string From 6553416cdb60ab40aee3803710319b8f02680170 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sun, 16 Jul 2023 23:56:59 +0500 Subject: [PATCH 26/40] modify join challenge api --- controllers/challengeController.go | 93 +++++++++++++++++++----------- models/models.go | 9 +++ routes/routes.go | 2 +- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index e5de970..fd192d6 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -3,6 +3,7 @@ package controllers import ( "context" "encoding/json" + "fmt" "log" "net/http" "strconv" @@ -86,15 +87,13 @@ var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ middlewares.ServerErrResponse(err.Error(), rw) return } - challenge.CreatedAt = time.Now().UTC() - challenge.UpdatedAt = time.Now().UTC() + props, _ := r.Context().Value("props").(jwt.MapClaims) + challenge.Identity = props["identity"].(string) + + now := time.Now().UTC() + challenge.CreatedAt = now + challenge.UpdatedAt = now collection := client.Database("challenge").Collection("challenges") - var existingChallenge models.Challenge - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "mnemonic", Value: challenge.Mnemonic}}).Decode(&existingChallenge) - if err == nil { - middlewares.ErrorResponse("Mnemonic Invalid", rw) - return - } result, err := collection.InsertOne(context.TODO(), challenge) if err != nil { middlewares.ServerErrResponse(err.Error(), rw) @@ -150,57 +149,85 @@ var DeleteChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ }) var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - params := mux.Vars(r) - id, _ := primitive.ObjectIDFromHex(params["id"]) + var req models.JoinChallenge + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + ChallengeID, _ := primitive.ObjectIDFromHex(req.ChallengeID) var challenge models.Challenge collection := client.Database("challenge").Collection("challenges") - err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) + err = collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: ChallengeID}}).Decode(&challenge) if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) + if err == mongo.ErrNoDocuments { + middlewares.ErrorResponse("challenge does not exists", rw) + return + } + middlewares.ErrorResponse(err.Error(), rw) return } props, _ := r.Context().Value("props").(jwt.MapClaims) identity := props["identity"].(string) - if challenge.Status == "private" { - if challenge.Coordinator == props["username"] || challenge.RecipientAddress == identity { + challengeBet, err := strconv.ParseFloat(challenge.AddBet, 64) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } - challenge.Participants = append(challenge.Participants, identity) + if challengeBet > req.Bet { + middlewares.ErrorResponse(fmt.Sprintf("bet must be greater than %v", challengeBet), rw) + return + } - res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } + req.Identity = identity + req.CreatedAt = time.Now().UTC() - if res.MatchedCount == 0 { - middlewares.ErrorResponse("challenge does not exist", rw) - return - } + challengeJoined := client.Database("challenge").Collection("challengeJoined") + + filter := bson.M{ + "challenge_id": req.ChallengeID, + "identity": req.Identity, + } - middlewares.SuccessRespond(params["id"], rw) + var getjoinedChallenge models.JoinChallenge + + err = challengeJoined.FindOne(context.TODO(), filter).Decode(&getjoinedChallenge) + if err != nil && err != mongo.ErrNoDocuments { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if getjoinedChallenge.ChallengeID != "" || getjoinedChallenge.Identity != "" { + if err != nil { + middlewares.ErrorResponse("you already joined this challenge", rw) return } - middlewares.ForbiddenResponse("you have no access for this challenge", rw) - return } - challenge.Participants = append(challenge.Participants, identity) - res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) + result, err := challengeJoined.InsertOne(context.TODO(), req) if err != nil { middlewares.ServerErrResponse(err.Error(), rw) return } - if res.MatchedCount == 0 { - middlewares.ErrorResponse("challenge does not exist", rw) + var bet models.Bet + bet.Identity = identity + bet.ChallengeID = req.ChallengeID + bet.Amount = req.Bet + + betCollection := client.Database("challenge").Collection("bets") + _, err = betCollection.InsertOne(context.TODO(), bet) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) return } - - middlewares.SuccessRespond(params["id"], rw) + res, _ := json.Marshal(result.InsertedID) + middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) }) var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { diff --git a/models/models.go b/models/models.go index 2bd4427..8b90a62 100644 --- a/models/models.go +++ b/models/models.go @@ -104,6 +104,15 @@ type Bet struct { UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } +type JoinChallenge struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Bet float64 `json:"bet,omitempty" bson:"bet,omitempty"` + ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` +} + type Steps struct { ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` Identity string `json:"identity,omitempty" bson:"identity,omitempty"` diff --git a/routes/routes.go b/routes/routes.go index 7e31183..cb8f930 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -32,7 +32,7 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.GetChallenge)).Methods("GET") challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.UpdateChallenge)).Methods("PUT") challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") - challenge.HandleFunc("/{id}/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") + challenge.HandleFunc("/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/winner/", middlewares.IsAuthorized(controllers.ChallengeWinner)).Methods("GET") From d531d6090918abb1ba2175e0996f07a9fad5df08 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Mon, 17 Jul 2023 00:19:05 +0500 Subject: [PATCH 27/40] modify unjoin challenge --- controllers/challengeController.go | 35 ++++++++---------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index fd192d6..fa20b3f 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -232,41 +232,24 @@ var JoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Reques var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { params := mux.Vars(r) - id, _ := primitive.ObjectIDFromHex(params["id"]) - - var challenge models.Challenge - - collection := client.Database("challenge").Collection("challenges") - err := collection.FindOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}).Decode(&challenge) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } props, _ := r.Context().Value("props").(jwt.MapClaims) identity := props["identity"].(string) - var check bool = false - for i, v := range challenge.Participants { - if v == identity { - challenge.Participants = append(challenge.Participants[:i], challenge.Participants[i+1:]...) - check = true - break - } - } - if !check { - middlewares.ErrorResponse("you have already leave this challenge", rw) - return + filter := bson.M{ + "challenge_id": params["id"], + "identity": identity, } - res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: bson.D{primitive.E{Key: "Participants", Value: challenge.Participants}}}}) + challengeJoined := client.Database("challenge").Collection("challengeJoined") + + deleteResult, err := challengeJoined.DeleteOne(context.TODO(), filter) if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return + log.Fatal(err) } - if res.MatchedCount == 0 { - middlewares.ErrorResponse("challenge does not exist", rw) + if deleteResult.DeletedCount == 0 { + middlewares.ErrorResponse("challenge does not exists", rw) return } From 863647ed194afb5c3fd6081205bec8627bba031b Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Thu, 20 Jul 2023 22:15:14 +0500 Subject: [PATCH 28/40] add two new apis --- controllers/challengeController.go | 77 +++++++++++++++++++++++++++--- models/models.go | 58 +++++++++++++--------- routes/routes.go | 3 ++ 3 files changed, 110 insertions(+), 28 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index fa20b3f..56dc5bf 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "log" "net/http" "strconv" "strings" @@ -36,7 +35,8 @@ var ListChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Reques var challenge models.Challenge err := cursor.Decode(&challenge) if err != nil { - log.Fatal(err) + middlewares.ServerErrResponse(err.Error(), rw) + return } challenges = append(challenges, &challenge) @@ -66,7 +66,8 @@ var GetChallenges = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Reques var challenge models.Challenge err := cursor.Decode(&challenge) if err != nil { - log.Fatal(err) + middlewares.ServerErrResponse(err.Error(), rw) + return } challenges = append(challenges, &challenge) } @@ -91,6 +92,7 @@ var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ challenge.Identity = props["identity"].(string) now := time.Now().UTC() + challenge.FundDeliveredFlag = false challenge.CreatedAt = now challenge.UpdatedAt = now collection := client.Database("challenge").Collection("challenges") @@ -245,7 +247,8 @@ var UnJoinChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ deleteResult, err := challengeJoined.DeleteOne(context.TODO(), filter) if err != nil { - log.Fatal(err) + middlewares.ServerErrResponse(err.Error(), rw) + return } if deleteResult.DeletedCount == 0 { @@ -290,7 +293,8 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ var step models.Steps err := cursor.Decode(&step) if err != nil { - log.Fatal(err) + middlewares.ServerErrResponse(err.Error(), rw) + return } steps = append(steps, &step) } @@ -312,7 +316,8 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ var bet models.Bet err := cursor.Decode(&bet) if err != nil { - log.Fatal(err) + middlewares.ServerErrResponse(err.Error(), rw) + return } bets = append(bets, &bet) } @@ -404,3 +409,63 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ } middlewares.SuccessRespondWithCustomMessage(bets, "no winner", rw) }) + +var UpdateFlag = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + params := mux.Vars(r) + var challenge models.UpdateFlagRequest + + id, _ := primitive.ObjectIDFromHex(params["id"]) + challenge.FundDeliveredFlag = true + + collection := client.Database("challenge").Collection("challenges") + res, err := collection.UpdateOne(context.TODO(), bson.D{primitive.E{Key: "_id", Value: id}}, bson.D{primitive.E{Key: "$set", Value: challenge}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + if res.MatchedCount == 0 { + middlewares.ErrorResponse("Challenge does not exist", rw) + return + } + + middlewares.SuccessResponse("Flag updated", rw) +}) + +var FinishedChallenges = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + var challenges []*models.GetChallenges + + collection := client.Database("challenge").Collection("challenges") + ctx := context.TODO() + currentDateTime := time.Now().Format("2006-01-02") + + filter := bson.M{ + "end_date": bson.M{"$gte": currentDateTime}, + "fund_delivered_flag": false, + } + + cursor, err := collection.Find(ctx, filter) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + defer cursor.Close(ctx) + + for cursor.Next(ctx) { + var challenge models.GetChallenges + err := cursor.Decode(&challenge) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + challenges = append(challenges, &challenge) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + middlewares.SuccessRespond(challenges, rw) +}) diff --git a/models/models.go b/models/models.go index 8b90a62..c27b26d 100644 --- a/models/models.go +++ b/models/models.go @@ -37,28 +37,29 @@ const ( // Challenge Model type Challenge struct { - ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` - StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` - EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` - Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` - Goal string `json:"goal,omitempty" bson:"goal,omitempty"` - GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` - GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` - AddBet string `json:"add_bet,omitempty" bson:"add_bet,omitempty"` - Category []string `json:"category,omitempty" bson:"category,omitempty"` - Name string `json:"name,omitempty" bson:"name,omitempty"` - Description string `json:"description,omitempty" bson:"description,omitempty"` - Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` - Content string `json:"content,omitempty" bson:"content,omitempty"` - HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` - Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` - RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` - MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` - Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` + Status ChallengeStatus `json:"status,omitempty" bson:"status,omitempty"` + Goal string `json:"goal,omitempty" bson:"goal,omitempty"` + GoalIncreaments string `json:"goal_increaments,omitempty" bson:"goal_increaments,omitempty"` + GoalThreshold string `json:"goal_threshold,omitempty" bson:"goal_threshold,omitempty"` + AddBet string `json:"add_bet,omitempty" bson:"add_bet,omitempty"` + Category []string `json:"category,omitempty" bson:"category,omitempty"` + Name string `json:"name,omitempty" bson:"name,omitempty"` + Description string `json:"description,omitempty" bson:"description,omitempty"` + // Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` + FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` + Content string `json:"content,omitempty" bson:"content,omitempty"` + HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` + RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` + MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` + // Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } type EscrowStatus string @@ -130,3 +131,16 @@ type WinnerResponse struct { ChallengeID string `json:"challenge_id,omitempty" bson:"challenge_id,omitempty"` Amount float64 `json:"amount,omitempty" bson:"amount,omitempty"` } + +type UpdateFlagRequest struct { + FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` +} + +type GetChallenges struct { + ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"` + StartDate string `json:"start_date,omitempty" bson:"start_date,omitempty"` + EndDate string `json:"end_date,omitempty" bson:"end_date,omitempty"` + FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` +} diff --git a/routes/routes.go b/routes/routes.go index cb8f930..35dba22 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -36,6 +36,9 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/winner/", middlewares.IsAuthorized(controllers.ChallengeWinner)).Methods("GET") + challenge.HandleFunc("/finished/", controllers.FinishedChallenges).Methods("GET") + challenge.HandleFunc("/update/flag/{id}", controllers.UpdateFlag).Methods("PUT") + // Challenge bet routes bet := challenge.PathPrefix("/bet").Subrouter() bet.HandleFunc("/add/", middlewares.IsAuthorized(controllers.AddBetChallenge)).Methods("POST") From 5736efc0d842c385f2f05af2bbb4ec01aefdbb61 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 21 Jul 2023 00:18:15 +0500 Subject: [PATCH 29/40] fix bug --- routes/routes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/routes.go b/routes/routes.go index 35dba22..1f09a87 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -34,7 +34,7 @@ func Routes() *mux.Router { challenge.HandleFunc("/{id}", middlewares.IsAuthorized(controllers.DeleteChallenge)).Methods("DELETE") challenge.HandleFunc("/join/", middlewares.IsAuthorized(controllers.JoinChallenge)).Methods("POST") challenge.HandleFunc("/{id}/unjoin/", middlewares.IsAuthorized(controllers.UnJoinChallenge)).Methods("POST") - challenge.HandleFunc("/{id}/winner/", middlewares.IsAuthorized(controllers.ChallengeWinner)).Methods("GET") + challenge.HandleFunc("/{id}/winner/", controllers.ChallengeWinner).Methods("GET") challenge.HandleFunc("/finished/", controllers.FinishedChallenges).Methods("GET") challenge.HandleFunc("/update/flag/{id}", controllers.UpdateFlag).Methods("PUT") From bb45815e61c717cead37532c6bc315a699e5d67f Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sun, 23 Jul 2023 15:08:35 +0500 Subject: [PATCH 30/40] update get api --- controllers/challengeController.go | 25 +++++++++++++++++++++++++ models/models.go | 22 +++++++++++----------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 56dc5bf..b4be444 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -117,6 +117,31 @@ var GetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request middlewares.ServerErrResponse(err.Error(), rw) return } + challengeJoined := client.Database("challenge").Collection("challengeJoined") + cursor, err := challengeJoined.Find(context.TODO(), bson.D{primitive.E{Key: "challenge_id", Value: params["id"]}}) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + + var participants []*models.JoinChallenge + + for cursor.Next(context.TODO()) { + var participant models.JoinChallenge + err := cursor.Decode(&participant) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + participants = append(participants, &participant) + } + + if err := cursor.Err(); err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + challenge.Participants = participants + middlewares.SuccessRespond(challenge, rw) }) diff --git a/models/models.go b/models/models.go index c27b26d..e72d091 100644 --- a/models/models.go +++ b/models/models.go @@ -49,17 +49,17 @@ type Challenge struct { Name string `json:"name,omitempty" bson:"name,omitempty"` Description string `json:"description,omitempty" bson:"description,omitempty"` // Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` - FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` - Content string `json:"content,omitempty" bson:"content,omitempty"` - HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` - Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` - RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` - MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` - // Participants []string `json:"participants,omitempty" bson:"participants,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` + FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` + Content string `json:"content,omitempty" bson:"content,omitempty"` + HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` + RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` + MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` + Participants []*JoinChallenge `json:"participants,omitempty" bson:"participants,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } type EscrowStatus string From 0f477483d244af51831daa331a459d8d9f6d8c71 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sun, 23 Jul 2023 15:30:14 +0500 Subject: [PATCH 31/40] fix bug --- controllers/challengeController.go | 4 ++-- models/models.go | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index b4be444..590d385 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -124,7 +124,7 @@ var GetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request return } - var participants []*models.JoinChallenge + var participants []models.JoinChallenge for cursor.Next(context.TODO()) { var participant models.JoinChallenge @@ -133,7 +133,7 @@ var GetChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request middlewares.ServerErrResponse(err.Error(), rw) return } - participants = append(participants, &participant) + participants = append(participants, participant) } if err := cursor.Err(); err != nil { diff --git a/models/models.go b/models/models.go index e72d091..ee49521 100644 --- a/models/models.go +++ b/models/models.go @@ -49,17 +49,17 @@ type Challenge struct { Name string `json:"name,omitempty" bson:"name,omitempty"` Description string `json:"description,omitempty" bson:"description,omitempty"` // Mnemonic string `json:"mnemonic,omitempty" bson:"mnemonic,omitempty"` - FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` - Content string `json:"content,omitempty" bson:"content,omitempty"` - HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` - Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` - Identity string `json:"identity,omitempty" bson:"identity,omitempty"` - Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` - RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` - MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` - Participants []*JoinChallenge `json:"participants,omitempty" bson:"participants,omitempty"` - CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` + FundDeliveredFlag bool `json:"fund_delivered_flag" bson:"fund_delivered_flag"` + Content string `json:"content,omitempty" bson:"content,omitempty"` + HeaderImage string `json:"header_image,omitempty" bson:"header_image,omitempty"` + Coordinator string `json:"coordinator,omitempty" bson:"coordinator,omitempty"` + Identity string `json:"identity,omitempty" bson:"identity,omitempty"` + Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` + RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` + MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` + Participants []JoinChallenge `json:"participants,omitempty" bson:"participants,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } type EscrowStatus string From cfae5fc229c198e3fe9d6765e5bb465de8540d20 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sun, 23 Jul 2023 15:33:03 +0500 Subject: [PATCH 32/40] fix bug --- models/models.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/models.go b/models/models.go index ee49521..b4acf79 100644 --- a/models/models.go +++ b/models/models.go @@ -57,7 +57,7 @@ type Challenge struct { Visible bool `json:"visible,omitempty" bson:"visible,omitempty"` RecipientAddress string `json:"recipient_address,omitempty" bson:"recipient_address,omitempty"` MinBetAmount float64 `json:"min_bet_amount,omitempty" bson:"min_bet_amount,omitempty"` - Participants []JoinChallenge `json:"participants,omitempty" bson:"participants,omitempty"` + Participants []JoinChallenge `json:"participant,omitempty" bson:"participant,omitempty"` CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"` } From c5e970c5fbcb56276b1b662b21feace3650940a5 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 28 Jul 2023 14:39:05 +0500 Subject: [PATCH 33/40] setup docker --- .env | 3 +++ .gitignore | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..c07e3a4 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +PORT=8080 +MONGO_URL=mongodb+srv://root:root@cluster0.jgltqbs.mongodb.net/?retryWrites=true&w=majority +JWT_SECRET=secret \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0f87e70..d5e8410 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ main gin-bin -.env +# .env # exclude everything uploaded/* From 0ebdb93be869d9a5301e4bb1734c76dfad088cb3 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 28 Jul 2023 15:27:08 +0500 Subject: [PATCH 34/40] setup docker --- .air.toml | 44 +++++++++++++++++++++++++++++++++++++++ Dockerfile | 32 +++++++++++++++++++++-------- docker-compose.yml | 51 +++++++++++++++++++++++++++------------------- 3 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 .air.toml diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..7b1fede --- /dev/null +++ b/.air.toml @@ -0,0 +1,44 @@ +root = "." +testdata_dir = "testdata" +tmp_dir = ".bin" + +[build] + args_bin = [] + bin = "./.bin/main" + cmd = "go build -o ./.bin/main ." + delay = 0 + exclude_dir = ["assets", ".bin", "vendor", "testdata"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/Dockerfile b/Dockerfile index 15dcd3b..af1d933 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,28 @@ +# FROM golang:latest + +# ENV GO111MODULE=on +# ENV PORT=8080 +# WORKDIR /app +# COPY go.mod /app +# COPY go.sum /app + +# RUN go mod download +# RUN go install -mod=mod github.com/githubnemo/CompileDaemon +# COPY . /app +# ENTRYPOINT CompileDaemon --build="go build -o main" --command=./main + FROM golang:latest -ENV GO111MODULE=on -ENV PORT=8080 -WORKDIR /app -COPY go.mod /app -COPY go.sum /app +RUN mkdir /golang + +RUN go install github.com/cosmtrek/air@latest + +ADD . /golang/ + +RUN go install github.com/cosmtrek/air@latest + +WORKDIR /golang RUN go mod download -RUN go install -mod=mod github.com/githubnemo/CompileDaemon -COPY . /app -ENTRYPOINT CompileDaemon --build="go build -o main" --command=./main \ No newline at end of file + +CMD ["air", "-c", ".air.toml"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index aa70b85..6552b31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,26 +1,35 @@ version: '3.3' services: + # api: + # build: . + # ports: + # - '3100:8080' + # volumes: + # - .:/app + # depends_on: + # - 'mongo' + # web: + # container_name: 'web' + # image: 'nginx:latest' + # ports: + # - '80:80' + # volumes: + # - ./nginx/dev.conf.d:/etc/nginx/conf.d + # depends_on: + # - 'api' + # mongo: + # image: 'mongo:latest' + # container_name: 'mongo' + # ports: + # - '27100:27017' + # volumes: + # - ./data/dev/mongo:/data/db api: - build: . + container_name: api + build: + context: . + dockerfile: Dockerfile ports: - - '3100:8080' + - 8080:8080 volumes: - - .:/app - depends_on: - - 'mongo' - web: - container_name: 'web' - image: 'nginx:latest' - ports: - - '80:80' - volumes: - - ./nginx/dev.conf.d:/etc/nginx/conf.d - depends_on: - - 'api' - mongo: - image: 'mongo:latest' - container_name: 'mongo' - ports: - - '27100:27017' - volumes: - - ./data/dev/mongo:/data/db + - ./:/api From 27bec0f99c5b686a0accff9e1ce5131457097d93 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Wed, 2 Aug 2023 20:25:04 +0500 Subject: [PATCH 35/40] update create challenge response --- controllers/challengeController.go | 2 +- handlers/response.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 590d385..95a9003 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -102,7 +102,7 @@ var CreateChallenge = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ return } res, _ := json.Marshal(result.InsertedID) - middlewares.SuccessResponse(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), rw) + middlewares.SuccessResponseWithData(`Inserted at `+strings.Replace(string(res), `"`, ``, 2), result.InsertedID, rw) }) // GetChallenge -> Get a challenge by id diff --git a/handlers/response.go b/handlers/response.go index ab308a3..627be74 100644 --- a/handlers/response.go +++ b/handlers/response.go @@ -113,6 +113,20 @@ func SuccessResponse(msg string, writer http.ResponseWriter) { json.NewEncoder(writer).Encode(temp) } +func SuccessResponseWithData(msg string, data interface{}, writer http.ResponseWriter) { + type errdata struct { + Statuscode int `json:"status"` + Message string `json:"msg"` + Data interface{} `json:"data"` + } + temp := &errdata{Statuscode: 200, Message: msg, Data: data} + + //Send header, status code and output to writer + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusOK) + json.NewEncoder(writer).Encode(temp) +} + // ErrorResponse -> error formatter func ErrorResponse(error string, writer http.ResponseWriter) { type errdata struct { From 29640e677a4291ffd655182b04ee86bda81917db Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Wed, 2 Aug 2023 20:28:05 +0500 Subject: [PATCH 36/40] update create challenge response --- handlers/response.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/handlers/response.go b/handlers/response.go index 627be74..268ffdc 100644 --- a/handlers/response.go +++ b/handlers/response.go @@ -113,13 +113,13 @@ func SuccessResponse(msg string, writer http.ResponseWriter) { json.NewEncoder(writer).Encode(temp) } -func SuccessResponseWithData(msg string, data interface{}, writer http.ResponseWriter) { +func SuccessResponseWithData(msg string, Id interface{}, writer http.ResponseWriter) { type errdata struct { Statuscode int `json:"status"` Message string `json:"msg"` - Data interface{} `json:"data"` + Id interface{} `json:"id"` } - temp := &errdata{Statuscode: 200, Message: msg, Data: data} + temp := &errdata{Statuscode: 200, Message: msg, Id: Id} //Send header, status code and output to writer writer.Header().Set("Content-Type", "application/json") From a2b97ed648e3bbd59f1e0c88e27e124236b78ae4 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 4 Aug 2023 01:00:41 +0500 Subject: [PATCH 37/40] remove little bug --- controllers/challengeController.go | 45 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/controllers/challengeController.go b/controllers/challengeController.go index 95a9003..37902ff 100644 --- a/controllers/challengeController.go +++ b/controllers/challengeController.go @@ -377,34 +377,35 @@ var ChallengeWinner = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Requ if checkChallengeWins { - if challenges.Goal == "distance" { - floatGoalThershold, err := strconv.ParseFloat(challenges.GoalThreshold, 32) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - for _, v := range steps { - floatUserDistance, err := strconv.ParseFloat(v.StepsDistance, 64) - if err != nil { - middlewares.ServerErrResponse(err.Error(), rw) - return - } - if floatUserDistance > floatGoalThershold { - winnerRecord = append(winnerRecord, v) - } - } - } else if challenges.Goal == "count" { - intGoalThershold, err := strconv.ParseInt(challenges.GoalThreshold, 10, 64) + // if challenges.Goal == "distance" { + floatGoalThershold, err := strconv.ParseFloat(challenges.GoalThreshold, 32) + if err != nil { + middlewares.ServerErrResponse(err.Error(), rw) + return + } + for _, v := range steps { + floatUserDistance, err := strconv.ParseFloat(v.StepsDistance, 64) if err != nil { middlewares.ServerErrResponse(err.Error(), rw) return } - for _, v := range steps { - if v.StepsCount > intGoalThershold { - winnerRecord = append(winnerRecord, v) - } + if floatUserDistance >= floatGoalThershold { + winnerRecord = append(winnerRecord, v) } } + // } + // else if challenges.Goal == "count" { + // intGoalThershold, err := strconv.ParseInt(challenges.GoalThreshold, 10, 64) + // if err != nil { + // middlewares.ServerErrResponse(err.Error(), rw) + // return + // } + // for _, v := range steps { + // if v.StepsCount > intGoalThershold { + // winnerRecord = append(winnerRecord, v) + // } + // } + // } var totalAmount float64 From f5828a0afda89fa58e0911304be1ebb134738db2 Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 4 Aug 2023 20:38:05 +0500 Subject: [PATCH 38/40] update username --- controllers/userController.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controllers/userController.go b/controllers/userController.go index fb2f084..52f5959 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -51,6 +51,8 @@ var RegisterUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request middlewares.ServerErrResponse(err.Error(), rw) return } + toLowerCase := strings.ToLower(user.Username) + user.Username = toLowerCase user.Password = passwordHash result, err := collection.InsertOne(r.Context(), user) if err != nil { From 2360de3b4361c255542fbc25fad68220f950d76c Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Fri, 4 Aug 2023 21:46:16 +0500 Subject: [PATCH 39/40] update username --- controllers/userController.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controllers/userController.go b/controllers/userController.go index 52f5959..3d0fe6e 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -29,9 +29,10 @@ var RegisterUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request middlewares.ServerErrResponse(err.Error(), rw) return } + toLowerCase := strings.ToLower(user.Username) collection := client.Database("challenge").Collection("users") var existingUser models.User - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: toLowerCase}}).Decode(&existingUser) if err == nil { middlewares.ErrorResponse("Username is already taken.", rw) return @@ -51,7 +52,6 @@ var RegisterUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request middlewares.ServerErrResponse(err.Error(), rw) return } - toLowerCase := strings.ToLower(user.Username) user.Username = toLowerCase user.Password = passwordHash result, err := collection.InsertOne(r.Context(), user) @@ -71,9 +71,10 @@ var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { middlewares.ServerErrResponse(err.Error(), rw) return } + toLowerCase := strings.ToLower(user.Username) collection := client.Database("challenge").Collection("users") var existingUser models.User - err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: user.Username}}).Decode(&existingUser) + err = collection.FindOne(r.Context(), bson.D{primitive.E{Key: "username", Value: toLowerCase}}).Decode(&existingUser) if err != nil { middlewares.ErrorResponse("User doesn't exist", rw) From e51c761ce4e5b1a4917dceb5fac718b0e8b40a9e Mon Sep 17 00:00:00 2001 From: TalentedSunday Date: Sat, 12 Aug 2023 18:08:59 +0500 Subject: [PATCH 40/40] fix username bug --- controllers/userController.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/userController.go b/controllers/userController.go index 3d0fe6e..7902536 100644 --- a/controllers/userController.go +++ b/controllers/userController.go @@ -85,7 +85,7 @@ var LoginUser = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { middlewares.ErrorResponse("Password doesn't match", rw) return } - token, err := middlewares.GenerateJWT(user.Username, existingUser.Identity, existingUser.PrivateKey) + token, err := middlewares.GenerateJWT(toLowerCase, existingUser.Identity, existingUser.PrivateKey) if err != nil { middlewares.ErrorResponse("Failed to generate JWT", rw) return