├── .env.example ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── controllers ├── auth_controller.go └── greeter_controller.go ├── coverage.out ├── database ├── collections.go └── database.go ├── docker-compose.yml ├── go.mod ├── go.sum ├── helpers └── respond_with_error.go ├── jobs ├── greeter_job.go ├── greeter_job_mail.go └── jobs.go ├── log └── letslog.go ├── mail ├── mail.go └── templates │ └── greeter-template.html ├── main.go ├── routes ├── auth_routes.go └── routes.go ├── runner.conf ├── tests └── main_test.go └── types ├── access_token_response.go ├── credentials_response.go └── user.go /.env.example: -------------------------------------------------------------------------------- 1 | PORT=:8080 2 | DATABASE_HOST=127.0.0.1 3 | DATABASE_PORT=27017 4 | DATABASE=letsgo_testdb 5 | 6 | LOG_LEVEL=debug 7 | 8 | MAIL_HOST=smtp.mailtrap.io 9 | MAIL_PORT=2525 10 | MAIL_USERNAME=null 11 | MAIL_PASSWORD=null 12 | 13 | SERVE_TLS=false 14 | CERTIFICATE_LOCATION= 15 | KEY_FILE_LOCATION= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor/ 3 | .env 4 | .env.testing 5 | letsgo 6 | log/letsgo.log 7 | tmp/ 8 | tests/log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.11.x 5 | 6 | services: 7 | - mongodb 8 | 9 | git: 10 | depth: 1 11 | 12 | before_script: 13 | - cp .env.example .env 14 | - cp .env.example .env.testing 15 | script: 16 | - go test ./tests/main_test.go -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang as builder 2 | 3 | ENV GO111MODULE=on 4 | 5 | WORKDIR /app 6 | 7 | COPY go.mod . 8 | COPY go.sum . 9 | 10 | RUN go mod download 11 | 12 | COPY . . 13 | 14 | RUN CGO_ENABLED=0 GODS=linux GOARCH=amd64 go build 15 | 16 | FROM ubuntu 17 | 18 | COPY --from=builder /app/letsgo /app/ 19 | COPY --from=builder /app/.env /app/ 20 | 21 | RUN mkdir /app/log 22 | 23 | EXPOSE 8080 24 | ENTRYPOINT ["/app/letsgo"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 letsgo-framework 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # letsgo 2 | [![Build Status](https://travis-ci.org/letsgo-framework/letsgo.svg?branch=master)](https://travis-ci.org/letsgo-framework/letsgo) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/letsgo-framework/letsgo)](https://goreportcard.com/report/github.com/letsgo-framework/letsgo) 4 | [![Coverage Status](https://coveralls.io/repos/github/letsgo-framework/letsgo/badge.svg?branch=master)](https://coveralls.io/github/letsgo-framework/letsgo?branch=master) 5 | [![Sourcegraph](https://sourcegraph.com/github.com/letsgo-framework/letsgo/-/badge.svg)](https://sourcegraph.com/github.com/letsgo-framework/letsgo?badge) 6 | [![Join the chat at https://gitter.im/letsgo-framework/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/letsgo-framework/community) 7 | 8 | ## Go api starter 9 | ### Ingredients 10 | 11 | - Go 12 | - [gin] ( https://github.com/gin-gonic/gin ) 13 | - [mongodb] ( https://www.mongodb.com/ ) 14 | - [mongo-go-driver] ( https://github.com/mongodb/mongo-go-driver ) 15 | - [oauth2] ( https://github.com/golang/oauth2 ) 16 | - [check] ( https://godoc.org/gopkg.in/check.v1 ) 17 | - [godotenv] ( https://github.com/joho/godotenv ) 18 | - [go-oauth2/gin-server] ( github.com/go-oauth2/gin-server ) 19 | - [cors] ( github.com/gin-contrib/cors ) 20 | *** 21 | ### Directory Structure 22 | 23 | By default, your project's structure will look like this: 24 | 25 | - `/controllers`: contains the core code of your application. 26 | - `/database`: contains mongo-go-driver connector. 27 | - `/helpers`: contains helpers functions of your application. 28 | - `/middlewares`: contains middlewares of your application. 29 | - `/routes`: directory contains RESTful api routes of your application. 30 | - `/tests`: contains tests of your application. 31 | - `/types`: contains the types/structures of your application. 32 | *** 33 | ### Environment Configuration 34 | 35 | letsGo uses `godotenv` for setting environment variables. The root directory of your application will contain a `.env.example` file. 36 | copy and rename it to `.env` to set your environment variables. 37 | 38 | You need to create a `.env.testing` file from `.env.example` for running tests. 39 | *** 40 | ### Setting up 41 | 42 | - clone letsGo 43 | - change package name in `go.mod` to your package name 44 | - change the internal package (controllers, tests, helpers etc.) paths as per your requirement 45 | - setup `.env` and `.env.testing` 46 | - run `go mod download` to install dependencies 47 | 48 | #### OR `letsgo-cli` can be used to setup new project 49 | 50 | ### install letsgo-cli 51 | ``` 52 | go get github.com/letsgo-framework/letsgo-cli 53 | ``` 54 | 55 | 56 | ### Create a new project 57 | 58 | ```bash 59 | letsgo-cli init 60 | ``` 61 | 62 | - **letsgo-cli init github.com myapp**
63 | Generates a new project called **myapp** in your `GOPATH` inside `github.com` and installs the default plugins through the glide. 64 | *** 65 | ### Run : ```go run main.go``` 66 | *** 67 | ### Build : ```go build``` 68 | *** 69 | ### Test : ```go test tests/main_test.go``` 70 | 71 | ### Coverall : 72 | ``` 73 | go test -v -coverpkg=./... -coverprofile=coverage.out ./... 74 | 75 | goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN 76 | ``` 77 | *** 78 | ### Authentication 79 | 80 | letsgo uses Go OAuth2 (https://godoc.org/golang.org/x/oauth2) for authentication. 81 | *** 82 | 83 | ### Deploy into Docker 84 | 85 | ``` 86 | sudo docker run --rm -v "$PWD":/go/src/github.com/letsgo-framework/letsgo -w /go/src/github.com/letsgo-framework/letsgo iron/go:dev go build -o letsgo 87 | ``` 88 | ``` 89 | sudo docker build -t sab94/letsgo . 90 | ``` 91 | ``` 92 | sudo docker run --rm -p 8080:8080 sab94/letsgo 93 | ``` 94 | 95 | # Thank You 96 | [![https://www.jetbrains.com/?from=letsgo](https://user-images.githubusercontent.com/15252513/68403814-8f147100-01a3-11ea-9dbc-f51d36de3ef3.png)](https://www.jetbrains.com/?from=letsgo) 97 | -------------------------------------------------------------------------------- /controllers/auth_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Authentication Controller 4 | |-------------------------------------------------------------------------- 5 | | 6 | | GetCredentials works on oauth2 Client Credentials Grant and returns CLIENT_ID, CLIENT_SECRET 7 | | GetToken takes CLIENT_ID, CLIENT_SECRET, grant_type, scope and returns access_token and some other information 8 | */ 9 | 10 | package controllers 11 | 12 | import ( 13 | "github.com/gin-gonic/gin" 14 | "github.com/go-oauth2/gin-server" 15 | "github.com/google/uuid" 16 | "github.com/letsgo-framework/letsgo/database" 17 | letslog "github.com/letsgo-framework/letsgo/log" 18 | "github.com/letsgo-framework/letsgo/types" 19 | "go.mongodb.org/mongo-driver/bson" 20 | "go.mongodb.org/mongo-driver/bson/primitive" 21 | "golang.org/x/crypto/bcrypt" 22 | "golang.org/x/net/context" 23 | "gopkg.in/oauth2.v3/manage" 24 | "gopkg.in/oauth2.v3/models" 25 | "gopkg.in/oauth2.v3/server" 26 | "gopkg.in/oauth2.v3/store" 27 | "time" 28 | ) 29 | 30 | var clientStore = store.NewClientStore() 31 | var manager = manage.NewDefaultManager() 32 | 33 | // AuthInit initializes authentication 34 | func AuthInit() { 35 | cfg := &manage.Config{ 36 | // access token expiration time 37 | AccessTokenExp: time.Hour * 2, 38 | // refresh token expiration time 39 | RefreshTokenExp: time.Hour * 24 * 7, 40 | // whether to generate the refreshing token 41 | IsGenerateRefresh: true, 42 | } 43 | 44 | manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg) 45 | manager.SetPasswordTokenCfg(cfg) 46 | 47 | // token memory store 48 | manager.MustTokenStorage(store.NewMemoryTokenStore()) 49 | 50 | manager.MapClientStorage(clientStore) 51 | 52 | ginserver.InitServer(manager) 53 | ginserver.SetAllowGetAccessRequest(true) 54 | ginserver.SetClientInfoHandler(server.ClientFormHandler) 55 | 56 | ginserver.SetPasswordAuthorizationHandler(login) 57 | 58 | err := clientStore.Set("client@letsgo", &models.Client{ 59 | ID: "client@letsgo", 60 | Secret: "Va4a8bFFhTJZdybnzyhjHjj6P9UVh7UL", 61 | Domain: "http://localhost:8080", 62 | }) 63 | 64 | if err != nil { 65 | letslog.Error(err.Error()) 66 | } 67 | } 68 | 69 | // GetCredentials sends client credentials 70 | func GetCredentials(c *gin.Context) { 71 | clientId := uuid.New().String() 72 | clientSecret := uuid.New().String() 73 | err := clientStore.Set(clientId, &models.Client{ 74 | ID: clientId, 75 | Secret: clientSecret, 76 | Domain: "http://localhost:8000", 77 | }) 78 | if err != nil { 79 | letslog.Error(err.Error()) 80 | } 81 | c.JSON(200, map[string]string{"CLIENT_ID": clientId, "CLIENT_SECRET": clientSecret}) 82 | c.Done() 83 | } 84 | 85 | // GetToken sends accecc_token 86 | func GetToken(c *gin.Context) { 87 | ginserver.HandleTokenRequest(c) 88 | } 89 | 90 | // Verify accessToken with client 91 | func Verify(c *gin.Context) { 92 | ti, exists := c.Get(ginserver.DefaultConfig.TokenKey) 93 | if exists { 94 | c.JSON(200, ti) 95 | return 96 | } 97 | c.String(200, "not found") 98 | } 99 | 100 | // register 101 | func Register (c *gin.Context) { 102 | a := types.User{} 103 | ctx := context.Background() 104 | collection := database.UserCollection() 105 | err := c.BindJSON(&a) 106 | a.Password,_ = generateHash(a.Password) 107 | a.Id = primitive.NewObjectID() 108 | 109 | 110 | if err != nil { 111 | letslog.Error(err.Error()) 112 | c.Abort() 113 | } 114 | _, err = collection.InsertOne(ctx, a) 115 | if err != nil { 116 | letslog.Error(err.Error()) 117 | c.Abort() 118 | } 119 | c.JSON(200, map[string]string{"message" : "Registration Successful"}) 120 | c.Done() 121 | } 122 | 123 | // Generate a salted hash for the input string 124 | func generateHash(s string) (string, error) { 125 | saltedBytes := []byte(s) 126 | hashedBytes, err := bcrypt.GenerateFromPassword(saltedBytes, bcrypt.DefaultCost) 127 | if err != nil { 128 | return "", err 129 | } 130 | 131 | hash := string(hashedBytes[:]) 132 | return hash, nil 133 | } 134 | 135 | // Compare string to generated hash 136 | func compare(hash string, s string) error { 137 | incoming := []byte(s) 138 | existing := []byte(hash) 139 | 140 | return bcrypt.CompareHashAndPassword(existing, incoming) 141 | } 142 | 143 | func login (username, password string) (userID string, err error) { 144 | 145 | collection := database.UserCollection() 146 | 147 | user := types.User{} 148 | err = collection.FindOne(context.Background(), bson.M{"username": username}).Decode(&user) 149 | 150 | if err != nil { 151 | letslog.Error(err.Error()) 152 | return userID, err 153 | } 154 | loginError := compare(user.Password, password) 155 | 156 | if loginError != nil { 157 | letslog.Error(loginError.Error()) 158 | return userID, err 159 | } else { 160 | userID = user.Id.Hex() 161 | return userID, err 162 | } 163 | } 164 | 165 | // Returns ObjectId of logged in user 166 | func AuthId(c *gin.Context) (primitive.ObjectID, error) { 167 | ti, _ := c.Get(ginserver.DefaultConfig.TokenKey) 168 | token := ti.(*models.Token) 169 | return primitive.ObjectIDFromHex(token.UserID) 170 | } 171 | 172 | // Returns Client of the logged in user 173 | func AuthClient(c *gin.Context) string { 174 | ti, _ := c.Get(ginserver.DefaultConfig.TokenKey) 175 | token := ti.(*models.Token) 176 | return token.ClientID 177 | } 178 | 179 | // Returns logged in user 180 | func AuthUser(c *gin.Context) (types.User, error) { 181 | id, _ := AuthId(c) 182 | user := types.User{} 183 | collection := database.UserCollection() 184 | err := collection.FindOne(context.Background(), bson.M{"_id": id}).Decode(&user) 185 | 186 | return user, err 187 | } -------------------------------------------------------------------------------- /controllers/greeter_controller.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | // Welcome !! The content below is only a placeholder and can be replaced. 8 | type Welcome struct { 9 | Greet string `json:"greet"` 10 | Doc string `json:"link_to_doc"` 11 | Github string `json:"github"` 12 | Examples string `json:"examples"` 13 | } 14 | 15 | // Greet is the response for api/v1 16 | func Greet(c *gin.Context) { 17 | 18 | welcome := Welcome{ 19 | Greet: "Welcome to letsGo", 20 | Doc: "https://letsgo-framework.github.io/", 21 | Github: "https://github.com/letsgo-framework/letsgo", 22 | Examples: "Coming Soon", 23 | } 24 | c.JSON(200, welcome) 25 | c.Done() 26 | } 27 | -------------------------------------------------------------------------------- /coverage.out: -------------------------------------------------------------------------------- 1 | mode: set 2 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:34.17,64.16 11 1 3 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:64.16,66.3 1 0 4 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:70.37,78.16 4 1 5 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:81.2,82.10 2 1 6 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:78.16,80.3 1 0 7 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:86.31,88.2 1 1 8 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:91.29,93.12 2 1 9 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:97.2,97.28 1 0 10 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:93.12,96.3 2 1 11 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:101.32,110.16 7 1 12 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:114.2,115.16 2 1 13 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:119.2,120.10 2 1 14 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:110.16,113.3 2 0 15 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:115.16,118.3 2 0 16 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:124.45,127.16 3 1 17 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:131.2,132.18 2 1 18 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:127.16,129.3 1 0 19 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:136.43,141.2 3 1 20 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:143.67,150.16 4 1 21 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:154.2,156.23 2 1 22 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:150.16,153.3 2 0 23 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:156.23,159.3 2 0 24 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:159.8,162.3 2 1 25 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:166.57,170.2 3 0 26 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:173.40,177.2 3 0 27 | github.com/letsgo-framework/letsgo/controllers/auth_controller.go:180.51,187.2 5 0 28 | github.com/letsgo-framework/letsgo/helpers/respond_with_error.go:6.70,8.2 1 1 29 | github.com/letsgo-framework/letsgo/mail/mail.go:27.42,29.2 1 0 30 | github.com/letsgo-framework/letsgo/mail/mail.go:31.47,44.2 9 0 31 | github.com/letsgo-framework/letsgo/mail/mail.go:46.82,66.16 15 0 32 | github.com/letsgo-framework/letsgo/mail/mail.go:69.2,69.47 1 0 33 | github.com/letsgo-framework/letsgo/mail/mail.go:76.2,76.41 1 0 34 | github.com/letsgo-framework/letsgo/mail/mail.go:80.2,80.45 1 0 35 | github.com/letsgo-framework/letsgo/mail/mail.go:83.2,83.26 1 0 36 | github.com/letsgo-framework/letsgo/mail/mail.go:89.2,90.16 2 0 37 | github.com/letsgo-framework/letsgo/mail/mail.go:94.2,95.16 2 0 38 | github.com/letsgo-framework/letsgo/mail/mail.go:99.2,100.16 2 0 39 | github.com/letsgo-framework/letsgo/mail/mail.go:104.2,106.39 2 0 40 | github.com/letsgo-framework/letsgo/mail/mail.go:66.16,68.3 1 0 41 | github.com/letsgo-framework/letsgo/mail/mail.go:69.47,71.48 2 0 42 | github.com/letsgo-framework/letsgo/mail/mail.go:71.48,73.4 1 0 43 | github.com/letsgo-framework/letsgo/mail/mail.go:76.41,78.3 1 0 44 | github.com/letsgo-framework/letsgo/mail/mail.go:80.45,82.3 1 0 45 | github.com/letsgo-framework/letsgo/mail/mail.go:83.26,84.39 1 0 46 | github.com/letsgo-framework/letsgo/mail/mail.go:84.39,86.4 1 0 47 | github.com/letsgo-framework/letsgo/mail/mail.go:90.16,92.3 1 0 48 | github.com/letsgo-framework/letsgo/mail/mail.go:95.16,97.3 1 0 49 | github.com/letsgo-framework/letsgo/mail/mail.go:100.16,102.3 1 0 50 | github.com/letsgo-framework/letsgo/jobs/greeter_job_mail.go:7.22,13.2 1 0 51 | github.com/letsgo-framework/letsgo/database/collections.go:5.41,7.2 1 1 52 | github.com/letsgo-framework/letsgo/controllers/greeter_controller.go:16.28,26.2 3 1 53 | github.com/letsgo-framework/letsgo/jobs/jobs.go:7.23,16.2 5 1 54 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:10.54,15.54 2 1 55 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:24.2,28.2 5 1 56 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:32.2,32.13 1 1 57 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:15.54,17.4 1 1 58 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:19.38,21.4 1 1 59 | github.com/letsgo-framework/letsgo/routes/auth_routes.go:28.2,30.3 1 1 60 | github.com/letsgo-framework/letsgo/routes/routes.go:21.31,32.2 5 1 61 | github.com/letsgo-framework/letsgo/routes/routes.go:38.2,38.10 1 1 62 | github.com/letsgo-framework/letsgo/routes/routes.go:32.2,36.3 3 1 63 | github.com/letsgo-framework/letsgo/main.go:11.13,19.16 3 0 64 | github.com/letsgo-framework/letsgo/main.go:25.2,30.16 4 0 65 | github.com/letsgo-framework/letsgo/main.go:34.2,34.38 1 0 66 | github.com/letsgo-framework/letsgo/main.go:19.16,21.3 1 0 67 | github.com/letsgo-framework/letsgo/main.go:21.8,23.3 1 0 68 | github.com/letsgo-framework/letsgo/main.go:30.16,32.3 1 0 69 | github.com/letsgo-framework/letsgo/main.go:34.38,36.3 1 0 70 | github.com/letsgo-framework/letsgo/main.go:36.8,38.3 1 0 71 | github.com/letsgo-framework/letsgo/log/letslog.go:59.22,61.20 2 1 72 | github.com/letsgo-framework/letsgo/log/letslog.go:65.2,65.17 1 1 73 | github.com/letsgo-framework/letsgo/log/letslog.go:61.20,63.3 1 1 74 | github.com/letsgo-framework/letsgo/log/letslog.go:69.37,70.22 1 1 75 | github.com/letsgo-framework/letsgo/log/letslog.go:75.2,75.15 1 0 76 | github.com/letsgo-framework/letsgo/log/letslog.go:70.22,71.13 1 1 77 | github.com/letsgo-framework/letsgo/log/letslog.go:71.13,73.4 1 1 78 | github.com/letsgo-framework/letsgo/log/letslog.go:78.75,84.23 5 1 79 | github.com/letsgo-framework/letsgo/log/letslog.go:84.23,85.48 1 1 80 | github.com/letsgo-framework/letsgo/log/letslog.go:85.48,89.24 4 1 81 | github.com/letsgo-framework/letsgo/log/letslog.go:89.24,91.5 1 1 82 | github.com/letsgo-framework/letsgo/log/letslog.go:93.8,94.48 1 0 83 | github.com/letsgo-framework/letsgo/log/letslog.go:94.48,98.24 4 0 84 | github.com/letsgo-framework/letsgo/log/letslog.go:98.24,100.5 1 0 85 | github.com/letsgo-framework/letsgo/log/letslog.go:105.76,111.23 5 1 86 | github.com/letsgo-framework/letsgo/log/letslog.go:111.23,112.33 1 1 87 | github.com/letsgo-framework/letsgo/log/letslog.go:112.33,116.24 4 0 88 | github.com/letsgo-framework/letsgo/log/letslog.go:116.24,119.5 2 0 89 | github.com/letsgo-framework/letsgo/log/letslog.go:121.8,122.33 1 0 90 | github.com/letsgo-framework/letsgo/log/letslog.go:122.33,126.24 4 0 91 | github.com/letsgo-framework/letsgo/log/letslog.go:126.24,129.5 2 0 92 | github.com/letsgo-framework/letsgo/log/letslog.go:134.38,139.2 3 1 93 | github.com/letsgo-framework/letsgo/log/letslog.go:141.23,143.2 1 0 94 | github.com/letsgo-framework/letsgo/log/letslog.go:145.21,159.27 4 1 95 | github.com/letsgo-framework/letsgo/log/letslog.go:169.2,169.27 1 1 96 | github.com/letsgo-framework/letsgo/log/letslog.go:179.2,179.27 1 1 97 | github.com/letsgo-framework/letsgo/log/letslog.go:188.2,188.27 1 1 98 | github.com/letsgo-framework/letsgo/log/letslog.go:198.2,198.43 1 1 99 | github.com/letsgo-framework/letsgo/log/letslog.go:159.27,168.3 8 1 100 | github.com/letsgo-framework/letsgo/log/letslog.go:169.27,178.3 8 0 101 | github.com/letsgo-framework/letsgo/log/letslog.go:179.27,187.3 7 0 102 | github.com/letsgo-framework/letsgo/log/letslog.go:188.27,196.3 7 0 103 | github.com/letsgo-framework/letsgo/database/database.go:33.49,35.17 2 1 104 | github.com/letsgo-framework/letsgo/database/database.go:40.2,44.16 4 1 105 | github.com/letsgo-framework/letsgo/database/database.go:47.2,49.16 3 1 106 | github.com/letsgo-framework/letsgo/database/database.go:54.2,56.19 2 1 107 | github.com/letsgo-framework/letsgo/database/database.go:35.17,39.3 3 1 108 | github.com/letsgo-framework/letsgo/database/database.go:44.16,46.3 1 0 109 | github.com/letsgo-framework/letsgo/database/database.go:49.16,51.3 1 1 110 | github.com/letsgo-framework/letsgo/database/database.go:51.8,53.3 1 0 111 | github.com/letsgo-framework/letsgo/database/database.go:60.53,62.16 2 1 112 | github.com/letsgo-framework/letsgo/database/database.go:65.2,66.17 2 1 113 | github.com/letsgo-framework/letsgo/database/database.go:71.2,76.16 6 1 114 | github.com/letsgo-framework/letsgo/database/database.go:79.2,80.16 2 1 115 | github.com/letsgo-framework/letsgo/database/database.go:86.2,86.19 1 1 116 | github.com/letsgo-framework/letsgo/database/database.go:62.16,64.3 1 0 117 | github.com/letsgo-framework/letsgo/database/database.go:66.17,70.3 3 1 118 | github.com/letsgo-framework/letsgo/database/database.go:76.16,78.3 1 0 119 | github.com/letsgo-framework/letsgo/database/database.go:80.16,82.3 1 1 120 | github.com/letsgo-framework/letsgo/database/database.go:82.8,84.3 1 0 121 | github.com/letsgo-framework/letsgo/jobs/greeter_job.go:6.14,8.2 1 0 122 | -------------------------------------------------------------------------------- /database/collections.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import "go.mongodb.org/mongo-driver/mongo" 4 | 5 | func UserCollection() *mongo.Collection { 6 | return DB.Collection("users") 7 | } 8 | -------------------------------------------------------------------------------- /database/database.go: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | Mongo Database Connection 4 | |-------------------------------------------------------------------------- 5 | | 6 | | We are using mongo-go-driver to connect to mongodb 7 | | Connect is used to make connection 8 | | TestConnect is used to make connection while running tests 9 | | 10 | */ 11 | 12 | package database 13 | 14 | import ( 15 | "context" 16 | "fmt" 17 | "github.com/joho/godotenv" 18 | letslog "github.com/letsgo-framework/letsgo/log" 19 | "go.mongodb.org/mongo-driver/mongo" 20 | "go.mongodb.org/mongo-driver/mongo/options" 21 | "go.mongodb.org/mongo-driver/mongo/readpref" 22 | "os" 23 | "time" 24 | ) 25 | 26 | // DB is pointer of Mongo Database 27 | var DB *mongo.Database 28 | 29 | // Client pointer of Mongo Client 30 | var Client *mongo.Client 31 | 32 | // Connect to database 33 | func Connect() (*mongo.Client, *mongo.Database) { 34 | dbURL := os.Getenv("DATABASE_URL") 35 | if dbURL == "" { 36 | dbHost := os.Getenv("DATABASE_HOST") 37 | dbPort := os.Getenv("DATABASE_PORT") 38 | dbURL = fmt.Sprintf("mongodb://%s:%s", dbHost, dbPort) 39 | } 40 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 41 | defer cancel() 42 | 43 | client, err := mongo.Connect(ctx, options.Client().ApplyURI(dbURL)) 44 | if err != nil { 45 | letslog.Fatal(err) 46 | } 47 | Client = client 48 | err = Client.Ping(context.Background(), readpref.Primary()) 49 | if err == nil { 50 | letslog.Info("Connected to MongoDB!") 51 | } else { 52 | letslog.Error("Could not connect to MongoDB! Please check if mongo is running.") 53 | } 54 | DB = Client.Database(os.Getenv("DATABASE")) 55 | 56 | return Client, DB 57 | } 58 | 59 | // TestConnect to database while testing 60 | func TestConnect() (*mongo.Client, *mongo.Database) { 61 | err := godotenv.Load("../.env.testing") 62 | if err != nil { 63 | letslog.Error("Error loading .env.testing file") 64 | } 65 | dbURL := os.Getenv("DATABASE_URL") 66 | if dbURL == "" { 67 | dbHost := os.Getenv("DATABASE_HOST") 68 | dbPort := os.Getenv("DATABASE_PORT") 69 | dbURL = fmt.Sprintf("mongodb://%s:%s", dbHost, dbPort) 70 | } 71 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 72 | defer cancel() 73 | client, err := mongo.Connect(ctx, options.Client().ApplyURI(dbURL)) 74 | Client = client 75 | DB = Client.Database(os.Getenv("DATABASE")) 76 | if err != nil { 77 | letslog.Fatal(err) 78 | } 79 | err = Client.Ping(context.Background(), readpref.Primary()) 80 | if err == nil { 81 | letslog.Info("Connected to MongoDB for testing!") 82 | } else { 83 | letslog.Error("Could not connect to MongoDB!") 84 | } 85 | 86 | return Client, DB 87 | } 88 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | app: 4 | container_name: letsgo 5 | restart: always 6 | build: . 7 | ports: 8 | - "8080:8080" 9 | links: 10 | - mongo 11 | mongo: 12 | container_name: mongo 13 | image: mongo 14 | volumes: 15 | - /var/mongo/data:/data/db 16 | ports: 17 | - "27017:27017" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/letsgo-framework/letsgo 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gin-contrib/cors v1.3.0 7 | github.com/gin-contrib/sse v0.1.0 // indirect 8 | github.com/gin-gonic/gin v1.4.0 9 | github.com/go-oauth2/gin-server v0.0.0-20190119052936-0d298614c9ba 10 | github.com/go-stack/stack v1.8.0 // indirect 11 | github.com/golang/snappy v0.0.1 // indirect 12 | github.com/google/uuid v1.1.1 13 | github.com/joho/godotenv v1.3.0 14 | github.com/mattn/go-colorable v0.1.2 15 | github.com/robfig/cron v1.2.0 16 | github.com/ugorji/go v1.1.7 // indirect 17 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect 18 | github.com/xdg/stringprep v1.0.0 // indirect 19 | go.mongodb.org/mongo-driver v1.0.4 20 | golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 21 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c 22 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 23 | gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 24 | gopkg.in/oauth2.v3 v3.10.1 25 | ) 26 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 6 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 7 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 8 | github.com/gavv/httpexpect v0.0.0-20180803094507-bdde30871313/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= 9 | github.com/gavv/monotime v0.0.0-20171021193802-6f8212e8d10d/go.mod h1:vmp8DIyckQMXOPl0AQVHt+7n5h7Gb7hS6CUydiV8QeA= 10 | github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg= 11 | github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc= 12 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= 13 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 14 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 15 | github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= 16 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= 17 | github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= 18 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= 19 | github.com/go-oauth2/gin-server v0.0.0-20190119052936-0d298614c9ba h1:p6Ycjrd5VHAZ6P0CBJceJ+MJwr2NgIF8uhofgrMsXtM= 20 | github.com/go-oauth2/gin-server v0.0.0-20190119052936-0d298614c9ba/go.mod h1:f08F3l5/Pbayb4pjnv5PpUdQLFejgGfHrTjA6IZb0eM= 21 | github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0= 22 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 23 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 24 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 25 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 26 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 27 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 28 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 29 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 30 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 31 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 32 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 33 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 34 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 35 | github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= 36 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 37 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 38 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 39 | github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 40 | github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= 41 | github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 42 | github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 43 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 44 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 45 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 46 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 47 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 48 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 49 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 50 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 51 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 52 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 53 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 54 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 55 | github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= 56 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 57 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 58 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 59 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 60 | github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= 61 | github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= 62 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 63 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 64 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= 65 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 66 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 67 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 68 | github.com/tidwall/btree v0.0.0-20170113224114-9876f1454cf0 h1:QnyrPZZvPmR0AtJCxxfCtI1qN+fYpKTKJ/5opWmZ34k= 69 | github.com/tidwall/btree v0.0.0-20170113224114-9876f1454cf0/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8= 70 | github.com/tidwall/buntdb v1.0.0 h1:urIJqQ8OR9fibXXtFSu8sR5arMqZK8ZnNq22yWl+A+8= 71 | github.com/tidwall/buntdb v1.0.0/go.mod h1:Y39xhcDW10WlyYXeLgGftXVbjtM0QP+/kpz8xl9cbzE= 72 | github.com/tidwall/gjson v1.1.3 h1:u4mspaByxY+Qk4U1QYYVzGFI8qxN/3jtEV0ZDb2vRic= 73 | github.com/tidwall/gjson v1.1.3/go.mod h1:c/nTNbUr0E0OrXEhq1pwa8iEgc2DOt4ZZqAt1HtCkPA= 74 | github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7XByQEJVT8+9v0W/tIY0Oo4OwrE= 75 | github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M= 76 | github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= 77 | github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= 78 | github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo= 79 | github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao= 80 | github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE= 81 | github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ= 82 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 83 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 84 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 85 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 86 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 87 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 88 | github.com/valyala/fasthttp v1.0.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= 89 | github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= 90 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= 91 | github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= 92 | github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= 93 | github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= 94 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 95 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 96 | github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= 97 | github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= 98 | github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= 99 | github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= 100 | github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= 101 | go.mongodb.org/mongo-driver v1.0.4 h1:bHxbjH6iwh1uInchXadI6hQR107KEbgYsMzoblDONmQ= 102 | go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= 103 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 104 | golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 h1:mgAKeshyNqWKdENOnQsg+8dRTwZFIwFaO3HNl52sweA= 105 | golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 106 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 107 | golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 108 | golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4= 109 | golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 110 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 111 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= 112 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 113 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= 114 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 115 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 116 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 117 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= 118 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 119 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 120 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= 121 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 122 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 123 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 124 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 125 | gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 126 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 127 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 128 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= 129 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= 130 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= 131 | gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hTWHhDTQDk/m2gorfVHUEa9vo3tp3D7TzwjI= 132 | gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 133 | gopkg.in/oauth2.v3 v3.10.1 h1:/abis3O6tZFizY1/FgKGoDOmmu2ddvscBSSPdHVa6OI= 134 | gopkg.in/oauth2.v3 v3.10.1/go.mod h1:nTG+m2PRcHR9jzGNrGdxSsUKz7vvwkqSlhFrstgZcRU= 135 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 136 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= 137 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 138 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 139 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 140 | -------------------------------------------------------------------------------- /helpers/respond_with_error.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | // RespondWithError creates response for error 6 | func RespondWithError(c *gin.Context, code int, message interface{}) { 7 | c.AbortWithStatusJSON(code, gin.H{"error": message}) 8 | } 9 | -------------------------------------------------------------------------------- /jobs/greeter_job.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | letslog "github.com/letsgo-framework/letsgo/log" 5 | ) 6 | func Greet() { 7 | letslog.Debug("Hello Jobs") 8 | } -------------------------------------------------------------------------------- /jobs/greeter_job_mail.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | "github.com/letsgo-framework/letsgo/mail" 5 | ) 6 | 7 | func GreetingMail() { 8 | mail.SendMail([]string{"greet@letsgo.com"}, "Greetings", "greeter-template.html", struct { 9 | App string 10 | }{ 11 | App: "LetsGO", 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /jobs/jobs.go: -------------------------------------------------------------------------------- 1 | package jobs 2 | 3 | import ( 4 | "github.com/robfig/cron" 5 | ) 6 | 7 | func Run() *cron.Cron { 8 | c := cron.New() 9 | 10 | // Add Jobs here 11 | c.AddFunc("@every 1m", Greet) 12 | c.AddFunc("@every 1m", GreetingMail) 13 | 14 | c.Start() 15 | return c 16 | } -------------------------------------------------------------------------------- /log/letslog.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mattn/go-colorable" 6 | "gopkg.in/natefinch/lumberjack.v2" 7 | "log" 8 | "os" 9 | "time" 10 | ) 11 | 12 | var logger = log.New(colorable.NewColorableStderr(), "", 0) 13 | 14 | type logFunc func(string, ...interface{}) 15 | type logFuncInterface func(...interface{}) 16 | 17 | var ( 18 | Debug logFunc 19 | Debugln logFuncInterface 20 | Info logFunc 21 | Infoln logFuncInterface 22 | Warn logFunc 23 | Warnln logFuncInterface 24 | Error logFunc 25 | Errorln logFuncInterface 26 | ) 27 | 28 | var colors = map[string]string{ 29 | "reset": "0", 30 | "red": "31", 31 | "green": "32", 32 | "yellow": "33", 33 | "blue": "34", 34 | "magenta": "35", 35 | "cyan": "36", 36 | "bold_red": "31;1", 37 | "bold_green": "32;1", 38 | "bold_yellow": "33;1", 39 | "bold_blue": "34;1", 40 | "bold_magenta": "35;1", 41 | "bold_cyan": "36;1", 42 | "bright_red": "31;2", 43 | "bright_green": "32;2", 44 | "bright_yellow": "33;2", 45 | "bright_blue": "34;2", 46 | "bright_magenta": "35;2", 47 | "bright_cyan": "36;2", 48 | } 49 | 50 | var settings = map[string]string{ 51 | "log_color_debug": "cyan", 52 | "log_color_info": "green", 53 | "log_color_warn": "yellow", 54 | "log_color_error": "red", 55 | } 56 | 57 | var levels = []string{"error", "warn", "info", "debug"} 58 | 59 | func getenv() string { 60 | logLevel := os.Getenv("LOG_LEVEL") 61 | if logLevel == "" { 62 | logLevel = "debug" 63 | } 64 | 65 | return logLevel 66 | } 67 | 68 | 69 | func find(a []string, x string) int { 70 | for i, n := range a { 71 | if x == n { 72 | return i 73 | } 74 | } 75 | return len(a) 76 | } 77 | 78 | func newLogFunc(prefix string, omitLog bool) func(string, ...interface{}) { 79 | color, clear := "", "" 80 | color = fmt.Sprintf("\033[%sm", logColor(prefix)) 81 | clear = fmt.Sprintf("\033[%sm", colors["reset"]) 82 | prefix = fmt.Sprintf("%-11s", prefix) 83 | 84 | if prefix != "error" { 85 | return func(format string, v ...interface{}) { 86 | now := time.Now() 87 | timeString := fmt.Sprintf("%d:%d:%02d", now.Hour(), now.Minute(), now.Second()) 88 | format = fmt.Sprintf("%s%s %s |%s %s", color, timeString, prefix, clear, format) 89 | if omitLog == false { 90 | logger.Printf(format, v...) 91 | } 92 | } 93 | } else { 94 | return func(format string, v ...interface{}) { 95 | now := time.Now() 96 | timeString := fmt.Sprintf("%d:%d:%02d", now.Hour(), now.Minute(), now.Second()) 97 | format = fmt.Sprintf("%s%s %s |%s %s", color, timeString, prefix, clear, format) 98 | if omitLog == false { 99 | logger.Fatalf(format, v...) 100 | } 101 | } 102 | } 103 | } 104 | 105 | func newLogFuncInterface(prefix string, omitLog bool) func(...interface{}) { 106 | color, clear := "", "" 107 | color = fmt.Sprintf("\033[%sm", logColor(prefix)) 108 | clear = fmt.Sprintf("\033[%sm", colors["reset"]) 109 | prefix = fmt.Sprintf("%-11s", prefix) 110 | 111 | if prefix != "error" { 112 | return func(v ...interface{}) { 113 | now := time.Now() 114 | timeString := fmt.Sprintf("%d:%d:%02d", now.Hour(), now.Minute(), now.Second()) 115 | format := fmt.Sprintf("%s%s %s |%s", color, timeString, prefix, clear) 116 | if omitLog == false { 117 | logger.Println(format) 118 | logger.Println(v...) 119 | } 120 | } 121 | } else { 122 | return func(v ...interface{}) { 123 | now := time.Now() 124 | timeString := fmt.Sprintf("%d:%d:%02d", now.Hour(), now.Minute(), now.Second()) 125 | format := fmt.Sprintf("%s%s %s |%s", color, timeString, prefix, clear) 126 | if omitLog == false { 127 | logger.Println(format) 128 | logger.Println(v...) 129 | } 130 | } 131 | } 132 | } 133 | 134 | func logColor(logName string) string { 135 | settingsKey := fmt.Sprintf("log_color_%s", logName) 136 | colorName := settings[settingsKey] 137 | 138 | return colors[colorName] 139 | } 140 | 141 | func Fatal(err error) { 142 | logger.Fatal(err) 143 | } 144 | 145 | func InitLogFuncs() { 146 | // Configure Logging 147 | logger.SetOutput(&lumberjack.Logger{ 148 | Filename: "./log/letsgo.log", 149 | MaxSize: 500, // megabytes 150 | MaxBackups: 3, 151 | MaxAge: 28, //days 152 | Compress: true, // disabled by default 153 | }) 154 | 155 | targetLevel := getenv() 156 | 157 | targetLevelIndex := find(levels, targetLevel) 158 | 159 | if targetLevelIndex == 3 { 160 | Debug = newLogFunc("debug", false) 161 | Debugln = newLogFuncInterface("debug", false) 162 | Info = newLogFunc("info", false) 163 | Infoln = newLogFuncInterface("info", false) 164 | Warn = newLogFunc("warn", false) 165 | Warnln = newLogFuncInterface("warn", false) 166 | Error = newLogFunc("error", false) 167 | Errorln = newLogFuncInterface("error", false) 168 | } 169 | if targetLevelIndex == 2 { 170 | Debug = newLogFunc("debug", true) 171 | Debugln = newLogFuncInterface("debug", true) 172 | Info = newLogFunc("info", false) 173 | Infoln = newLogFuncInterface("info", false) 174 | Warn = newLogFunc("warn", false) 175 | Warnln = newLogFuncInterface("warn", false) 176 | Error = newLogFunc("error", false) 177 | Errorln = newLogFuncInterface("error", false) 178 | } 179 | if targetLevelIndex == 1 { 180 | Debug = newLogFunc("debug", true) 181 | Info = newLogFunc("info", true) 182 | Infoln = newLogFuncInterface("info", true) 183 | Warn = newLogFunc("warn", false) 184 | Warnln = newLogFuncInterface("warn", false) 185 | Error = newLogFunc("error", false) 186 | Errorln = newLogFuncInterface("error", false) 187 | } 188 | if targetLevelIndex == 0 { 189 | Debug = newLogFunc("debug", true) 190 | Info = newLogFunc("info", true) 191 | Infoln = newLogFuncInterface("info", true) 192 | Warn = newLogFunc("warn", true) 193 | Warnln = newLogFuncInterface("warn", true) 194 | Error = newLogFunc("error", false) 195 | Errorln = newLogFuncInterface("error", false) 196 | } 197 | 198 | Debug("Log level set to %s", targetLevel) 199 | } -------------------------------------------------------------------------------- /mail/mail.go: -------------------------------------------------------------------------------- 1 | package mail 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "fmt" 7 | "html/template" 8 | "log" 9 | "net/smtp" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | type Mail struct { 15 | senderId string 16 | toIds []string 17 | subject string 18 | template string 19 | data interface{} 20 | } 21 | 22 | type SmtpServer struct { 23 | host string 24 | port string 25 | } 26 | 27 | func (s *SmtpServer) ServerName() string { 28 | return s.host + ":" + s.port 29 | } 30 | 31 | func (mail *Mail) BuildMessage() bytes.Buffer { 32 | 33 | var body bytes.Buffer 34 | headers := "MIME-version: 1.0;\nContent-Type: text/html;" 35 | body.Write([]byte(fmt.Sprintf("From: %s\r\n", mail.senderId))) 36 | body.Write([]byte(fmt.Sprintf("To: %s\r\n", strings.Join(mail.toIds, ";")))) 37 | body.Write([]byte(fmt.Sprintf("Subject: %s\n%s\n\n", mail.subject, headers))) 38 | wd, _ := os.Getwd() 39 | t, _ := template.ParseFiles(wd+"/mail/templates/"+mail.template) 40 | 41 | t.Execute(&body, mail.data) 42 | 43 | return body 44 | } 45 | 46 | func SendMail(toIds []string, subject string, template string, data interface{}) { 47 | var Host = os.Getenv("MAIL_HOST") 48 | var Port = os.Getenv("MAIL_PORT") 49 | var Username = os.Getenv("MAIL_USERNAME") 50 | var Password = os.Getenv("MAIL_PASSWORD") 51 | 52 | mail := Mail{} 53 | mail.senderId = Username 54 | mail.toIds = toIds 55 | mail.subject = subject 56 | mail.template = template 57 | mail.data = data 58 | 59 | messageBody := mail.BuildMessage() 60 | 61 | smtpServer := SmtpServer{host: Host, port: Port} 62 | 63 | auth := smtp.PlainAuth("", mail.senderId, Password, smtpServer.host) 64 | 65 | client, err := smtp.Dial(smtpServer.ServerName()) 66 | if err != nil { 67 | log.Panic(err) 68 | } 69 | if ok, _ := client.Extension("STARTTLS"); ok { 70 | config := &tls.Config{ServerName: smtpServer.host} 71 | if err = client.StartTLS(config); err != nil { 72 | log.Panic(err) 73 | } 74 | } 75 | 76 | if err = client.Auth(auth); err != nil { 77 | log.Panic(err) 78 | } 79 | 80 | if err = client.Mail(Username); err != nil { 81 | log.Panic(err) 82 | } 83 | for _, k := range toIds { 84 | if err = client.Rcpt(k); err != nil { 85 | log.Panic(err) 86 | } 87 | } 88 | 89 | w, err := client.Data() 90 | if err != nil { 91 | log.Panic(err) 92 | } 93 | 94 | _, err = w.Write(messageBody.Bytes()) 95 | if err != nil { 96 | log.Panic(err) 97 | } 98 | 99 | err = w.Close() 100 | if err != nil { 101 | log.Panic(err) 102 | } 103 | 104 | client.Quit() 105 | 106 | log.Println("Mail sent successfully") 107 | } -------------------------------------------------------------------------------- /mail/templates/greeter-template.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Hello {{.App}} 10 |

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/joho/godotenv" 5 | "github.com/letsgo-framework/letsgo/database" 6 | letslog "github.com/letsgo-framework/letsgo/log" 7 | "github.com/letsgo-framework/letsgo/routes" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | 13 | // Load env 14 | err := godotenv.Load() 15 | 16 | // Setup log writing 17 | letslog.InitLogFuncs() 18 | 19 | if err != nil { 20 | letslog.Error("Error loading .env file") 21 | } else { 22 | letslog.Info("env loaded") 23 | } 24 | 25 | database.Connect() 26 | 27 | srv := routes.PaveRoutes() 28 | 29 | port := os.Getenv("PORT") 30 | if port == "" { 31 | port = ":8080" 32 | } 33 | 34 | if os.Getenv("SERVE_TLS") == "true" { 35 | srv.RunTLS(port,os.Getenv("CERTIFICATE_LOCATION"),"KEY_FILE_LOCATION") 36 | } else { 37 | srv.Run(port) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /routes/auth_routes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ginserver "github.com/go-oauth2/gin-server" 6 | "github.com/letsgo-framework/letsgo/controllers" 7 | "github.com/letsgo-framework/letsgo/helpers" 8 | ) 9 | 10 | func AuthRoutes(r *gin.RouterGroup) *gin.RouterGroup { 11 | 12 | // Auth Init 13 | controllers.AuthInit() 14 | config := ginserver.Config{ 15 | ErrorHandleFunc: func(ctx *gin.Context, err error) { 16 | helpers.RespondWithError(ctx, 401, "invalid access_token") 17 | }, 18 | TokenKey: "github.com/go-oauth2/gin-server/access-token", 19 | Skipper: func(_ *gin.Context) bool { 20 | return false 21 | }, 22 | } 23 | 24 | r.GET("/credentials", controllers.GetCredentials) 25 | r.GET("/login", controllers.GetToken) 26 | r.POST("/register", controllers.Register) 27 | auth := r.Group("auth") 28 | { 29 | auth.Use(ginserver.HandleTokenVerify(config)) 30 | } 31 | 32 | return auth 33 | } 34 | -------------------------------------------------------------------------------- /routes/routes.go: -------------------------------------------------------------------------------- 1 | /* 2 | |-------------------------------------------------------------------------- 3 | | API Routes 4 | |-------------------------------------------------------------------------- 5 | | 6 | | Here is where you can register API routes for your application. 7 | | Enjoy building your API! 8 | | 9 | */ 10 | 11 | package routes 12 | 13 | import ( 14 | "github.com/gin-contrib/cors" 15 | "github.com/gin-gonic/gin" 16 | "github.com/letsgo-framework/letsgo/controllers" 17 | ) 18 | 19 | // PaveRoutes sets up all api routes 20 | func PaveRoutes() *gin.Engine { 21 | r := gin.Default() 22 | 23 | // CORS 24 | r.Use(cors.Default()) 25 | 26 | // Start CRON 27 | // jobs.Run() 28 | 29 | // Grouped api 30 | v1 := r.Group("/api/v1") 31 | { 32 | v1.GET("/", controllers.Greet) 33 | auth := AuthRoutes(v1) 34 | auth.GET("/", controllers.Verify) 35 | } 36 | 37 | return r 38 | } 39 | -------------------------------------------------------------------------------- /runner.conf: -------------------------------------------------------------------------------- 1 | root: . 2 | tmp_path: ./tmp 3 | build_name: runner-build 4 | build_log: runner-build-errors.log 5 | valid_ext: .go, .tpl, .tmpl, .html 6 | no_rebuild_ext: .tpl, .tmpl, .html 7 | ignored: assets, tmp, vendor 8 | build_delay: 600 9 | colors: 1 10 | log_color_main: cyan 11 | log_color_build: yellow 12 | log_color_runner: green 13 | log_color_watcher: magenta 14 | log_color_app: -------------------------------------------------------------------------------- /tests/main_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/gin-gonic/gin" 9 | "github.com/joho/godotenv" 10 | "github.com/letsgo-framework/letsgo/database" 11 | letslog "github.com/letsgo-framework/letsgo/log" 12 | "github.com/letsgo-framework/letsgo/routes" 13 | "github.com/letsgo-framework/letsgo/types" 14 | "go.mongodb.org/mongo-driver/bson" 15 | "go.mongodb.org/mongo-driver/mongo/readpref" 16 | . "gopkg.in/check.v1" 17 | "io/ioutil" 18 | "log" 19 | "net/http" 20 | "os" 21 | "testing" 22 | ) 23 | 24 | type TestInsert struct { 25 | Name string `form:"name" binding:"required" json:"name" bson:"name"` 26 | } 27 | 28 | type TestSuite struct { 29 | srv *gin.Engine 30 | } 31 | 32 | var _ = Suite(&TestSuite{}) 33 | 34 | func TestMain(m *testing.M) { 35 | // Setup log writing 36 | letslog.InitLogFuncs() 37 | err := godotenv.Load("../.env.testing") 38 | database.TestConnect() 39 | 40 | database.DB.Drop(context.Background()) 41 | 42 | if err != nil { 43 | log.Fatal("Error loading .env file") 44 | } 45 | s := TestSuite{ 46 | srv: routes.PaveRoutes(), 47 | } 48 | go s.srv.Run(os.Getenv("PORT")) 49 | 50 | os.Exit(m.Run()) 51 | } 52 | 53 | 54 | func (s *TestSuite) TestGetEnv(c *C) { 55 | 56 | dbPort := os.Getenv("DATABASE_PORT") 57 | fmt.Printf("db port %s", dbPort) 58 | if dbPort == "" { 59 | c.Error() 60 | c.Fail() 61 | } 62 | c.Assert(dbPort, Equals, "27017") 63 | } 64 | 65 | func (s *TestSuite) TestHelloWorld(c *C) { 66 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/" 67 | client := &http.Client{} 68 | req, _ := http.NewRequest("GET", requestURL, nil) 69 | 70 | resp, err := client.Do(req) 71 | if err != nil { 72 | c.Error(err) 73 | c.Fail() 74 | } 75 | defer resp.Body.Close() 76 | c.Assert(resp.StatusCode, Equals, 200) 77 | } 78 | 79 | func (s *TestSuite) TestCredentials(c *C) { 80 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/credentials/" 81 | client := &http.Client{} 82 | req, _ := http.NewRequest("GET", requestURL, nil) 83 | 84 | resp, err := client.Do(req) 85 | if err != nil { 86 | c.Error(err) 87 | c.Fail() 88 | } 89 | defer resp.Body.Close() 90 | c.Assert(resp.StatusCode, Equals, 200) 91 | } 92 | 93 | func (s *TestSuite) TestTokenSuccess(c *C) { 94 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/credentials/" 95 | client := &http.Client{} 96 | req, _ := http.NewRequest("GET", requestURL, nil) 97 | 98 | resp, err := client.Do(req) 99 | if err != nil { 100 | c.Error(err) 101 | c.Fail() 102 | } 103 | defer resp.Body.Close() 104 | responseData, _ := ioutil.ReadAll(resp.Body) 105 | var credResponse types.CredentialResponse 106 | json.Unmarshal(responseData, &credResponse) 107 | 108 | requestURL = "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/login?grant_type=client_credentials&client_id=" + credResponse.CLIENT_ID + "&client_secret=" + credResponse.CLIENT_SECRET + "&scope=read" 109 | 110 | req, _ = http.NewRequest("GET", requestURL, nil) 111 | 112 | resp, err = client.Do(req) 113 | if err != nil { 114 | c.Error(err) 115 | c.Fail() 116 | } 117 | defer resp.Body.Close() 118 | c.Assert(resp.StatusCode, Equals, 200) 119 | } 120 | 121 | func (s *TestSuite) TestTokenFail(c *C) { 122 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/login" 123 | client := &http.Client{} 124 | req, _ := http.NewRequest("GET", requestURL, nil) 125 | 126 | resp, err := client.Do(req) 127 | if err != nil { 128 | c.Error(err) 129 | c.Fail() 130 | } 131 | defer resp.Body.Close() 132 | c.Assert(resp.StatusCode, Equals, 401) 133 | } 134 | 135 | func (s *TestSuite) TestAccessTokenSuccess(c *C) { 136 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/credentials/" 137 | client := &http.Client{} 138 | req, _ := http.NewRequest("GET", requestURL, nil) 139 | 140 | resp, err := client.Do(req) 141 | if err != nil { 142 | c.Error(err) 143 | c.Fail() 144 | } 145 | defer resp.Body.Close() 146 | responseData, _ := ioutil.ReadAll(resp.Body) 147 | var credResponse types.CredentialResponse 148 | json.Unmarshal(responseData, &credResponse) 149 | 150 | requestURL = "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/login?grant_type=client_credentials&client_id=" + credResponse.CLIENT_ID + "&client_secret=" + credResponse.CLIENT_SECRET + "&scope=read" 151 | 152 | req, _ = http.NewRequest("GET", requestURL, nil) 153 | 154 | resp, err = client.Do(req) 155 | if err != nil { 156 | c.Error(err) 157 | c.Fail() 158 | } 159 | defer resp.Body.Close() 160 | 161 | respData, _ := ioutil.ReadAll(resp.Body) 162 | var tokenResponse types.TokenResponse 163 | json.Unmarshal(respData, &tokenResponse) 164 | 165 | requestURL = "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/auth?access_token=" + tokenResponse.AccessToken 166 | 167 | req, _ = http.NewRequest("GET", requestURL, nil) 168 | 169 | resp, err = client.Do(req) 170 | if err != nil { 171 | c.Error(err) 172 | c.Fail() 173 | } 174 | defer resp.Body.Close() 175 | 176 | c.Assert(resp.StatusCode, Equals, 200) 177 | } 178 | 179 | func (s *TestSuite) TestAccessTokenFail(c *C) { 180 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/credentials/" 181 | client := &http.Client{} 182 | req, _ := http.NewRequest("GET", requestURL, nil) 183 | 184 | resp, err := client.Do(req) 185 | if err != nil { 186 | c.Error(err) 187 | c.Fail() 188 | } 189 | defer resp.Body.Close() 190 | responseData, _ := ioutil.ReadAll(resp.Body) 191 | var credResponse types.CredentialResponse 192 | json.Unmarshal(responseData, &credResponse) 193 | 194 | requestURL = "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/login?grant_type=client_credentials&client_id=" + credResponse.CLIENT_ID + "&client_secret=" + credResponse.CLIENT_SECRET + "&scope=read" 195 | 196 | req, _ = http.NewRequest("GET", requestURL, nil) 197 | 198 | resp, err = client.Do(req) 199 | if err != nil { 200 | c.Error(err) 201 | c.Fail() 202 | } 203 | defer resp.Body.Close() 204 | 205 | respData, _ := ioutil.ReadAll(resp.Body) 206 | var tokenResponse types.TokenResponse 207 | json.Unmarshal(respData, &tokenResponse) 208 | 209 | requestURL = "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/auth?access_token=mywrongaccesstoken" 210 | 211 | req, _ = http.NewRequest("GET", requestURL, nil) 212 | 213 | resp, err = client.Do(req) 214 | if err != nil { 215 | c.Error(err) 216 | c.Fail() 217 | } 218 | defer resp.Body.Close() 219 | 220 | c.Assert(resp.StatusCode, Equals, 401) 221 | } 222 | 223 | func (s *TestSuite) TestDatabaseTestConnection(c *C) { 224 | database.TestConnect() 225 | err := database.Client.Ping(context.Background(), readpref.Primary()) 226 | c.Assert(err, Equals, nil) 227 | } 228 | 229 | func (s *TestSuite) TestDatabaseConnection(c *C) { 230 | database.Connect() 231 | err := database.Client.Ping(context.Background(), readpref.Primary()) 232 | c.Assert(err, Equals, nil) 233 | } 234 | 235 | func (s *TestSuite) TestDBInsert(c *C) { 236 | database.TestConnect() 237 | input := TestInsert{Name: "testname"} 238 | collection := database.DB.Collection("test_collection") 239 | _, err := collection.InsertOne(context.Background(), input) 240 | if err != nil { 241 | c.Error(err) 242 | } 243 | result := TestInsert{} 244 | err = collection.FindOne(context.Background(), bson.M{"name": "testname"}).Decode(&result) 245 | if err != nil { 246 | c.Error(err) 247 | } 248 | c.Assert(result, Equals, input) 249 | } 250 | 251 | //Test User-registartion 252 | func (s *TestSuite) Test1UserRegistration(c *C) { 253 | data := types.User{ 254 | Name: "Letsgo User", 255 | Username: "letsgoUs3r", 256 | Password: "qwerty", 257 | } 258 | 259 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/register" 260 | client := &http.Client{} 261 | b := new(bytes.Buffer) 262 | json.NewEncoder(b).Encode(data) 263 | req, _ := http.NewRequest("POST", requestURL, b) 264 | resp, err := client.Do(req) 265 | if err != nil { 266 | c.Error(err) 267 | c.Fail() 268 | } 269 | defer resp.Body.Close() 270 | respData, _ := ioutil.ReadAll(resp.Body) 271 | var user types.User 272 | json.Unmarshal(respData, &user) 273 | c.Assert(resp.StatusCode, Equals, 200) 274 | } 275 | 276 | func (s *TestSuite) Test2UserLoginPasswordGrant(c *C) { 277 | requestURL := "http://127.0.0.1" + os.Getenv("PORT") + "/api/v1/login?grant_type=password&client_id=client@letsgo&client_secret=Va4a8bFFhTJZdybnzyhjHjj6P9UVh7UL&scope=read&username=letsgoUs3r&password=qwerty" 278 | req, _ := http.NewRequest("GET", requestURL, nil) 279 | client := &http.Client{} 280 | resp, err := client.Do(req) 281 | if err != nil { 282 | letslog.Debug(err.Error()) 283 | c.Error(err) 284 | c.Fail() 285 | } 286 | defer resp.Body.Close() 287 | 288 | c.Assert(resp.StatusCode, Equals, 200) 289 | } 290 | 291 | func Test(t *testing.T) { 292 | TestingT(t) 293 | } 294 | -------------------------------------------------------------------------------- /types/access_token_response.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // TokenResponse is type for accesstoken response 4 | type TokenResponse struct { 5 | AccessToken string `json:"access_token"` 6 | ExpiresIn string `json:"expires_in"` 7 | Scope string `json:"scope"` 8 | TokenType string `json:"token_type"` 9 | } 10 | -------------------------------------------------------------------------------- /types/credentials_response.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // CredentialResponse is Client Credential response type 4 | type CredentialResponse struct { 5 | CLIENT_ID string 6 | CLIENT_SECRET string 7 | } 8 | -------------------------------------------------------------------------------- /types/user.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "go.mongodb.org/mongo-driver/bson/primitive" 4 | 5 | type User struct { 6 | Id primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"` 7 | Name string `json:"name"` 8 | Username string `json:"username"` 9 | Password string `json:"password"` 10 | } --------------------------------------------------------------------------------