├── .gitignore ├── README.md ├── authentication-service ├── README.md ├── authApp ├── authentication-service.dockerfile ├── cmd │ └── api │ │ ├── handlers.go │ │ ├── helpers.go │ │ ├── main.go │ │ └── routes.go ├── data │ └── models.go ├── go.mod └── go.sum ├── broker-service ├── README.md ├── broker-service.dockerfile ├── brokerApp ├── cmd │ └── api │ │ ├── handlers.go │ │ ├── helpers.go │ │ ├── main.go │ │ └── routes.go ├── event │ ├── emitter.go │ └── event.go ├── go.mod ├── go.sum └── logs │ ├── logs.pb.go │ ├── logs.proto │ └── logs_grpc.pb.go ├── demo.webm ├── front-end ├── .DS_Store ├── cmd │ ├── .DS_Store │ └── web │ │ ├── main.go │ │ └── templates │ │ ├── base.layout.gohtml │ │ ├── footer.partial.gohtml │ │ ├── header.partial.gohtml │ │ └── test.page.gohtml ├── front-end.dockerfile └── go.mod ├── go-micro-arch.png ├── go.work ├── go.work.sum ├── listener-service ├── README.md ├── event │ ├── consumer.go │ └── event.go ├── go.mod ├── go.sum ├── listener-service.dockerfile ├── listenerApp └── main.go ├── logger-service ├── cmd │ └── api │ │ ├── grpc.go │ │ ├── handlers.go │ │ ├── helpers.go │ │ ├── main.go │ │ ├── routes.go │ │ └── rpc.go ├── data │ └── models.go ├── go.mod ├── go.sum ├── logger-service.dockerfile ├── loggerServiceApp └── logs │ ├── logs.pb.go │ ├── logs.proto │ └── logs_grpc.pb.go ├── mail-service ├── cmd │ └── api │ │ ├── handlers.go │ │ ├── helpers.go │ │ ├── mailer.go │ │ ├── main.go │ │ └── routes.go ├── go.mod ├── go.sum ├── mail-service.dockerfile ├── mailApp └── templates │ └── mail │ ├── mail.html.gohtml │ └── mail.plain.gohtml └── project ├── Caddyfile ├── Makefile ├── caddy.dockerfile ├── docker-compose.yml ├── ingress.yml ├── k8s ├── authentication.yml ├── broker.yml ├── front-end.yml ├── listener.yml ├── logger.yml ├── mail.yml ├── mailhog.yml ├── mongo.yml └── rabbit.yml ├── postgres.yml └── swarm.yml /.gitignore: -------------------------------------------------------------------------------- 1 | project/db-data -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-micro 2 | 3 | This is a project consisting of 6 different microservices written in GoLang. This is an End-to-End Distributed System that uses different protocols like rpc, GRPC , REST API's and Queueing Systems for communication between the services. 4 | 5 | The main goal of this project is to provide a clear and concise understanding of the communication mechanisms utilized by this microservice architecture-based distributed system. By doing so, users can gain a comprehensive understanding of how the system operates and how these communication mechanisms enable seamless integration between different software services, leading to a more efficient and scalable application.. 6 | 7 | This project can be deployed either via a docker swarm (also via minikube cluster in future). 8 | 9 | ![Project Architecture](go-micro-arch.png) 10 | --- 11 | 12 | ### Demo Video 13 | [Screencast from 01-05-24 11:14:35 AM IST.webm](https://github.com/ayushthe1/go-micro/assets/114604338/98876a9e-b408-4b5a-b6d4-ea7ee8c5af9d) 14 | 15 | 16 | --- 17 | 18 | ### These are the core services that perform distributed actions 19 | - [authentication-service](https://github.com/ayushthe1/go-micro/tree/master/authentication-service) for Authenticating users. Stores the users data in a Postgres Container. 20 | - [Broker Service](https://github.com/ayushthe1/go-micro/tree/master/broker-service) is the central node point for handling each request from the client and rendering a response to the client. This Service calls the right service(authentication, listener, logger and mailer ) when a request is called from the front-end 21 | - [logger-service](https://github.com/ayushthe1/go-micro/tree/master/logger-service) receives and accepts the data from the authentication, listener and mailer service ,when each service has been called through the broker service . The data from each service is stored in a MongoDb database.It also handles the gRPC and RPC actions when called by the broker service. 22 | - [mail-service](https://github.com/ayushthe1/go-micro/tree/master/mail-service) Handles sending of mails 23 | - [listener-service](https://github.com/ayushthe1/go-micro/tree/master/listener-service) This service consumes messages in RabbitMQ (running in a different container) and initiates a process. 24 | - [front-end](https://github.com/ayushthe1/go-micro/tree/master/project/Caddyfile) just displays outputs for each action performed internally 25 | - [Caddy-service](https://github.com/ayushthe1/go-micro/tree/master/project/Caddyfile) acts as a reverse proxy. It forwards requests coming to http://localhost to the front-end service. 26 | 27 | 28 | 29 | 30 | 31 | --- 32 | 33 | ### To deploy via Docker swarm 34 | 35 | 1. Ensure you have docker installed and running on your system. 36 | 37 | 2. In your terminal ,type `sudo nano /etc/hosts` and enter your password. 38 | 39 | 3. In the file ,add a line `127.0.0.1 backend` . The updated file should look something like this : 40 |
41 | 42 | ![image](https://github.com/ayushthe1/go-micro/assets/114604338/8f5c0541-a892-4b8e-90e3-fee70b14fed6) 43 | 44 | 4. Clone this repo 45 | 46 | 5. From inside the repo, cd into `/project` directory. 47 | 48 | 6. Type command `make deploy_swarm` in the terminal. 49 | 50 | 7. The project will be deployed on docker swarm. 51 | 52 | 53 | -------------------------------------------------------------------------------- /authentication-service/README.md: -------------------------------------------------------------------------------- 1 | ### The Authentication microservice authenticates a user 2 | 3 | - Uses Postgres as its database 4 | - Uses [jackc](github.com/jackc/pgx/v4) as its postgres driver 5 | - Uses [chi router](https://github.com/go-chi/chi/v5) for routing 6 | - Uses [chi](github.com/go-chi/cors) as its CORS 7 | -------------------------------------------------------------------------------- /authentication-service/authApp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/authentication-service/authApp -------------------------------------------------------------------------------- /authentication-service/authentication-service.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN mkdir /app 4 | 5 | COPY authApp /app 6 | 7 | CMD ["/app/authApp"] -------------------------------------------------------------------------------- /authentication-service/cmd/api/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "net/http" 9 | ) 10 | 11 | func (app *Config) Authenticate(w http.ResponseWriter, r *http.Request) { 12 | var requestPayload struct { 13 | Email string `json:"email"` 14 | Password string `json:"password"` 15 | } 16 | 17 | err := app.readJSON(w, r, &requestPayload) 18 | if err != nil { 19 | app.errorJSON(w, err, http.StatusBadRequest) 20 | return 21 | } 22 | 23 | // validate the user against the database 24 | user, err := app.Models.User.GetByEmail(requestPayload.Email) 25 | if err != nil { 26 | app.errorJSON(w, errors.New("invalid credentials"), http.StatusBadRequest) 27 | return 28 | } 29 | 30 | valid, err := user.PasswordMatches(requestPayload.Password) 31 | if err != nil || !valid { 32 | app.errorJSON(w, errors.New("invalid credentials"), http.StatusBadRequest) 33 | return 34 | } 35 | 36 | // log the successfull authentication 37 | err = app.logRequest("authenticationnn", fmt.Sprint("%s logged", user.Email)) 38 | if err != nil { 39 | app.errorJSON(w, err) 40 | return 41 | } 42 | 43 | payload := jsonResponse{ 44 | Error: false, 45 | Message: fmt.Sprintf("Logged in user %s", user.Email), 46 | Data: user, 47 | } 48 | 49 | app.writeJSON(w, http.StatusAccepted, payload) 50 | } 51 | 52 | func (app *Config) logRequest(name, data string) error { 53 | var entry struct { 54 | Name string `json:"name"` 55 | Data string `json:"data"` 56 | } 57 | 58 | entry.Name = name 59 | entry.Data = data 60 | 61 | jsonData, _ := json.Marshal(entry) 62 | logServiceURL := "http://logger-service/log" 63 | 64 | request, err := http.NewRequest("POST", logServiceURL, bytes.NewBuffer(jsonData)) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | client := &http.Client{} 70 | _, err = client.Do(request) 71 | if err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | 77 | } 78 | -------------------------------------------------------------------------------- /authentication-service/cmd/api/helpers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type jsonResponse struct { 11 | Error bool `json:"ERRoR"` 12 | Message string `json:"message"` 13 | Data any `json:"data,omitempty"` 14 | } 15 | 16 | // function to read json 17 | func (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error { 18 | maxBytes := 1048576 // 1 MB 19 | 20 | // limiting the size of incoming request bodies 21 | r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes)) 22 | 23 | dec := json.NewDecoder(r.Body) 24 | err := dec.Decode(data) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | // decode a JSON value into an empty struct{}. 30 | err = dec.Decode(&struct{}{}) 31 | // io.EOF is an error value indicating the end of the input source has been reached. 32 | // If err is not equal to io.EOF, it means that the decoding process did not reach the end of the input source, and there are additional JSON values remaining. 33 | if err != io.EOF { 34 | return errors.New("body must have only a single json value") 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // function to write json 41 | // The ellipsis (...) before http.Header indicates that it can accept a variable number of arguments, allowing you to pass or store multiple http.Header values. 42 | func (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error { 43 | out, err := json.Marshal(data) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(headers) > 0 { 49 | for key, value := range headers[0] { 50 | w.Header()[key] = value 51 | } 52 | } 53 | 54 | w.Header().Set("Content-Type", "application/json") 55 | w.WriteHeader(status) 56 | 57 | _, err = w.Write(out) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // function to write error message as json 66 | func (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error { 67 | statusCode := http.StatusBadRequest 68 | 69 | if len(status) > 0 { 70 | statusCode = status[0] 71 | } 72 | 73 | var payload jsonResponse 74 | payload.Error = true 75 | payload.Message = err.Error() 76 | 77 | return app.writeJSON(w, statusCode, payload) 78 | } 79 | -------------------------------------------------------------------------------- /authentication-service/cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "authentication/data" 5 | 6 | // Package sql provides a generic interface around SQL (or SQL-like) databases.The sql package must be used in conjunction with a database driver 7 | "database/sql" 8 | "fmt" 9 | "log" 10 | "net/http" 11 | "os" 12 | "time" 13 | 14 | // "pgx" Go driver is a software component that acts as a bridge between your Go application and the PostgreSQL database server. It provides a set of tools and functions that make it easy for your Go code to send requests and receive responses from the database. It simplifies the process of integrating a database into your application, making it easier to store and retrieve data as needed. 15 | 16 | _ "github.com/jackc/pgconn" 17 | _ "github.com/jackc/pgx/v4" 18 | _ "github.com/jackc/pgx/v4/stdlib" 19 | ) 20 | 21 | // Broker is also listening on port 8084 and we can make authentication service also listen on that port as docker lets multiple containers listen on same port and treats them as indiviual servers 22 | 23 | type Config struct { 24 | DB *sql.DB 25 | Models data.Models 26 | } 27 | 28 | // authentication service will listen on port 80 inside of docker. 29 | // you can have multiple services listening on the same port inside docker, just as if they were separate machines. So every single web/api service inside of docker can all listen on port 80. 30 | const webPort = "80" 31 | 32 | var counts int64 33 | 34 | func main() { 35 | log.Println("Starting authentication service") 36 | 37 | // connect to DB 38 | conn := connectToDB() 39 | if conn == nil { 40 | log.Panic("Can't connect to Postgres!") 41 | } 42 | 43 | // set up config 44 | app := Config{ 45 | DB: conn, 46 | Models: data.New(conn), 47 | } 48 | 49 | srv := &http.Server{ 50 | Addr: fmt.Sprintf(":%s", webPort), 51 | Handler: app.routes(), 52 | } 53 | 54 | err := srv.ListenAndServe() 55 | if err != nil { 56 | log.Panic(err) 57 | } 58 | } 59 | 60 | func openDB(dsn string) (*sql.DB, error) { 61 | db, err := sql.Open("pgx", dsn) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | err = db.Ping() 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | return db, nil 72 | } 73 | 74 | func connectToDB() *sql.DB { 75 | log.Println("Getting DSN string") 76 | dsn := os.Getenv("DSN") 77 | log.Println("DSN is", dsn) 78 | 79 | for { 80 | connection, err := openDB(dsn) 81 | if err != nil { 82 | log.Println("Postgres not yet ready ...") 83 | counts++ 84 | } else { 85 | log.Println("Connected to Postgres") 86 | return connection 87 | } 88 | 89 | if counts > 10 { 90 | log.Println(err) 91 | return nil 92 | } 93 | 94 | log.Println("Backing off for 2 sec ...") 95 | time.Sleep(2 * time.Second) 96 | continue 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /authentication-service/cmd/api/routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/middleware" 7 | "github.com/go-chi/chi/v5" 8 | "github.com/go-chi/cors" 9 | ) 10 | 11 | func (app *Config) routes() http.Handler { 12 | mux := chi.NewRouter() 13 | 14 | // specify who is allowed to connect 15 | mux.Use(cors.Handler(cors.Options{ 16 | AllowedOrigins: []string{"https://*", "http://*"}, 17 | AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 18 | AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, 19 | ExposedHeaders: []string{"Link"}, 20 | AllowCredentials: true, 21 | MaxAge: 300, 22 | })) 23 | 24 | mux.Use(middleware.Heartbeat("/ping")) 25 | 26 | // create a new route 27 | mux.Post("/authenticate", app.Authenticate) 28 | 29 | return mux 30 | } 31 | -------------------------------------------------------------------------------- /authentication-service/data/models.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | "log" 8 | "time" 9 | 10 | "golang.org/x/crypto/bcrypt" 11 | ) 12 | 13 | const dbTimeout = time.Second * 3 14 | 15 | var db *sql.DB 16 | 17 | // New is the function used to create an instance of the data package. It returns the type 18 | // Model, which embeds all the types we want to be available to our application. 19 | func New(dbPool *sql.DB) Models { 20 | db = dbPool 21 | 22 | return Models{ 23 | User: User{}, 24 | } 25 | } 26 | 27 | // Models is the type for this package. Note that any model that is included as a member 28 | // in this type is available to us throughout the application, anywhere that the 29 | // app variable is used, provided that the model is also added in the New function. 30 | type Models struct { 31 | User User 32 | } 33 | 34 | // User is the structure which holds one user from the database. 35 | type User struct { 36 | ID int `json:"id"` 37 | Email string `json:"email"` 38 | FirstName string `json:"first_name,omitempty"` 39 | LastName string `json:"last_name,omitempty"` 40 | Password string `json:"-"` 41 | Active int `json:"active"` 42 | CreatedAt time.Time `json:"created_at"` 43 | UpdatedAt time.Time `json:"updated_at"` 44 | } 45 | 46 | // GetAll returns a slice of all users, sorted by last name 47 | func (u *User) GetAll() ([]*User, error) { 48 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 49 | defer cancel() 50 | 51 | query := `select id, email, first_name, last_name, password, user_active, created_at, updated_at 52 | from users order by last_name` 53 | 54 | rows, err := db.QueryContext(ctx, query) 55 | if err != nil { 56 | return nil, err 57 | } 58 | defer rows.Close() 59 | 60 | var users []*User 61 | 62 | for rows.Next() { 63 | var user User 64 | err := rows.Scan( 65 | &user.ID, 66 | &user.Email, 67 | &user.FirstName, 68 | &user.LastName, 69 | &user.Password, 70 | &user.Active, 71 | &user.CreatedAt, 72 | &user.UpdatedAt, 73 | ) 74 | if err != nil { 75 | log.Println("Error scanning", err) 76 | return nil, err 77 | } 78 | 79 | users = append(users, &user) 80 | } 81 | 82 | return users, nil 83 | } 84 | 85 | // GetByEmail returns one user by email 86 | func (u *User) GetByEmail(email string) (*User, error) { 87 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 88 | defer cancel() 89 | 90 | query := `select id, email, first_name, last_name, password, user_active, created_at, updated_at from users where email = $1` 91 | 92 | var user User 93 | row := db.QueryRowContext(ctx, query, email) 94 | 95 | err := row.Scan( 96 | &user.ID, 97 | &user.Email, 98 | &user.FirstName, 99 | &user.LastName, 100 | &user.Password, 101 | &user.Active, 102 | &user.CreatedAt, 103 | &user.UpdatedAt, 104 | ) 105 | 106 | if err != nil { 107 | return nil, err 108 | } 109 | 110 | return &user, nil 111 | } 112 | 113 | // GetOne returns one user by id 114 | func (u *User) GetOne(id int) (*User, error) { 115 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 116 | defer cancel() 117 | 118 | query := `select id, email, first_name, last_name, password, user_active, created_at, updated_at from users where id = $1` 119 | 120 | var user User 121 | row := db.QueryRowContext(ctx, query, id) 122 | 123 | err := row.Scan( 124 | &user.ID, 125 | &user.Email, 126 | &user.FirstName, 127 | &user.LastName, 128 | &user.Password, 129 | &user.Active, 130 | &user.CreatedAt, 131 | &user.UpdatedAt, 132 | ) 133 | 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | return &user, nil 139 | } 140 | 141 | // Update updates one user in the database, using the information 142 | // stored in the receiver u 143 | func (u *User) Update() error { 144 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 145 | defer cancel() 146 | 147 | stmt := `update users set 148 | email = $1, 149 | first_name = $2, 150 | last_name = $3, 151 | user_active = $4, 152 | updated_at = $5 153 | where id = $6 154 | ` 155 | 156 | _, err := db.ExecContext(ctx, stmt, 157 | u.Email, 158 | u.FirstName, 159 | u.LastName, 160 | u.Active, 161 | time.Now(), 162 | u.ID, 163 | ) 164 | 165 | if err != nil { 166 | return err 167 | } 168 | 169 | return nil 170 | } 171 | 172 | // Delete deletes one user from the database, by User.ID 173 | func (u *User) Delete() error { 174 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 175 | defer cancel() 176 | 177 | stmt := `delete from users where id = $1` 178 | 179 | _, err := db.ExecContext(ctx, stmt, u.ID) 180 | if err != nil { 181 | return err 182 | } 183 | 184 | return nil 185 | } 186 | 187 | // DeleteByID deletes one user from the database, by ID 188 | func (u *User) DeleteByID(id int) error { 189 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 190 | defer cancel() 191 | 192 | stmt := `delete from users where id = $1` 193 | 194 | _, err := db.ExecContext(ctx, stmt, id) 195 | if err != nil { 196 | return err 197 | } 198 | 199 | return nil 200 | } 201 | 202 | // Insert inserts a new user into the database, and returns the ID of the newly inserted row 203 | func (u *User) Insert(user User) (int, error) { 204 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 205 | defer cancel() 206 | 207 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), 12) 208 | if err != nil { 209 | return 0, err 210 | } 211 | 212 | var newID int 213 | stmt := `insert into users (email, first_name, last_name, password, user_active, created_at, updated_at) 214 | values ($1, $2, $3, $4, $5, $6, $7) returning id` 215 | 216 | err = db.QueryRowContext(ctx, stmt, 217 | user.Email, 218 | user.FirstName, 219 | user.LastName, 220 | hashedPassword, 221 | user.Active, 222 | time.Now(), 223 | time.Now(), 224 | ).Scan(&newID) 225 | 226 | if err != nil { 227 | return 0, err 228 | } 229 | 230 | return newID, nil 231 | } 232 | 233 | // ResetPassword is the method we will use to change a user's password. 234 | func (u *User) ResetPassword(password string) error { 235 | ctx, cancel := context.WithTimeout(context.Background(), dbTimeout) 236 | defer cancel() 237 | 238 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) 239 | if err != nil { 240 | return err 241 | } 242 | 243 | stmt := `update users set password = $1 where id = $2` 244 | _, err = db.ExecContext(ctx, stmt, hashedPassword, u.ID) 245 | if err != nil { 246 | return err 247 | } 248 | 249 | return nil 250 | } 251 | 252 | // PasswordMatches uses Go's bcrypt package to compare a user supplied password 253 | // with the hash we have stored for a given user in the database. If the password 254 | // and hash match, we return true; otherwise, we return false. 255 | func (u *User) PasswordMatches(plainText string) (bool, error) { 256 | err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(plainText)) 257 | if err != nil { 258 | switch { 259 | case errors.Is(err, bcrypt.ErrMismatchedHashAndPassword): 260 | // invalid password 261 | return false, nil 262 | default: 263 | return false, err 264 | } 265 | } 266 | 267 | return true, nil 268 | } 269 | -------------------------------------------------------------------------------- /authentication-service/go.mod: -------------------------------------------------------------------------------- 1 | module authentication 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-chi/chi v1.5.4 7 | github.com/go-chi/chi/v5 v5.0.8 8 | github.com/go-chi/cors v1.2.1 9 | github.com/jackc/pgconn v1.14.0 10 | github.com/jackc/pgx/v4 v4.18.1 11 | golang.org/x/crypto v0.6.0 12 | ) 13 | 14 | require ( 15 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 16 | github.com/jackc/pgio v1.0.0 // indirect 17 | github.com/jackc/pgpassfile v1.0.0 // indirect 18 | github.com/jackc/pgproto3/v2 v2.3.2 // indirect 19 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 20 | github.com/jackc/pgtype v1.14.0 // indirect 21 | golang.org/x/text v0.7.0 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /authentication-service/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= 3 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 4 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 5 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 6 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 7 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 8 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= 13 | github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= 14 | github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= 15 | github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 16 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 17 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 18 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 19 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 20 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 21 | github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= 22 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 23 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 24 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 25 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 26 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 27 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 28 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 29 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 30 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 31 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 32 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 33 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 34 | github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= 35 | github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= 36 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 37 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 38 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 39 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 40 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 41 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 42 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 43 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 44 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 45 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 46 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 47 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 48 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 49 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 50 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 51 | github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= 52 | github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 53 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 54 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 55 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 56 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 57 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 58 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 59 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 60 | github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= 61 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 62 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 63 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 64 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 65 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 66 | github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= 67 | github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= 68 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 69 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 70 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 71 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 72 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 73 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 74 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 75 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 76 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 77 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 78 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 79 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 80 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 81 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 82 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 83 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 84 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 85 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 86 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 87 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 88 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 89 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 90 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 91 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 92 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 93 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 94 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 95 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 96 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 97 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 98 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 99 | github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= 100 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 101 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 102 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 103 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 104 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 105 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 106 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 107 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 108 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 109 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 110 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 111 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 112 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 113 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 114 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 115 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 116 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 117 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 118 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 119 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 120 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 121 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 122 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 123 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 124 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 125 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 126 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 127 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 128 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 129 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 130 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 131 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 132 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 133 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 134 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 135 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 136 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 137 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 138 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 139 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 140 | golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= 141 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 142 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 143 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 144 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 145 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 146 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 147 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 148 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 149 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 150 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 151 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 152 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 153 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 154 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 155 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 156 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 157 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 158 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 159 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 160 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 161 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 162 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 163 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 164 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 165 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 166 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 167 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 168 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 169 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 170 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 171 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 172 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 173 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 174 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 175 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 176 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 177 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 178 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 179 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 180 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 181 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 182 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 183 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 184 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 185 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 186 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 187 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 188 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 189 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 190 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 191 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 192 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 193 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 194 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 195 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 196 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 197 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 198 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 199 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 200 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 201 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 202 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 203 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 204 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 205 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 206 | -------------------------------------------------------------------------------- /broker-service/README.md: -------------------------------------------------------------------------------- 1 | ### The Broker Service processes each request sent by the front-end microservice and sends a response back to it. 2 | It receives request from the client and sends to the logger service. 3 | 4 | --- 5 | 6 | #### Based on the function calls that sends to the logger-service , i pick to use any of these function that does the following 7 | - The broker can send requests using Api's , it sends it to the logger service and saves it on the logger service database then displays it in the frontend 8 | 9 | - The broker can send events to the Queue and send it to the logger service the logger service database, then displays it in the frontend 10 | 11 | - The broker can send requets using RPC , it sends it to the logger service and saves it into logger-Service database then displays resut in the frontend 12 | 13 | --- 14 | 15 | 16 | #### Libraries Used 17 | - Uses [chi router](https://github.com/go-chi/chi/v5) for routing 18 | - Uses [chi](github.com/go-chi/cors) as its CORS 19 | -------------------------------------------------------------------------------- /broker-service/broker-service.dockerfile: -------------------------------------------------------------------------------- 1 | ## Dockerfile tells docker-compose how to build the image. The Dockerfile is used to build images, while docker-compose helps you run them as containers. 2 | 3 | 4 | # # base go image (builder is the name of this image) 5 | # FROM golang:1.18-alpine as builder 6 | 7 | # # run a command on the docker image we're building 8 | # RUN mkdir /app 9 | 10 | # # copy everything from the current folder (.) into the app folder we created above in our docker image 11 | # COPY . /app 12 | 13 | # # Set the working directory 14 | # WORKDIR /app 15 | 16 | # # BUild our go code , CGO_ENABLED is a environment variable 17 | # RUN CGO_ENABLED=0 go build -o brokerApp ./cmd/api 18 | 19 | # # chmod +x on a file (your script) only means, that you'll make it executable 20 | # RUN chmod +x /app/brokerApp 21 | 22 | 23 | 24 | #### We removed the above lines as we're already building the brokerApp binary from the makefile command build_broker. The above code also does the same thing ,so we avoided repeating. 25 | 26 | 27 | 28 | ## Below is a new Docker image ,seperate to the above image 29 | # Build a tiny docker image 30 | FROM alpine:latest 31 | 32 | # Run command on new docker image 33 | RUN mkdir /app 34 | 35 | 36 | # COPY --from=builder /app/brokerApp /app 37 | 38 | # brokerApp binary will be build intially by makefile target build_broker and then this dockerfile will be RUN 39 | COPY brokerApp /app 40 | 41 | # When we run this command ,it should first build all of our code on one docker image and then create a much smaller Docker image and copy over the executable (brokerApp) to this new image 42 | CMD ["/app/brokerApp"] -------------------------------------------------------------------------------- /broker-service/brokerApp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/broker-service/brokerApp -------------------------------------------------------------------------------- /broker-service/cmd/api/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "broker/event" 5 | "broker/logs" 6 | "bytes" 7 | "context" 8 | "encoding/json" 9 | "errors" 10 | "log" 11 | "net/http" 12 | "net/rpc" 13 | "time" 14 | 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/credentials/insecure" 17 | ) 18 | 19 | type RequestPayload struct { 20 | Action string `json:"action"` 21 | Auth AuthPayload `json:"auth,omitempty"` 22 | Log LogPayload `json:"log,omitempty"` 23 | Mail MailPayload `json:"mail,omitempty"` 24 | } 25 | 26 | type MailPayload struct { 27 | From string `json:"from"` 28 | To string `json:"to"` 29 | Subject string `json:"subject"` 30 | Message string `json:"message"` 31 | } 32 | 33 | type AuthPayload struct { 34 | Email string `json:"email"` 35 | Password string `json:"password"` 36 | } 37 | 38 | type LogPayload struct { 39 | Name string `json:"name"` 40 | Data string `json:"data"` 41 | } 42 | 43 | func (app *Config) Broker(w http.ResponseWriter, r *http.Request) { 44 | payload := jsonResponse{ 45 | Error: false, 46 | Message: "Hit the broker", 47 | } 48 | 49 | _ = app.writeJSON(w, http.StatusOK, payload) 50 | } 51 | 52 | // HandleSubmission is the main point of entry into the broker. It accepts a JSON 53 | // payload and performs an action based on the value of "action" in that JSON. 54 | func (app *Config) HandleSubmission(w http.ResponseWriter, r *http.Request) { 55 | var requestPayload RequestPayload 56 | 57 | err := app.readJSON(w, r, &requestPayload) 58 | if err != nil { 59 | app.errorJSON(w, err) 60 | return 61 | } 62 | 63 | switch requestPayload.Action { 64 | case "auth": 65 | app.authenticate(w, requestPayload.Auth) 66 | case "log": 67 | // app.logItem(w, requestPayload.Log) 68 | // app.logEventViaRabbit(w, requestPayload.Log) 69 | app.LogItemViaRPC(w, requestPayload.Log) 70 | case "mail": 71 | app.sendMail(w, requestPayload.Mail) 72 | default: 73 | app.errorJSON(w, errors.New("unknown action")) 74 | } 75 | } 76 | 77 | func (app *Config) logItem(w http.ResponseWriter, entry LogPayload) { 78 | jsonData, _ := json.MarshalIndent(entry, "", "\t") 79 | 80 | logServiceURL := "http://logger-service/log" 81 | 82 | request, err := http.NewRequest("POST", logServiceURL, bytes.NewBuffer(jsonData)) 83 | if err != nil { 84 | app.errorJSON(w, err) 85 | return 86 | } 87 | 88 | request.Header.Set("Content-Type", "application/json") 89 | 90 | client := &http.Client{} 91 | 92 | response, err := client.Do(request) 93 | if err != nil { 94 | app.errorJSON(w, err) 95 | return 96 | } 97 | defer response.Body.Close() 98 | 99 | if response.StatusCode != http.StatusAccepted { 100 | app.errorJSON(w, err) 101 | return 102 | } 103 | 104 | var payload jsonResponse 105 | payload.Error = false 106 | payload.Message = "logged" 107 | 108 | app.writeJSON(w, http.StatusAccepted, payload) 109 | 110 | } 111 | 112 | // authenticate calls the authentication microservice and sends back the appropriate response 113 | func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) { 114 | // create some json we'll send to the auth microservice 115 | jsonData, _ := json.MarshalIndent(a, "", "\t") 116 | 117 | // call the service (This is done form inside the docker container) 118 | log.Println("Sending POST request to authentication-service from broker") 119 | 120 | // Since we don't specify a port, it calls on default port 80 (inside docker) 121 | request, err := http.NewRequest("POST", "http://authentication-service/authenticate", bytes.NewBuffer(jsonData)) 122 | if err != nil { 123 | app.errorJSON(w, err) 124 | return 125 | } 126 | 127 | client := &http.Client{} 128 | response, err := client.Do(request) 129 | if err != nil { 130 | app.errorJSON(w, err) 131 | return 132 | } 133 | defer response.Body.Close() 134 | 135 | // make sure we get back the correct status code 136 | if response.StatusCode == http.StatusUnauthorized { 137 | app.errorJSON(w, errors.New("invalid credentials")) 138 | return 139 | } else if response.StatusCode != http.StatusAccepted { 140 | app.errorJSON(w, errors.New("error calling auth service")) 141 | return 142 | } 143 | 144 | // create a variable we'll read response.Body into 145 | var jsonFromService jsonResponse 146 | 147 | // decode the json from the auth service 148 | err = json.NewDecoder(response.Body).Decode(&jsonFromService) 149 | if err != nil { 150 | app.errorJSON(w, err) 151 | return 152 | } 153 | 154 | if jsonFromService.Error { 155 | app.errorJSON(w, err, http.StatusUnauthorized) 156 | return 157 | } 158 | 159 | var payload jsonResponse 160 | payload.Error = false 161 | payload.Message = "Authenticateddd!" 162 | payload.Data = jsonFromService.Data 163 | 164 | app.writeJSON(w, http.StatusAccepted, payload) 165 | } 166 | 167 | func (app *Config) sendMail(w http.ResponseWriter, msg MailPayload) { 168 | jsonData, _ := json.Marshal(msg) 169 | 170 | // call the mail service 171 | mailServiceURL := "http://mailer-service/send" 172 | 173 | // post to mail service 174 | // buffer stores data as a sequence of bytes. The bytes.Buffer type in the standard library treats the data as a byte slice ([]byte).when you read data from a buffer, you receive a byte slice ([]byte). Buffer is like a holding area that holds a certain amount of data until it is processed or transferred elsewhere. 175 | request, err := http.NewRequest("POST", mailServiceURL, bytes.NewBuffer(jsonData)) 176 | if err != nil { 177 | log.Println(err) 178 | app.errorJSON(w, err) 179 | return 180 | } 181 | 182 | request.Header.Set("Content-Type", "application/json") 183 | 184 | client := &http.Client{} 185 | response, err := client.Do(request) 186 | if err != nil { 187 | log.Println(err) 188 | app.errorJSON(w, err) 189 | return 190 | } 191 | defer response.Body.Close() 192 | 193 | // make sure we get back the right status code 194 | if response.StatusCode != http.StatusAccepted { 195 | app.errorJSON(w, errors.New("error calling mail service")) 196 | return 197 | } 198 | 199 | // send back json 200 | var payload jsonResponse 201 | payload.Error = false 202 | payload.Message = "Message sent to " + msg.To 203 | 204 | app.writeJSON(w, http.StatusAccepted, payload) 205 | 206 | } 207 | 208 | // Different function to handle logging an item and we'll do so by emitting an event to Rabbit MQ 209 | func (app *Config) logEventViaRabbit(w http.ResponseWriter, l LogPayload) { 210 | err := app.pushToQueue(l.Name, l.Data) 211 | if err != nil { 212 | app.errorJSON(w, err) 213 | return 214 | } 215 | 216 | var payload jsonResponse 217 | payload.Error = false 218 | payload.Message = "logged via RabbitMQ" 219 | 220 | app.writeJSON(w, http.StatusAccepted, payload) 221 | } 222 | 223 | // we'll use this function every time we need to push something to the queue 224 | func (app *Config) pushToQueue(name, msg string) error { 225 | emitter, err := event.NewEventEmitter(app.Rabbit) 226 | if err != nil { 227 | return err 228 | } 229 | 230 | payload := LogPayload{ 231 | Name: name, 232 | Data: msg, 233 | } 234 | 235 | j, _ := json.Marshal(&payload) 236 | err = emitter.Push(string(j), "log.INFO") 237 | if err != nil { 238 | return err 239 | } 240 | 241 | return nil 242 | } 243 | 244 | type RPCPayload struct { 245 | Name string 246 | Data string 247 | } 248 | 249 | // log event via rpc 250 | func (app *Config) LogItemViaRPC(w http.ResponseWriter, l LogPayload) { 251 | 252 | // get rpc client ;logger-service is our service name in docker compose 253 | client, err := rpc.Dial("tcp", "logger-service:5001") 254 | if err != nil { 255 | app.errorJSON(w, err) 256 | return 257 | } 258 | 259 | // After we have the client, we need to create some kind of payload. We need to create a type that exactly matches the one that the remote RPCServer expects to get 260 | 261 | rpcPayload := RPCPayload{ 262 | Name: l.Name, 263 | Data: l.Data, 264 | } 265 | 266 | var result string 267 | // Any method that i want to expose to rpc on the serverend must be exported. So it has to start with a capital letter 268 | err = client.Call("RPCServer.LogInfo", rpcPayload, &result) 269 | if err != nil { 270 | app.errorJSON(w, err) 271 | return 272 | } 273 | 274 | // write some json back to the user 275 | payload := jsonResponse{ 276 | Error: false, 277 | Message: result, 278 | } 279 | 280 | app.writeJSON(w, http.StatusAccepted, payload) 281 | 282 | } 283 | 284 | func (app *Config) LogViaGRPC(w http.ResponseWriter, r *http.Request) { 285 | var requestPayload RequestPayload 286 | 287 | err := app.readJSON(w, r, &requestPayload) 288 | log.Println("THIS IS THE REQUESTpayload :", requestPayload) 289 | if err != nil { 290 | app.errorJSON(w, err) 291 | return 292 | } 293 | 294 | conn, err := grpc.Dial("logger-service:50001", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) 295 | if err != nil { 296 | app.errorJSON(w, err) 297 | return 298 | } 299 | defer conn.Close() // defer closing it or we'll have connections left open all over the places and it's a resource leak 300 | 301 | // create a client 302 | c := logs.NewLogServiceClient(conn) 303 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 304 | defer cancel() 305 | 306 | _, err = c.WriteLog(ctx, &logs.LogRequest{ 307 | LogEntry: &logs.Log{ 308 | Name: requestPayload.Log.Name, 309 | Data: requestPayload.Log.Data, 310 | }, 311 | }) 312 | if err != nil { 313 | app.errorJSON(w, err) 314 | return 315 | } 316 | 317 | var payload jsonResponse 318 | payload.Error = false 319 | payload.Message = "logged" 320 | 321 | app.writeJSON(w, http.StatusAccepted, payload) 322 | } 323 | 324 | // package main 325 | 326 | // import ( 327 | // "bytes" 328 | // "encoding/json" 329 | // "errors" 330 | // "net/http" 331 | // ) 332 | 333 | // type RequestPayload struct { 334 | // Action string `json:"action"` 335 | // // we'll create a different type for each of the possible action. (eg: auth ,mail ,log) 336 | // Auth AuthPayload `json:"auth, omitempty"` 337 | // } 338 | 339 | // type AuthPayload struct { 340 | // // fields we need to authenticate 341 | // Email string `json:"email"` 342 | // Password string `json:"password"` 343 | // } 344 | 345 | // // define a handler and add it to the routes file 346 | // // Note that function Broker starts with capital B as it's required to use this function in other files. 347 | // func (app *Config) Broker(w http.ResponseWriter, r *http.Request) { 348 | // payload := jsonResponse{ 349 | // Error: false, 350 | // Message: "Hit the broker babyy", 351 | // } 352 | 353 | // // // Write this data (payload) out 354 | // // // json.Marshal is used to convert a Go data structure (such as a struct, map, or slice) into its corresponding JSON representation.It takes a Go value as input and returns a byte slice containing the JSON-encoded data. 355 | // // out, _ := json.Marshal(payload) 356 | // // w.Header().Set("Content-Type", "application/json") 357 | // // w.WriteHeader(http.StatusAccepted) 358 | 359 | // // // w.Write() function is used to write the response body(w). out is a byte slice containing the JSON-encoded data that will be sent as the response body. 360 | // // w.Write(out) 361 | 362 | // // OR 363 | 364 | // _ = app.writeJSON(w, http.StatusOK, payload) 365 | 366 | // } 367 | 368 | // func (app *Config) HandleSubmission(w http.ResponseWriter, r *http.Request) { 369 | 370 | // // read into variable of type request payload 371 | // var requestPayload RequestPayload 372 | 373 | // err := app.readJSON(w, r, &requestPayload) 374 | // if err != nil { 375 | // app.errorJSON(w, err) 376 | // return 377 | // } 378 | 379 | // switch requestPayload.Action { 380 | // case "auth": 381 | // app.authenticate(w, requestPayload.Auth) 382 | // default: 383 | // app.errorJSON(w, errors.New("unknown action")) 384 | // } 385 | // } 386 | 387 | // func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) { 388 | // // create some json that we'll send to the auth microservice 389 | // jsonData, _ := json.MarshalIndent(a, "", "\t") 390 | 391 | // // call the service 392 | // request, err := http.NewRequest("POST", "http://localhost:8081/authenticate", bytes.NewBuffer(jsonData)) 393 | 394 | // if err != nil { 395 | // app.errorJSON(w, err) 396 | // return 397 | // } 398 | 399 | // client := &http.Client{} 400 | // response, err := client.Do(request) 401 | // if err != nil { 402 | // app.errorJSON(w, err) 403 | // return 404 | // } 405 | // defer response.Body.Close() 406 | 407 | // // make sure we get back the correct status code 408 | // if response.StatusCode == http.StatusUnauthorized { 409 | // app.errorJSON(w, errors.New("Invalid credentials (in handlers.go broker)")) 410 | // return 411 | // } else if response.StatusCode != http.StatusAccepted { 412 | // app.errorJSON(w, errors.New("error calling auth service (in handlers.go broker)")) 413 | // return 414 | // } 415 | 416 | // // create a variable we'll read response.Body into (this response is coming from authentication service) 417 | // var jsonFromService jsonResponse 418 | 419 | // // decode the json from the auth service 420 | // err = json.NewDecoder(response.Body).Decode(&jsonFromService) 421 | // if err != nil { 422 | // app.errorJSON(w, err) 423 | // return 424 | // } 425 | 426 | // if jsonFromService.Error { 427 | // app.errorJSON(w, err, http.StatusUnauthorized) 428 | // return 429 | // } 430 | 431 | // // what we want to send back to the user 432 | // var payload jsonResponse 433 | // payload.Error = false 434 | // payload.Message = "Authenticated oooribabaaa " 435 | // payload.Data = jsonFromService.Data 436 | 437 | // app.writeJSON(w, http.StatusAccepted, payload) 438 | // } 439 | -------------------------------------------------------------------------------- /broker-service/cmd/api/helpers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type jsonResponse struct { 11 | Error bool `json:"errorrr"` 12 | Message string `json:"message"` 13 | Data any `json:"data,omitempty"` 14 | } 15 | 16 | // function to read json 17 | func (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error { 18 | maxBytes := 1048576 // 1 MB 19 | 20 | // limiting the size of incoming request bodies 21 | r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes)) 22 | 23 | dec := json.NewDecoder(r.Body) 24 | err := dec.Decode(data) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | // decode a JSON value into an empty struct{}. 30 | err = dec.Decode(&struct{}{}) 31 | // io.EOF is an error value indicating the end of the input source has been reached. 32 | // If err is not equal to io.EOF, it means that the decoding process did not reach the end of the input source, and there are additional JSON values remaining. 33 | if err != io.EOF { 34 | return errors.New("body must have only a single json value") 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // function to write json 41 | // The ellipsis (...) before http.Header indicates that it can accept a variable number of arguments, allowing you to pass or store multiple http.Header values. 42 | func (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error { 43 | out, err := json.Marshal(data) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(headers) > 0 { 49 | for key, value := range headers[0] { 50 | w.Header()[key] = value 51 | } 52 | } 53 | 54 | w.Header().Set("Content-Type", "application/json") 55 | w.WriteHeader(status) 56 | 57 | _, err = w.Write(out) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // function to write error message as json 66 | func (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error { 67 | statusCode := http.StatusBadRequest 68 | 69 | if len(status) > 0 { 70 | statusCode = status[0] 71 | } 72 | 73 | var payload jsonResponse 74 | payload.Error = true 75 | payload.Message = err.Error() 76 | 77 | return app.writeJSON(w, statusCode, payload) 78 | } 79 | -------------------------------------------------------------------------------- /broker-service/cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math" 7 | "net/http" 8 | "os" 9 | "time" 10 | 11 | amqp "github.com/rabbitmq/amqp091-go" 12 | ) 13 | 14 | const webPort = "8080" // listen on this port inside docker 15 | 16 | // Receiver that we use for the application 17 | type Config struct { 18 | Rabbit *amqp.Connection 19 | } 20 | 21 | func main() { 22 | // try to connect to rabbitmq 23 | rabbitConn, err := connect() 24 | if err != nil { 25 | log.Println(err) 26 | os.Exit(1) 27 | } 28 | defer rabbitConn.Close() 29 | 30 | app := Config{ 31 | Rabbit: rabbitConn, 32 | } 33 | 34 | log.Printf("Starting broker service on port %s \n", webPort) 35 | 36 | // define http server 37 | srv := &http.Server{ 38 | Addr: fmt.Sprintf(":%s", webPort), 39 | Handler: app.routes(), 40 | } 41 | 42 | // start the server 43 | err = srv.ListenAndServe() 44 | if err != nil { 45 | log.Panic(err) 46 | } 47 | 48 | } 49 | 50 | func connect() (*amqp.Connection, error) { 51 | var counts int64 52 | var backoff = 1 * time.Second 53 | var connection *amqp.Connection 54 | 55 | // don't continue until rabbit is ready 56 | for { 57 | // rabbitmq matches our service in docker compose 58 | c, err := amqp.Dial("amqp://guest:guest@rabbitmq") 59 | if err != nil { 60 | fmt.Println("RabbitMQ not yet ready...") 61 | counts++ 62 | } else { 63 | log.Println("Connected to RabbitMQ") 64 | connection = c 65 | break 66 | } 67 | 68 | // If we didn't connect : 69 | 70 | // if we didn't connect after 5 tries 71 | if counts > 5 { 72 | fmt.Println(err) 73 | return nil, err 74 | } 75 | 76 | // if we haven't tried atleast 5 times ,then call backoff 77 | backoff = time.Duration(math.Pow(float64(counts), 2)) * time.Second 78 | log.Println("backing off....") 79 | // suspends the execution of the loop for the calculated duration. 80 | time.Sleep(backoff) 81 | continue 82 | } 83 | 84 | return connection, nil 85 | } 86 | -------------------------------------------------------------------------------- /broker-service/cmd/api/routes.go: -------------------------------------------------------------------------------- 1 | // servemux (also known as a router) stores a mapping between the predefined URL paths for your application and the corresponding handlers. Usually you have one servemux for your application containing all your routes. 2 | 3 | // Handlers are responsible for carrying out your application logic and writing response headers and bodies. 4 | 5 | // http.ServeMux is An HTTP request multiplexer, often called a router, is responsible for routing incoming HTTP requests to the appropriate handler functions based on the request's URL or other criteria. 6 | 7 | // http.DefaultServeMux is the default instance of http.ServeMux created by Go's net/http package. When you register your handler functions using functions like http.HandleFunc without explicitly specifying a custom http.ServeMux, they are automatically registered with http.DefaultServeMux. 8 | 9 | package main 10 | 11 | import ( 12 | "net/http" 13 | 14 | "github.com/go-chi/chi/v5" 15 | "github.com/go-chi/chi/v5/middleware" 16 | "github.com/go-chi/cors" 17 | ) 18 | 19 | func (app *Config) routes() http.Handler { 20 | mux := chi.NewRouter() 21 | 22 | // specify who is allowed to connect 23 | mux.Use(cors.Handler(cors.Options{ 24 | AllowedOrigins: []string{"https://*", "http://*"}, 25 | AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 26 | AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, 27 | ExposedHeaders: []string{"Link"}, 28 | AllowCredentials: true, 29 | MaxAge: 300, 30 | })) 31 | 32 | mux.Use(middleware.Heartbeat("/ping")) 33 | 34 | mux.Post("/", app.Broker) 35 | 36 | mux.Post("/log-grpc", app.LogViaGRPC) 37 | 38 | // single point of entry to broker service 39 | mux.Post("/handle", app.HandleSubmission) 40 | 41 | return mux 42 | } 43 | -------------------------------------------------------------------------------- /broker-service/event/emitter.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | amqp "github.com/rabbitmq/amqp091-go" 5 | "log" 6 | ) 7 | 8 | // push events onto the queue 9 | type Emitter struct { 10 | connection *amqp.Connection 11 | } 12 | 13 | func (e *Emitter) setup() error { 14 | 15 | channel, err := e.connection.Channel() 16 | if err != nil { 17 | return err 18 | } 19 | 20 | defer channel.Close() 21 | return declareExchange(channel) 22 | } 23 | 24 | func (e *Emitter) Push(event string, severity string) error { 25 | channel, err := e.connection.Channel() 26 | if err != nil { 27 | return err 28 | } 29 | defer channel.Close() 30 | 31 | log.Println("Publishing to channel....") 32 | err = channel.Publish( 33 | "logs_topic", 34 | severity, 35 | false, 36 | false, 37 | amqp.Publishing{ 38 | ContentType: "text/plain", 39 | Body: []byte(event), 40 | }, 41 | ) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | return nil 47 | } 48 | 49 | func NewEventEmitter(conn *amqp.Connection) (Emitter, error) { 50 | emitter := Emitter{ 51 | connection: conn, 52 | } 53 | 54 | err := emitter.setup() 55 | if err != nil { 56 | return Emitter{}, err 57 | } 58 | 59 | return emitter, nil 60 | } 61 | -------------------------------------------------------------------------------- /broker-service/event/event.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | amqp "github.com/rabbitmq/amqp091-go" 5 | ) 6 | 7 | func declareExchange(ch *amqp.Channel) error { 8 | return ch.ExchangeDeclare( 9 | "logs_topic", // name of the exchange 10 | "topic", // type 11 | true, //durable 12 | false, // autodeleted 13 | false, // is this an exchange that's just used internally 14 | false, // no-wait? 15 | nil, // arguments? 16 | ) 17 | } 18 | 19 | func declareRandomQueue(ch *amqp.Channel) (amqp.Queue, error) { 20 | return ch.QueueDeclare( // declaring a queue with these attributes 21 | "", // name? 22 | false, // durable? 23 | false, // delete when unused? 24 | true, // exclusive? 25 | false, // no-wait? 26 | nil, //arguments 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /broker-service/go.mod: -------------------------------------------------------------------------------- 1 | module broker 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-chi/chi/v5 v5.0.8 // indirect 7 | github.com/go-chi/cors v1.2.1 // indirect 8 | github.com/golang/protobuf v1.5.3 // indirect 9 | github.com/rabbitmq/amqp091-go v1.8.1 // indirect 10 | golang.org/x/net v0.9.0 // indirect 11 | golang.org/x/sys v0.7.0 // indirect 12 | golang.org/x/text v0.9.0 // indirect 13 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 14 | google.golang.org/grpc v1.56.2 // indirect 15 | google.golang.org/protobuf v1.30.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /broker-service/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= 4 | github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 5 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 6 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 7 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 8 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 9 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 10 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 11 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 12 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 13 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= 16 | github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 19 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 20 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 21 | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 22 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 23 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 24 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 25 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 26 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 27 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 28 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 29 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= 30 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= 31 | google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI= 32 | google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= 33 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 34 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 35 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 36 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 37 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 38 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 39 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 40 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 41 | -------------------------------------------------------------------------------- /broker-service/logs/logs.pb.go: -------------------------------------------------------------------------------- 1 | // This file is used to determine what kind of source code should be produced. 2 | 3 | // .proto file is used to define the structure and format of the data that will be exchanged between different systems or services. 4 | 5 | //Once the .proto file is defined, it can be compiled into language-specific code (e.g., Go, Java, Python) using a protocol buffer compiler. The generated code provides convenient APIs to work with the defined messages and services in your programming language of choice. The generated code provides functions and methods to easily create, read, and send data according to the structure defined in the .proto file. 6 | 7 | // In summary, a .proto file is used to define the structure of data that will be exchanged between programs. It acts as a contract or agreement between programs, ensuring they understand each other's data format. The file is used to generate code in different languages, making it easier to work with the defined data structures and enabling communication between programs written in different languages. 8 | 9 | // Code generated by protoc-gen-go. DO NOT EDIT. 10 | // versions: 11 | // protoc-gen-go v1.27.1 12 | // protoc v3.12.4 13 | // source: logs.proto 14 | 15 | package logs 16 | 17 | import ( 18 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 19 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 20 | reflect "reflect" 21 | sync "sync" 22 | ) 23 | 24 | const ( 25 | // Verify that this generated code is sufficiently up-to-date. 26 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 27 | // Verify that runtime/protoimpl is sufficiently up-to-date. 28 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 29 | ) 30 | 31 | // kinds of information that will be passed around (information we pass everytime we sends something to logger microservice) 32 | type Log struct { 33 | state protoimpl.MessageState 34 | sizeCache protoimpl.SizeCache 35 | unknownFields protoimpl.UnknownFields 36 | 37 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 38 | Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` 39 | } 40 | 41 | func (x *Log) Reset() { 42 | *x = Log{} 43 | if protoimpl.UnsafeEnabled { 44 | mi := &file_logs_proto_msgTypes[0] 45 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 46 | ms.StoreMessageInfo(mi) 47 | } 48 | } 49 | 50 | func (x *Log) String() string { 51 | return protoimpl.X.MessageStringOf(x) 52 | } 53 | 54 | func (*Log) ProtoMessage() {} 55 | 56 | func (x *Log) ProtoReflect() protoreflect.Message { 57 | mi := &file_logs_proto_msgTypes[0] 58 | if protoimpl.UnsafeEnabled && x != nil { 59 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 60 | if ms.LoadMessageInfo() == nil { 61 | ms.StoreMessageInfo(mi) 62 | } 63 | return ms 64 | } 65 | return mi.MessageOf(x) 66 | } 67 | 68 | // Deprecated: Use Log.ProtoReflect.Descriptor instead. 69 | func (*Log) Descriptor() ([]byte, []int) { 70 | return file_logs_proto_rawDescGZIP(), []int{0} 71 | } 72 | 73 | func (x *Log) GetName() string { 74 | if x != nil { 75 | return x.Name 76 | } 77 | return "" 78 | } 79 | 80 | func (x *Log) GetData() string { 81 | if x != nil { 82 | return x.Data 83 | } 84 | return "" 85 | } 86 | 87 | // LogRequest is the request to perform a log operation 88 | type LogRequest struct { 89 | state protoimpl.MessageState 90 | sizeCache protoimpl.SizeCache 91 | unknownFields protoimpl.UnknownFields 92 | 93 | LogEntry *Log `protobuf:"bytes,1,opt,name=logEntry,proto3" json:"logEntry,omitempty"` 94 | } 95 | 96 | func (x *LogRequest) Reset() { 97 | *x = LogRequest{} 98 | if protoimpl.UnsafeEnabled { 99 | mi := &file_logs_proto_msgTypes[1] 100 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 101 | ms.StoreMessageInfo(mi) 102 | } 103 | } 104 | 105 | func (x *LogRequest) String() string { 106 | return protoimpl.X.MessageStringOf(x) 107 | } 108 | 109 | func (*LogRequest) ProtoMessage() {} 110 | 111 | func (x *LogRequest) ProtoReflect() protoreflect.Message { 112 | mi := &file_logs_proto_msgTypes[1] 113 | if protoimpl.UnsafeEnabled && x != nil { 114 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 115 | if ms.LoadMessageInfo() == nil { 116 | ms.StoreMessageInfo(mi) 117 | } 118 | return ms 119 | } 120 | return mi.MessageOf(x) 121 | } 122 | 123 | // Deprecated: Use LogRequest.ProtoReflect.Descriptor instead. 124 | func (*LogRequest) Descriptor() ([]byte, []int) { 125 | return file_logs_proto_rawDescGZIP(), []int{1} 126 | } 127 | 128 | func (x *LogRequest) GetLogEntry() *Log { 129 | if x != nil { 130 | return x.LogEntry 131 | } 132 | return nil 133 | } 134 | 135 | // what we send back in response to LogRequest 136 | type LogResponse struct { 137 | state protoimpl.MessageState 138 | sizeCache protoimpl.SizeCache 139 | unknownFields protoimpl.UnknownFields 140 | 141 | Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` 142 | } 143 | 144 | func (x *LogResponse) Reset() { 145 | *x = LogResponse{} 146 | if protoimpl.UnsafeEnabled { 147 | mi := &file_logs_proto_msgTypes[2] 148 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 149 | ms.StoreMessageInfo(mi) 150 | } 151 | } 152 | 153 | func (x *LogResponse) String() string { 154 | return protoimpl.X.MessageStringOf(x) 155 | } 156 | 157 | func (*LogResponse) ProtoMessage() {} 158 | 159 | func (x *LogResponse) ProtoReflect() protoreflect.Message { 160 | mi := &file_logs_proto_msgTypes[2] 161 | if protoimpl.UnsafeEnabled && x != nil { 162 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 163 | if ms.LoadMessageInfo() == nil { 164 | ms.StoreMessageInfo(mi) 165 | } 166 | return ms 167 | } 168 | return mi.MessageOf(x) 169 | } 170 | 171 | // Deprecated: Use LogResponse.ProtoReflect.Descriptor instead. 172 | func (*LogResponse) Descriptor() ([]byte, []int) { 173 | return file_logs_proto_rawDescGZIP(), []int{2} 174 | } 175 | 176 | func (x *LogResponse) GetResult() string { 177 | if x != nil { 178 | return x.Result 179 | } 180 | return "" 181 | } 182 | 183 | var File_logs_proto protoreflect.FileDescriptor 184 | 185 | var file_logs_proto_rawDesc = []byte{ 186 | 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x6f, 187 | 0x67, 0x73, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 188 | 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 189 | 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 190 | 0x61, 0x22, 0x33, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 191 | 0x25, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 192 | 0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x08, 0x6c, 0x6f, 193 | 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x25, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 194 | 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 195 | 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x3d, 0x0a, 196 | 0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x57, 197 | 0x72, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x12, 0x10, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 198 | 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 199 | 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0x5a, 0x05, 200 | 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 201 | } 202 | 203 | var ( 204 | file_logs_proto_rawDescOnce sync.Once 205 | file_logs_proto_rawDescData = file_logs_proto_rawDesc 206 | ) 207 | 208 | func file_logs_proto_rawDescGZIP() []byte { 209 | file_logs_proto_rawDescOnce.Do(func() { 210 | file_logs_proto_rawDescData = protoimpl.X.CompressGZIP(file_logs_proto_rawDescData) 211 | }) 212 | return file_logs_proto_rawDescData 213 | } 214 | 215 | var file_logs_proto_msgTypes = make([]protoimpl.MessageInfo, 3) 216 | var file_logs_proto_goTypes = []interface{}{ 217 | (*Log)(nil), // 0: logs.Log 218 | (*LogRequest)(nil), // 1: logs.LogRequest 219 | (*LogResponse)(nil), // 2: logs.LogResponse 220 | } 221 | var file_logs_proto_depIdxs = []int32{ 222 | 0, // 0: logs.LogRequest.logEntry:type_name -> logs.Log 223 | 1, // 1: logs.LogService.WriteLog:input_type -> logs.LogRequest 224 | 2, // 2: logs.LogService.WriteLog:output_type -> logs.LogResponse 225 | 2, // [2:3] is the sub-list for method output_type 226 | 1, // [1:2] is the sub-list for method input_type 227 | 1, // [1:1] is the sub-list for extension type_name 228 | 1, // [1:1] is the sub-list for extension extendee 229 | 0, // [0:1] is the sub-list for field type_name 230 | } 231 | 232 | func init() { file_logs_proto_init() } 233 | func file_logs_proto_init() { 234 | if File_logs_proto != nil { 235 | return 236 | } 237 | if !protoimpl.UnsafeEnabled { 238 | file_logs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 239 | switch v := v.(*Log); i { 240 | case 0: 241 | return &v.state 242 | case 1: 243 | return &v.sizeCache 244 | case 2: 245 | return &v.unknownFields 246 | default: 247 | return nil 248 | } 249 | } 250 | file_logs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 251 | switch v := v.(*LogRequest); i { 252 | case 0: 253 | return &v.state 254 | case 1: 255 | return &v.sizeCache 256 | case 2: 257 | return &v.unknownFields 258 | default: 259 | return nil 260 | } 261 | } 262 | file_logs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 263 | switch v := v.(*LogResponse); i { 264 | case 0: 265 | return &v.state 266 | case 1: 267 | return &v.sizeCache 268 | case 2: 269 | return &v.unknownFields 270 | default: 271 | return nil 272 | } 273 | } 274 | } 275 | type x struct{} 276 | out := protoimpl.TypeBuilder{ 277 | File: protoimpl.DescBuilder{ 278 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 279 | RawDescriptor: file_logs_proto_rawDesc, 280 | NumEnums: 0, 281 | NumMessages: 3, 282 | NumExtensions: 0, 283 | NumServices: 1, 284 | }, 285 | GoTypes: file_logs_proto_goTypes, 286 | DependencyIndexes: file_logs_proto_depIdxs, 287 | MessageInfos: file_logs_proto_msgTypes, 288 | }.Build() 289 | File_logs_proto = out.File 290 | file_logs_proto_rawDesc = nil 291 | file_logs_proto_goTypes = nil 292 | file_logs_proto_depIdxs = nil 293 | } 294 | -------------------------------------------------------------------------------- /broker-service/logs/logs.proto: -------------------------------------------------------------------------------- 1 | // This file is used to determine what kind of source code should be produced. 2 | 3 | // .proto file is used to define the structure and format of the data that will be exchanged between different systems or services. 4 | 5 | //Once the .proto file is defined, it can be compiled into language-specific code (e.g., Go, Java, Python) using a protocol buffer compiler. The generated code provides convenient APIs to work with the defined messages and services in your programming language of choice. The generated code provides functions and methods to easily create, read, and send data according to the structure defined in the .proto file. 6 | 7 | // In summary, a .proto file is used to define the structure of data that will be exchanged between programs. It acts as a contract or agreement between programs, ensuring they understand each other's data format. The file is used to generate code in different languages, making it easier to work with the defined data structures and enabling communication between programs written in different languages. 8 | 9 | syntax = "proto3"; 10 | 11 | package logs; 12 | 13 | option go_package = "/logs"; 14 | 15 | // kinds of information that will be passed around (information we pass everytime we sends something to logger microservice) 16 | message Log{ 17 | string name = 1; 18 | string data = 2; 19 | } 20 | 21 | // LogRequest is the request to perform a log operation 22 | message LogRequest { 23 | Log logEntry = 1; 24 | } 25 | 26 | // what we send back in response to LogRequest 27 | message LogResponse { 28 | string result = 1; 29 | 30 | } 31 | 32 | // define the services 33 | service LogService { 34 | // define the name of the function that this gRPC system is going to have 35 | rpc WriteLog(LogRequest) returns (LogResponse); 36 | 37 | } 38 | 39 | // We run this command using the Protocol Buffers compiler (protoc) to generate Go code from the logs.proto file 40 | // protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative logs.proto -------------------------------------------------------------------------------- /broker-service/logs/logs_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.12.4 5 | // source: logs.proto 6 | 7 | package logs 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // LogServiceClient is the client API for LogService service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type LogServiceClient interface { 25 | // define the name of the function that this gRPC system is going to have 26 | WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) 27 | } 28 | 29 | type logServiceClient struct { 30 | cc grpc.ClientConnInterface 31 | } 32 | 33 | func NewLogServiceClient(cc grpc.ClientConnInterface) LogServiceClient { 34 | return &logServiceClient{cc} 35 | } 36 | 37 | func (c *logServiceClient) WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) { 38 | out := new(LogResponse) 39 | err := c.cc.Invoke(ctx, "/logs.LogService/WriteLog", in, out, opts...) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return out, nil 44 | } 45 | 46 | // LogServiceServer is the server API for LogService service. 47 | // All implementations must embed UnimplementedLogServiceServer 48 | // for forward compatibility 49 | type LogServiceServer interface { 50 | // define the name of the function that this gRPC system is going to have 51 | WriteLog(context.Context, *LogRequest) (*LogResponse, error) 52 | mustEmbedUnimplementedLogServiceServer() 53 | } 54 | 55 | // UnimplementedLogServiceServer must be embedded to have forward compatible implementations. 56 | type UnimplementedLogServiceServer struct { 57 | } 58 | 59 | func (UnimplementedLogServiceServer) WriteLog(context.Context, *LogRequest) (*LogResponse, error) { 60 | return nil, status.Errorf(codes.Unimplemented, "method WriteLog not implemented") 61 | } 62 | func (UnimplementedLogServiceServer) mustEmbedUnimplementedLogServiceServer() {} 63 | 64 | // UnsafeLogServiceServer may be embedded to opt out of forward compatibility for this service. 65 | // Use of this interface is not recommended, as added methods to LogServiceServer will 66 | // result in compilation errors. 67 | type UnsafeLogServiceServer interface { 68 | mustEmbedUnimplementedLogServiceServer() 69 | } 70 | 71 | func RegisterLogServiceServer(s grpc.ServiceRegistrar, srv LogServiceServer) { 72 | s.RegisterService(&LogService_ServiceDesc, srv) 73 | } 74 | 75 | func _LogService_WriteLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 76 | in := new(LogRequest) 77 | if err := dec(in); err != nil { 78 | return nil, err 79 | } 80 | if interceptor == nil { 81 | return srv.(LogServiceServer).WriteLog(ctx, in) 82 | } 83 | info := &grpc.UnaryServerInfo{ 84 | Server: srv, 85 | FullMethod: "/logs.LogService/WriteLog", 86 | } 87 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 88 | return srv.(LogServiceServer).WriteLog(ctx, req.(*LogRequest)) 89 | } 90 | return interceptor(ctx, in, info, handler) 91 | } 92 | 93 | // LogService_ServiceDesc is the grpc.ServiceDesc for LogService service. 94 | // It's only intended for direct use with grpc.RegisterService, 95 | // and not to be introspected or modified (even as a copy) 96 | var LogService_ServiceDesc = grpc.ServiceDesc{ 97 | ServiceName: "logs.LogService", 98 | HandlerType: (*LogServiceServer)(nil), 99 | Methods: []grpc.MethodDesc{ 100 | { 101 | MethodName: "WriteLog", 102 | Handler: _LogService_WriteLog_Handler, 103 | }, 104 | }, 105 | Streams: []grpc.StreamDesc{}, 106 | Metadata: "logs.proto", 107 | } 108 | -------------------------------------------------------------------------------- /demo.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/demo.webm -------------------------------------------------------------------------------- /front-end/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/front-end/.DS_Store -------------------------------------------------------------------------------- /front-end/cmd/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/front-end/cmd/.DS_Store -------------------------------------------------------------------------------- /front-end/cmd/web/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "html/template" 7 | "log" 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | func main() { 13 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 14 | render(w, "test.page.gohtml") 15 | }) 16 | 17 | fmt.Println("Starting front end service on port 8082") 18 | err := http.ListenAndServe(":8082", nil) 19 | if err != nil { 20 | log.Panic(err) 21 | } 22 | } 23 | 24 | //go:embed templates 25 | var templateFS embed.FS 26 | 27 | // http.ResponseWriter which allows us to write the HTTP response, 28 | // and t, a string that represents the template to render. 29 | func render(w http.ResponseWriter, t string) { 30 | 31 | partials := []string{ 32 | "templates/base.layout.gohtml", 33 | "templates/header.partial.gohtml", 34 | "templates/footer.partial.gohtml", 35 | } 36 | 37 | var templateSlice []string 38 | templateSlice = append(templateSlice, fmt.Sprintf("templates/%s", t)) 39 | 40 | for _, x := range partials { 41 | templateSlice = append(templateSlice, x) 42 | } 43 | 44 | tmpl, err := template.ParseFS(templateFS, templateSlice...) 45 | if err != nil { 46 | http.Error(w, err.Error(), http.StatusInternalServerError) 47 | return // exit the render function 48 | } 49 | fmt.Println(tmpl) 50 | 51 | var data struct { 52 | BrokerURL string 53 | } 54 | 55 | data.BrokerURL = os.Getenv("BROKER_URL") 56 | // data.BrokerURL = "http://localhost:8080" // for connecting to broker through load balancer (cmd minikube tunnel) 57 | 58 | // use the Execute method to render the template 59 | if err := tmpl.Execute(w, data); err != nil { 60 | http.Error(w, err.Error(), http.StatusInternalServerError) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /front-end/cmd/web/templates/base.layout.gohtml: -------------------------------------------------------------------------------- 1 | {{define "base" }} 2 | 3 | 4 | 5 | {{template "header" .}} 6 | 7 | 8 | 9 | {{block "content" .}} 10 | 11 | {{end}} 12 | 13 | {{block "js" .}} 14 | 15 | {{end}} 16 | 17 | {{template "footer" .}} 18 | 19 | 20 | 21 | 22 | {{end}} -------------------------------------------------------------------------------- /front-end/cmd/web/templates/footer.partial.gohtml: -------------------------------------------------------------------------------- 1 | {{define "footer"}} 2 |
3 |
4 |
5 |
6 | Copyright © GoCode.ca 7 |
8 |
9 |
10 | {{end}} -------------------------------------------------------------------------------- /front-end/cmd/web/templates/header.partial.gohtml: -------------------------------------------------------------------------------- 1 | {{define "header"}} 2 | 3 | 4 | 5 | 7 | 8 | Microservices in Go 9 | 10 | 11 | 12 | 13 | {{end}} -------------------------------------------------------------------------------- /front-end/cmd/web/templates/test.page.gohtml: -------------------------------------------------------------------------------- 1 | {{template "base" .}} 2 | 3 | {{define "content" }} 4 |
5 |
6 |
7 |

Test microservices

8 |
9 | Test Broker 10 | Test Auth 11 | Test Log 12 | Test Mail 13 | Test gRPC log 14 | 15 |
16 | Output shows here... 17 |
18 |
19 |
20 |
21 |
22 |

Sent

23 |
24 |
Nothing sent yet...
25 |
26 |
27 |
28 |

Received

29 |
30 |
Nothing received yet...
31 |
32 |
33 |
34 |
35 | {{end}} 36 | 37 | {{define "js"}} 38 | 213 | {{end}} -------------------------------------------------------------------------------- /front-end/front-end.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN mkdir /app 4 | 5 | COPY frontEndApp /app 6 | 7 | 8 | CMD ["/app/frontEndApp"] -------------------------------------------------------------------------------- /front-end/go.mod: -------------------------------------------------------------------------------- 1 | module frontend 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /go-micro-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/go-micro-arch.png -------------------------------------------------------------------------------- /go.work: -------------------------------------------------------------------------------- 1 | go 1.18 2 | 3 | use ( 4 | ./authentication-service 5 | ./broker-service 6 | ./front-end 7 | ./listener-service 8 | ./logger-service 9 | ./mail-service 10 | ) 11 | -------------------------------------------------------------------------------- /go.work.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= 3 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 4 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 5 | github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= 6 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 7 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 8 | github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= 9 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 10 | github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= 11 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 12 | github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= 13 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 14 | github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= 15 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 16 | github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= 17 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 18 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 19 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 21 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 22 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 23 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 24 | github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= 25 | github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= 26 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 27 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 28 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 29 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 30 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 31 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 32 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 33 | golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 34 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 35 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 36 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 37 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 38 | golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= 39 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 40 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 41 | golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= 42 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 43 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 44 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 45 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 46 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 47 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 48 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= 50 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 51 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 52 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 53 | golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= 54 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 55 | golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 56 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 57 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 58 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 59 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 60 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 61 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 62 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 63 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 64 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 65 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 66 | -------------------------------------------------------------------------------- /listener-service/README.md: -------------------------------------------------------------------------------- 1 | ## Listener Service 2 | 3 | ### This Service handles the message queues received from the broker service . 4 | 5 | - The listener service is a message broker for receiving and handling messages(request) from the broker service and calls the right service to hadle that particular request . 6 | 7 | The User makes a request to the broker & pushes the request to the rabbitmq, the listener-service gets a request from the queue to either , authenticate, send an email, and log something, if a user tries to login it sends a request to the authentication service and takes action based on the result attempt. 8 | -------------------------------------------------------------------------------- /listener-service/event/consumer.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | 10 | amqp "github.com/rabbitmq/amqp091-go" 11 | ) 12 | 13 | // consumer type which will be used for receiving events from the Q 14 | type Consumer struct { 15 | conn *amqp.Connection 16 | queueName string 17 | } 18 | 19 | type Payload struct { 20 | Name string `json:"name"` 21 | Data string `json:"data"` 22 | } 23 | 24 | func NewConsumer(conn *amqp.Connection) (Consumer, error) { 25 | consumer := Consumer{ 26 | conn: conn, 27 | } 28 | 29 | // Now we'll have to set up this consumer. We're going to have to open up a channel and declare an exchange 30 | err := consumer.setup() 31 | if err != nil { 32 | return Consumer{}, err 33 | } 34 | 35 | return consumer, nil 36 | 37 | } 38 | 39 | func (consumer *Consumer) setup() error { 40 | channel, err := consumer.conn.Channel() 41 | if err != nil { 42 | return err 43 | } 44 | 45 | return declareExchange(channel) 46 | } 47 | 48 | func (consumer *Consumer) Listen(topics []string) error { 49 | ch, err := consumer.conn.Channel() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | // close the channel when we're done with it otherwise we'll have a resource leak 55 | defer ch.Close() 56 | 57 | q, err := declareRandomQueue(ch) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | for _, s := range topics { 63 | // bind our channel to each of these topics (bind s to q) 64 | ch.QueueBind( 65 | q.Name, 66 | s, //topic 67 | "logs_topic", 68 | false, 69 | nil, 70 | ) 71 | 72 | if err != nil { 73 | return err 74 | } 75 | } 76 | 77 | messages, err := ch.Consume(q.Name, "", true, false, false, false, nil) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | // Consume all of the things that come from Rabbit MQ until I exit this application 83 | forever := make(chan bool) 84 | 85 | go func() { 86 | for d := range messages { 87 | var payload Payload 88 | _ = json.Unmarshal(d.Body, &payload) 89 | 90 | go handlePayload(payload) 91 | } 92 | }() 93 | 94 | fmt.Printf("Waiting for message [Exchange, Queue] [logs_topic, %s]\n", q.Name) 95 | 96 | <-forever // this channel will keep going the block of code forever 97 | 98 | return nil 99 | 100 | } 101 | 102 | // Using this function we're going to take an action based upon the name of an event that get pushed to us from the queue 103 | func handlePayload(payload Payload) { 104 | switch payload.Name { 105 | case "log", "event": 106 | // log whatever we get 107 | err := logEvent(payload) 108 | if err != nil { 109 | log.Println(err) 110 | } 111 | case "auth": 112 | // authenticate 113 | 114 | // we can have as many cases as we want, as long as we write the logic 115 | 116 | default: 117 | err := logEvent(payload) 118 | if err != nil { 119 | log.Println(err) 120 | } 121 | } 122 | 123 | } 124 | 125 | // implement the logic to log an event once we've received it from RabbitMQ 126 | func logEvent(entry Payload) error { 127 | jsonData, _ := json.MarshalIndent(entry, "", "\t") 128 | 129 | logServiceURL := "http://logger-service/log" 130 | 131 | request, err := http.NewRequest("POST", logServiceURL, bytes.NewBuffer(jsonData)) 132 | if err != nil { 133 | return err 134 | } 135 | 136 | request.Header.Set("Content-Type", "application/json") 137 | 138 | client := &http.Client{} 139 | 140 | response, err := client.Do(request) 141 | if err != nil { 142 | return err 143 | } 144 | defer response.Body.Close() 145 | 146 | if response.StatusCode != http.StatusAccepted { 147 | return err 148 | } 149 | 150 | return nil 151 | } 152 | -------------------------------------------------------------------------------- /listener-service/event/event.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | amqp "github.com/rabbitmq/amqp091-go" 5 | ) 6 | 7 | func declareExchange(ch *amqp.Channel) error { 8 | return ch.ExchangeDeclare( 9 | "logs_topic", // name of the exchange 10 | "topic", // type 11 | true, //durable 12 | false, // autodeleted 13 | false, // is this an exchange that's just used internally 14 | false, // no-wait? 15 | nil, // arguments? 16 | ) 17 | } 18 | 19 | func declareRandomQueue(ch *amqp.Channel) (amqp.Queue, error) { 20 | return ch.QueueDeclare( // declaring a queue with these attributes 21 | "", // name? 22 | false, // durable? 23 | false, // delete when unused? 24 | true, // exclusive? 25 | false, // no-wait? 26 | nil, //arguments 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /listener-service/go.mod: -------------------------------------------------------------------------------- 1 | module listener 2 | 3 | go 1.18 4 | 5 | require github.com/rabbitmq/amqp091-go v1.8.1 // indirect 6 | -------------------------------------------------------------------------------- /listener-service/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 4 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 5 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/rabbitmq/amqp091-go v1.8.1 h1:RejT1SBUim5doqcL6s7iN6SBmsQqyTgXb1xMlH0h1hA= 8 | github.com/rabbitmq/amqp091-go v1.8.1/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 11 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 12 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 13 | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 14 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 15 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 16 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 17 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 18 | -------------------------------------------------------------------------------- /listener-service/listener-service.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN mkdir /app 4 | 5 | COPY listenerApp /app 6 | 7 | 8 | CMD ["/app/listenerApp"] -------------------------------------------------------------------------------- /listener-service/listenerApp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/listener-service/listenerApp -------------------------------------------------------------------------------- /listener-service/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "listener/event" 6 | "log" 7 | "math" 8 | "os" 9 | "time" 10 | 11 | // AMQP is a messaging protocol used for communication between client applications and messaging brokers. It defines a set of rules and formats that ensure interoperability between different components of a messaging system. 12 | // https://www.rabbitmq.com/tutorials/amqp-concepts.html 13 | amqp "github.com/rabbitmq/amqp091-go" 14 | ) 15 | 16 | func main() { 17 | // try to connect to rabbitmq 18 | rabbitConn, err := connect() 19 | if err != nil { 20 | log.Println(err) 21 | os.Exit(1) 22 | } 23 | defer rabbitConn.Close() 24 | 25 | // start listening for messages 26 | log.Println("Listening for and consuming RabbitMQ messages...") 27 | 28 | // create a consumer that consumes message from the queue 29 | consumer, err := event.NewConsumer(rabbitConn) 30 | if err != nil { 31 | log.Println(err) 32 | } 33 | 34 | // watch the queue and comsume events 35 | err = consumer.Listen([]string{"log.INFO", "log.WARNING", "log.ERROR"}) 36 | if err != nil { 37 | log.Println(err) 38 | } 39 | } 40 | 41 | func connect() (*amqp.Connection, error) { 42 | var counts int64 43 | var backoff = 1 * time.Second 44 | var connection *amqp.Connection 45 | 46 | // don't continue until rabbit is ready 47 | for { 48 | // rabbitmq matches our service in docker compose 49 | c, err := amqp.Dial("amqp://guest:guest@rabbitmq") 50 | if err != nil { 51 | fmt.Println("RabbitMQ not yet ready...") 52 | counts++ 53 | } else { 54 | log.Println("Connected to RabbitMQ") 55 | connection = c 56 | break 57 | } 58 | 59 | // If we didn't connect : 60 | 61 | // if we didn't connect after 5 tries 62 | if counts > 5 { 63 | fmt.Println(err) 64 | return nil, err 65 | } 66 | 67 | // if we haven't tried atleast 5 times ,then call backoff 68 | backoff = time.Duration(math.Pow(float64(counts), 2)) * time.Second 69 | log.Println("backing off....") 70 | // suspends the execution of the loop for the calculated duration. 71 | time.Sleep(backoff) 72 | continue 73 | } 74 | 75 | return connection, nil 76 | } 77 | 78 | // AMQP stands for Advanced Message Queuing Protocol. It is an open standard application layer protocol for message-oriented middleware. AMQP defines a set of rules for how messages are formatted, delivered, and routed between applications. It is supported by a wide range of messaging brokers, including RabbitMQ. 79 | 80 | // Imagine you have two applications, A and B. You want A to be able to send messages to B, but you don't want A to have to wait for B to be ready to receive the message. You also don't want A to have to know anything about B's specific location or address.AMQP can help you solve this problem. AMQP provides a way for A to send messages to B without having to know anything about B's specific location or address. AMQP does this by using a messaging broker. The messaging broker is like a post office. It receives messages from A and stores them until B is ready to receive them.When B is ready to receive messages, it connects to the messaging broker and asks for messages. The messaging broker then delivers the messages to B.AMQP is a very flexible protocol. It can be used to send messages between applications in a variety of ways. For example, you can use AMQP to send messages between applications that are running on the same computer, or you can use AMQP to send messages between applications that are running on different computers in different parts of the world. 81 | 82 | // Here are some of the key features of AMQP: 83 | 84 | // Message orientation AMQP messages are sent and received in a message-oriented way, meaning that they are not tied to a specific connection or session. This makes AMQP more scalable and reliable than other messaging protocols, such as TCP/IP sockets. 85 | 86 | // Queuing AMQP messages can be stored in queues, which allows applications to send messages without having to wait for a recipient to be available. This can improve performance and reliability, especially in high-volume messaging applications. 87 | 88 | // Routing AMQP messages can be routed to specific recipients based on a variety of criteria, such as the message content or the recipient's address. This allows applications to decouple from each other and communicate in a loosely coupled way. 89 | 90 | // Security AMQP supports a variety of security features, such as authentication, authorization, and encryption. This helps to protect messages from unauthorized access and tampering. 91 | -------------------------------------------------------------------------------- /logger-service/cmd/api/grpc.go: -------------------------------------------------------------------------------- 1 | // We'll write a function called writeLog() that will allow us to communicate over gRPC 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "log" 9 | "logger-service/data" 10 | "logger-service/logs" 11 | "net" 12 | 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | type LogServer struct { 17 | logs.UnimplementedLogServiceServer // This field is going to be required for every service we ever write over gRPC (to ensure backward compatibility) 18 | Models data.Models // have access to necessary methods to write to Mongo 19 | 20 | } 21 | 22 | func (l *LogServer) WriteLog(ctx context.Context, req *logs.LogRequest) (*logs.LogResponse, error) { 23 | input := req.GetLogEntry() // gets the input 24 | log.Println("This is the input we get : ", input) 25 | 26 | // write the log 27 | logEntry := data.LogEntry{ 28 | Name: input.Name, 29 | Data: input.Data, 30 | } 31 | 32 | err := l.Models.LogEntry.Insert(logEntry) 33 | if err != nil { 34 | res := &logs.LogResponse{Result: "failed response coming from WriteLog func"} 35 | return res, err 36 | } 37 | 38 | // return response if nothing went wrong 39 | res := &logs.LogResponse{Result: "logged! response coming from WriteLog func"} 40 | return res, nil 41 | 42 | } 43 | 44 | // function to listen for gRPC connections (will start the gRPC listener) 45 | func (app *Config) gRPCListen() { 46 | lis, err := net.Listen("tcp", fmt.Sprintf(":%s", gRpcPort)) 47 | if err != nil { 48 | log.Fatalf("Failed to listen for gRPC %v", err) 49 | } 50 | 51 | s := grpc.NewServer() 52 | 53 | // register the service 54 | logs.RegisterLogServiceServer(s, &LogServer{Models: app.Models}) 55 | 56 | log.Printf("gRPC Server started on port %s", gRpcPort) 57 | 58 | if err := s.Serve(lis); err != nil { 59 | log.Fatalf("Failed to listen for gRPC %v", err) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /logger-service/cmd/api/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "logger-service/data" 5 | "net/http" 6 | ) 7 | 8 | type JSONPayload struct { 9 | Name string `json:"name"` 10 | Data string `json:"data"` 11 | } 12 | 13 | func (app *Config) WriteLog(w http.ResponseWriter, r *http.Request) { 14 | // read json into var 15 | var requestPayload JSONPayload 16 | _ = app.readJSON(w, r, &requestPayload) 17 | 18 | // insert data 19 | event := data.LogEntry{ 20 | Name: requestPayload.Name, 21 | Data: requestPayload.Data, 22 | } 23 | 24 | err := app.Models.LogEntry.Insert(event) 25 | if err != nil { 26 | app.errorJSON(w, err) 27 | return 28 | } 29 | 30 | resp := jsonResponse{ 31 | Error: false, 32 | Message: "logged", 33 | } 34 | 35 | app.writeJSON(w, http.StatusAccepted, resp) 36 | } 37 | -------------------------------------------------------------------------------- /logger-service/cmd/api/helpers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type jsonResponse struct { 11 | Error bool `json:"errorrr"` 12 | Message string `json:"message"` 13 | Data any `json:"data,omitempty"` 14 | } 15 | 16 | // function to read json 17 | func (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error { 18 | maxBytes := 1048576 // 1 MB 19 | 20 | // limiting the size of incoming request bodies 21 | r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes)) 22 | 23 | dec := json.NewDecoder(r.Body) 24 | err := dec.Decode(data) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | // decode a JSON value into an empty struct{}. 30 | err = dec.Decode(&struct{}{}) 31 | // io.EOF is an error value indicating the end of the input source has been reached. 32 | // If err is not equal to io.EOF, it means that the decoding process did not reach the end of the input source, and there are additional JSON values remaining. 33 | if err != io.EOF { 34 | return errors.New("body must have only a single json value") 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // function to write json 41 | // The ellipsis (...) before http.Header indicates that it can accept a variable number of arguments, allowing you to pass or store multiple http.Header values. 42 | func (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error { 43 | out, err := json.Marshal(data) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(headers) > 0 { 49 | for key, value := range headers[0] { 50 | w.Header()[key] = value 51 | } 52 | } 53 | 54 | w.Header().Set("Content-Type", "application/json") 55 | w.WriteHeader(status) 56 | 57 | _, err = w.Write(out) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // function to write error message as json 66 | func (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error { 67 | statusCode := http.StatusBadRequest 68 | 69 | if len(status) > 0 { 70 | statusCode = status[0] 71 | } 72 | 73 | var payload jsonResponse 74 | payload.Error = true 75 | payload.Message = err.Error() 76 | 77 | return app.writeJSON(w, statusCode, payload) 78 | } 79 | -------------------------------------------------------------------------------- /logger-service/cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "logger-service/data" 8 | "net" 9 | "net/http" 10 | "net/rpc" 11 | "time" 12 | 13 | "go.mongodb.org/mongo-driver/mongo" 14 | "go.mongodb.org/mongo-driver/mongo/options" 15 | ) 16 | 17 | const ( 18 | webPort = "80" 19 | rpcPort = "5001" 20 | mongoURL = "mongodb://mongo:27017" 21 | gRpcPort = "50001" 22 | ) 23 | 24 | var client *mongo.Client 25 | 26 | type Config struct { 27 | Models data.Models 28 | } 29 | 30 | func main() { 31 | // connect to mongo 32 | mongoClient, err := connectToMongo() 33 | if err != nil { 34 | log.Panic(err) 35 | } 36 | client = mongoClient 37 | 38 | // create a context in order to disconnect 39 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 40 | defer cancel() 41 | 42 | // close connection 43 | defer func() { 44 | if err = client.Disconnect(ctx); err != nil { 45 | panic(err) 46 | } 47 | }() 48 | 49 | app := Config{ 50 | Models: data.New(client), 51 | } 52 | 53 | // Register the RPC Server 54 | err = rpc.Register(new(RPCServer)) 55 | 56 | go app.rpcListen() 57 | 58 | // Listen for gRPC connection 59 | go app.gRPCListen() 60 | 61 | // start web server 62 | log.Println("Starting service on port", webPort) 63 | srv := &http.Server{ 64 | Addr: fmt.Sprintf(":%s", webPort), 65 | Handler: app.routes(), 66 | } 67 | 68 | err = srv.ListenAndServe() 69 | if err != nil { 70 | log.Panic() 71 | } 72 | 73 | } 74 | 75 | // func (app *Config) serve() { 76 | // srv := &http.Server{ 77 | // Addr: fmt.Sprintf(":%s", webPort), 78 | // Handler: app.routes(), 79 | // } 80 | 81 | // err := srv.ListenAndServe() 82 | // if err != nil { 83 | // log.Panic() 84 | // } 85 | // } 86 | 87 | func connectToMongo() (*mongo.Client, error) { 88 | // create connection options 89 | clientOptions := options.Client().ApplyURI(mongoURL) 90 | clientOptions.SetAuth(options.Credential{ 91 | Username: "admin", 92 | Password: "password", 93 | }) 94 | 95 | // connect 96 | c, err := mongo.Connect(context.TODO(), clientOptions) 97 | if err != nil { 98 | log.Println("Error connecting:", err) 99 | return nil, err 100 | } 101 | 102 | log.Println("Connected to mongo!") 103 | 104 | return c, nil 105 | } 106 | 107 | func (app *Config) rpcListen() error { 108 | log.Println("Starting RPC server on port ", rpcPort) 109 | 110 | listen, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%s", rpcPort)) 111 | if err != nil { 112 | return err 113 | } 114 | 115 | defer listen.Close() 116 | 117 | for { 118 | rpcConn, err := listen.Accept() 119 | if err != nil { 120 | continue 121 | } 122 | 123 | go rpc.ServeConn(rpcConn) 124 | } 125 | } 126 | 127 | // // Think of a context as a container that holds important information for a particular operation. It carries things like request-scoped values ( authentication tokens, request IDs,etc), deadlines (time limits) and cancellation signals. It helps manage and control how long an operation should take and allows for stopping or canceling it if needed. 128 | 129 | // Imagine you have a program with multiple tasks running at the same time. Each task might need to know how much time it has left to complete or if it should stop because something went wrong. That's where the context package comes in. 130 | 131 | // Here are some key things to understand about the context package: 132 | 133 | // Context: A context is like a box that holds important information related to a specific task or operation. It includes details like deadlines (time limits) and cancellation signals. 134 | 135 | // Cancelling: Sometimes, an operation may take too long, or you might want to stop it for some reason. With the context package, you can create a cancellation signal and pass it along with the context. When the signal is triggered, all the related tasks can gracefully stop what they're doing. 136 | 137 | // Timeouts: You can set deadlines or time limits for tasks using the context package. If a task doesn't finish within the specified time, it can be automatically canceled. This helps ensure that your program doesn't get stuck waiting forever for a task to complete. 138 | 139 | // Passing Values: The context package also allows you to pass values between different parts of your program. For example, you might want to pass an authentication token or a request ID across different function calls or goroutines. The context can hold and carry these values for you. 140 | 141 | // By using the context package, you can better manage and control concurrent operations, timeouts, cancellations, and the exchange of values in your Go programs. It provides a structured way to handle these common challenges and helps make your programs more reliable and responsive. 142 | 143 | // The context package in Go allows you to store and access the request scoped values throughout the execution flow of the request. 144 | 145 | // Request-scoped values are data or information that is associated with a specific HTTP request in a web application. They are values that are relevant and specific to a particular request and should be accessible across different parts of the code that handle the request. 146 | 147 | // Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context 148 | -------------------------------------------------------------------------------- /logger-service/cmd/api/routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-chi/chi/v5" 7 | "github.com/go-chi/chi/v5/middleware" 8 | "github.com/go-chi/cors" 9 | ) 10 | 11 | func (app *Config) routes() http.Handler { 12 | mux := chi.NewRouter() 13 | 14 | // specify who is allowed to connect 15 | mux.Use(cors.Handler(cors.Options{ 16 | AllowedOrigins: []string{"https://*", "http://*"}, 17 | AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 18 | AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, 19 | ExposedHeaders: []string{"Link"}, 20 | AllowCredentials: true, 21 | MaxAge: 300, 22 | })) 23 | 24 | mux.Use(middleware.Heartbeat("/ping")) 25 | 26 | mux.Post("/log", app.WriteLog) 27 | 28 | return mux 29 | } 30 | -------------------------------------------------------------------------------- /logger-service/cmd/api/rpc.go: -------------------------------------------------------------------------------- 1 | // The big difference between RPC and gRPC is that you don't have to have both ends (one microservice acting as client & other as server) written in go. 2 | // gRPC supports all languages and even if both ends are written in different languages. 3 | 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "logger-service/data" 10 | "time" 11 | ) 12 | 13 | type RPCServer struct{} 14 | 15 | // define the kind of payload that we're going to receive from RPC. 16 | // kind of data we're going to receive for any methods that are tied to RPCServer 17 | type RPCPayload struct { 18 | Name string 19 | Data string 20 | } 21 | 22 | func (r *RPCServer) LogInfo(payload RPCPayload, resp *string) error { 23 | collection := client.Database("logs").Collection("logs") 24 | _, err := collection.InsertOne(context.TODO(), data.LogEntry{ 25 | Name: payload.Name, 26 | Data: payload.Data, 27 | CreatedAt: time.Now(), 28 | }) 29 | if err != nil { 30 | log.Println("error writing to mongo", err) 31 | return err 32 | } 33 | 34 | *resp = "Processed payload via RPC:" + payload.Name 35 | return nil 36 | 37 | } 38 | -------------------------------------------------------------------------------- /logger-service/data/models.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | "go.mongodb.org/mongo-driver/mongo/options" 12 | ) 13 | 14 | var client *mongo.Client 15 | 16 | func New(mongo *mongo.Client) Models { 17 | client = mongo 18 | 19 | return Models{ 20 | LogEntry: LogEntry{}, 21 | } 22 | } 23 | 24 | type Models struct { 25 | LogEntry LogEntry 26 | } 27 | 28 | type LogEntry struct { 29 | ID string `bson:"_id,omitempty" json:"id,omitempty"` 30 | Name string `bson:"name" json:"name"` 31 | Data string `bson:"data" json:"data"` 32 | CreatedAt time.Time `bson:"created_at" json:"created_at"` 33 | UpdatedAt time.Time `bson:"updated_at" json:"updated_at"` 34 | } 35 | 36 | // function to insert a document into the collection in mongo 37 | func (l *LogEntry) Insert(entry LogEntry) error { 38 | collection := client.Database("logs").Collection("logs") 39 | 40 | _, err := collection.InsertOne(context.TODO(), LogEntry{ 41 | Name: entry.Name, 42 | Data: entry.Data, 43 | CreatedAt: time.Now(), 44 | UpdatedAt: time.Now(), 45 | }) 46 | if err != nil { 47 | log.Println("Error inserting into logs:", err) 48 | return err 49 | } 50 | 51 | return nil 52 | } 53 | 54 | func (l *LogEntry) All() ([]*LogEntry, error) { 55 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 56 | defer cancel() 57 | 58 | collection := client.Database("logs").Collection("logs") 59 | 60 | // specify specific options when we want to interact with above collection 61 | opts := options.Find() 62 | 63 | // specify specific options when we want to interact with above collection 64 | opts.SetSort(bson.D{{"created_at", -1}}) 65 | 66 | cursor, err := collection.Find(context.TODO(), bson.D{}, opts) 67 | if err != nil { 68 | log.Println("Finding all docs error:", err) 69 | return nil, err 70 | } 71 | defer cursor.Close(ctx) 72 | 73 | // variable to store our results in 74 | var logs []*LogEntry // slice of pointer to LogEntry 75 | 76 | for cursor.Next(ctx) { // DOn't take more than 15 sec 77 | var item LogEntry 78 | 79 | err := cursor.Decode(&item) 80 | if err != nil { 81 | log.Print("Error decoding log into slice:", err) 82 | return nil, err 83 | } else { 84 | logs = append(logs, &item) 85 | } 86 | } 87 | 88 | return logs, nil 89 | } 90 | 91 | // get a log entry by id 92 | func (l *LogEntry) GetOne(id string) (*LogEntry, error) { 93 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 94 | defer cancel() 95 | 96 | collection := client.Database("logs").Collection("logs") 97 | 98 | docID, err := primitive.ObjectIDFromHex(id) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | var entry LogEntry 104 | err = collection.FindOne(ctx, bson.M{"_id": docID}).Decode(&entry) 105 | if err != nil { 106 | return nil, err 107 | } 108 | 109 | return &entry, nil 110 | } 111 | 112 | // Drop the entire collection (log entries) 113 | func (l *LogEntry) DropCollection() error { 114 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 115 | defer cancel() 116 | 117 | collection := client.Database("logs").Collection("logs") 118 | 119 | if err := collection.Drop(ctx); err != nil { 120 | return err 121 | } 122 | 123 | return nil 124 | } 125 | 126 | func (l *LogEntry) Update() (*mongo.UpdateResult, error) { 127 | ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) 128 | defer cancel() 129 | 130 | collection := client.Database("logs").Collection("logs") 131 | 132 | docID, err := primitive.ObjectIDFromHex(l.ID) 133 | if err != nil { 134 | return nil, err 135 | } 136 | 137 | result, err := collection.UpdateOne( 138 | ctx, 139 | bson.M{"_id": docID}, 140 | bson.D{ 141 | {"$set", bson.D{ 142 | {"name", l.Name}, 143 | {"data", l.Data}, 144 | {"updated_at", time.Now()}, 145 | }}, 146 | }, 147 | ) 148 | 149 | if err != nil { 150 | return nil, err 151 | } 152 | 153 | return result, nil 154 | } 155 | -------------------------------------------------------------------------------- /logger-service/go.mod: -------------------------------------------------------------------------------- 1 | module logger-service 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/go-chi/chi v1.5.4 // indirect 7 | github.com/go-chi/chi/v5 v5.0.8 // indirect 8 | github.com/go-chi/cors v1.2.1 // indirect 9 | github.com/golang/protobuf v1.5.3 // indirect 10 | github.com/golang/snappy v0.0.1 // indirect 11 | github.com/klauspost/compress v1.13.6 // indirect 12 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect 13 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 14 | github.com/xdg-go/scram v1.1.2 // indirect 15 | github.com/xdg-go/stringprep v1.0.4 // indirect 16 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 17 | go.mongodb.org/mongo-driver v1.12.0 // indirect 18 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect 19 | golang.org/x/net v0.9.0 // indirect 20 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect 21 | golang.org/x/sys v0.7.0 // indirect 22 | golang.org/x/text v0.9.0 // indirect 23 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 24 | google.golang.org/grpc v1.56.1 // indirect 25 | google.golang.org/protobuf v1.31.0 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /logger-service/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs= 3 | github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg= 4 | github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= 5 | github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 6 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 7 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 8 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 9 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 10 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 11 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= 12 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 13 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 14 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 15 | github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= 16 | github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 17 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= 18 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 19 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 20 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 21 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= 22 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 23 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= 24 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 25 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 26 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 27 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 28 | go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= 29 | go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= 30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 31 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 32 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= 33 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 34 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 35 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 36 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 37 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 38 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 39 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= 40 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= 41 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= 43 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 44 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 45 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 46 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 48 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 49 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 50 | golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= 51 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 52 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 53 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 54 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 55 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 56 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 57 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 58 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 59 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 60 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 61 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 62 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 63 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 64 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 65 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 66 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 67 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 68 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= 69 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= 70 | google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= 71 | google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= 72 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 73 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 74 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 75 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 76 | google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= 77 | google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 78 | -------------------------------------------------------------------------------- /logger-service/logger-service.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN mkdir /app 4 | 5 | COPY loggerServiceApp /app 6 | 7 | CMD ["/app/loggerServiceApp"] -------------------------------------------------------------------------------- /logger-service/loggerServiceApp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/logger-service/loggerServiceApp -------------------------------------------------------------------------------- /logger-service/logs/logs.pb.go: -------------------------------------------------------------------------------- 1 | // This file is used to determine what kind of source code should be produced. 2 | 3 | // .proto file is used to define the structure and format of the data that will be exchanged between different systems or services. 4 | 5 | //Once the .proto file is defined, it can be compiled into language-specific code (e.g., Go, Java, Python) using a protocol buffer compiler. The generated code provides convenient APIs to work with the defined messages and services in your programming language of choice. The generated code provides functions and methods to easily create, read, and send data according to the structure defined in the .proto file. 6 | 7 | // Code generated by protoc-gen-go. DO NOT EDIT. 8 | // versions: 9 | // protoc-gen-go v1.27.1 10 | // protoc v3.12.4 11 | // source: logs.proto 12 | 13 | package logs 14 | 15 | import ( 16 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 17 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 18 | reflect "reflect" 19 | sync "sync" 20 | ) 21 | 22 | const ( 23 | // Verify that this generated code is sufficiently up-to-date. 24 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 25 | // Verify that runtime/protoimpl is sufficiently up-to-date. 26 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 27 | ) 28 | 29 | // kinds of information that will be passed around (information we pass everytime we sends something to logger microservice) 30 | type Log struct { 31 | state protoimpl.MessageState 32 | sizeCache protoimpl.SizeCache 33 | unknownFields protoimpl.UnknownFields 34 | 35 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 36 | Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` 37 | } 38 | 39 | func (x *Log) Reset() { 40 | *x = Log{} 41 | if protoimpl.UnsafeEnabled { 42 | mi := &file_logs_proto_msgTypes[0] 43 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 44 | ms.StoreMessageInfo(mi) 45 | } 46 | } 47 | 48 | func (x *Log) String() string { 49 | return protoimpl.X.MessageStringOf(x) 50 | } 51 | 52 | func (*Log) ProtoMessage() {} 53 | 54 | func (x *Log) ProtoReflect() protoreflect.Message { 55 | mi := &file_logs_proto_msgTypes[0] 56 | if protoimpl.UnsafeEnabled && x != nil { 57 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 58 | if ms.LoadMessageInfo() == nil { 59 | ms.StoreMessageInfo(mi) 60 | } 61 | return ms 62 | } 63 | return mi.MessageOf(x) 64 | } 65 | 66 | // Deprecated: Use Log.ProtoReflect.Descriptor instead. 67 | func (*Log) Descriptor() ([]byte, []int) { 68 | return file_logs_proto_rawDescGZIP(), []int{0} 69 | } 70 | 71 | func (x *Log) GetName() string { 72 | if x != nil { 73 | return x.Name 74 | } 75 | return "" 76 | } 77 | 78 | func (x *Log) GetData() string { 79 | if x != nil { 80 | return x.Data 81 | } 82 | return "" 83 | } 84 | 85 | // LogRequest is the request to perform a log operation 86 | type LogRequest struct { 87 | state protoimpl.MessageState 88 | sizeCache protoimpl.SizeCache 89 | unknownFields protoimpl.UnknownFields 90 | 91 | LogEntry *Log `protobuf:"bytes,1,opt,name=logEntry,proto3" json:"logEntry,omitempty"` 92 | } 93 | 94 | func (x *LogRequest) Reset() { 95 | *x = LogRequest{} 96 | if protoimpl.UnsafeEnabled { 97 | mi := &file_logs_proto_msgTypes[1] 98 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 99 | ms.StoreMessageInfo(mi) 100 | } 101 | } 102 | 103 | func (x *LogRequest) String() string { 104 | return protoimpl.X.MessageStringOf(x) 105 | } 106 | 107 | func (*LogRequest) ProtoMessage() {} 108 | 109 | func (x *LogRequest) ProtoReflect() protoreflect.Message { 110 | mi := &file_logs_proto_msgTypes[1] 111 | if protoimpl.UnsafeEnabled && x != nil { 112 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 113 | if ms.LoadMessageInfo() == nil { 114 | ms.StoreMessageInfo(mi) 115 | } 116 | return ms 117 | } 118 | return mi.MessageOf(x) 119 | } 120 | 121 | // Deprecated: Use LogRequest.ProtoReflect.Descriptor instead. 122 | func (*LogRequest) Descriptor() ([]byte, []int) { 123 | return file_logs_proto_rawDescGZIP(), []int{1} 124 | } 125 | 126 | func (x *LogRequest) GetLogEntry() *Log { 127 | if x != nil { 128 | return x.LogEntry 129 | } 130 | return nil 131 | } 132 | 133 | // what we send back in response to LogRequest 134 | type LogResponse struct { 135 | state protoimpl.MessageState 136 | sizeCache protoimpl.SizeCache 137 | unknownFields protoimpl.UnknownFields 138 | 139 | Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` 140 | } 141 | 142 | func (x *LogResponse) Reset() { 143 | *x = LogResponse{} 144 | if protoimpl.UnsafeEnabled { 145 | mi := &file_logs_proto_msgTypes[2] 146 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 147 | ms.StoreMessageInfo(mi) 148 | } 149 | } 150 | 151 | func (x *LogResponse) String() string { 152 | return protoimpl.X.MessageStringOf(x) 153 | } 154 | 155 | func (*LogResponse) ProtoMessage() {} 156 | 157 | func (x *LogResponse) ProtoReflect() protoreflect.Message { 158 | mi := &file_logs_proto_msgTypes[2] 159 | if protoimpl.UnsafeEnabled && x != nil { 160 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 161 | if ms.LoadMessageInfo() == nil { 162 | ms.StoreMessageInfo(mi) 163 | } 164 | return ms 165 | } 166 | return mi.MessageOf(x) 167 | } 168 | 169 | // Deprecated: Use LogResponse.ProtoReflect.Descriptor instead. 170 | func (*LogResponse) Descriptor() ([]byte, []int) { 171 | return file_logs_proto_rawDescGZIP(), []int{2} 172 | } 173 | 174 | func (x *LogResponse) GetResult() string { 175 | if x != nil { 176 | return x.Result 177 | } 178 | return "" 179 | } 180 | 181 | var File_logs_proto protoreflect.FileDescriptor 182 | 183 | var file_logs_proto_rawDesc = []byte{ 184 | 0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x6f, 185 | 0x67, 0x73, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 186 | 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 187 | 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 188 | 0x61, 0x22, 0x33, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 189 | 0x25, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 190 | 0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x08, 0x6c, 0x6f, 191 | 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x25, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 192 | 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 193 | 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x3d, 0x0a, 194 | 0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x57, 195 | 0x72, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x12, 0x10, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 196 | 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 197 | 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0x5a, 0x05, 198 | 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 199 | } 200 | 201 | var ( 202 | file_logs_proto_rawDescOnce sync.Once 203 | file_logs_proto_rawDescData = file_logs_proto_rawDesc 204 | ) 205 | 206 | func file_logs_proto_rawDescGZIP() []byte { 207 | file_logs_proto_rawDescOnce.Do(func() { 208 | file_logs_proto_rawDescData = protoimpl.X.CompressGZIP(file_logs_proto_rawDescData) 209 | }) 210 | return file_logs_proto_rawDescData 211 | } 212 | 213 | var file_logs_proto_msgTypes = make([]protoimpl.MessageInfo, 3) 214 | var file_logs_proto_goTypes = []interface{}{ 215 | (*Log)(nil), // 0: logs.Log 216 | (*LogRequest)(nil), // 1: logs.LogRequest 217 | (*LogResponse)(nil), // 2: logs.LogResponse 218 | } 219 | var file_logs_proto_depIdxs = []int32{ 220 | 0, // 0: logs.LogRequest.logEntry:type_name -> logs.Log 221 | 1, // 1: logs.LogService.WriteLog:input_type -> logs.LogRequest 222 | 2, // 2: logs.LogService.WriteLog:output_type -> logs.LogResponse 223 | 2, // [2:3] is the sub-list for method output_type 224 | 1, // [1:2] is the sub-list for method input_type 225 | 1, // [1:1] is the sub-list for extension type_name 226 | 1, // [1:1] is the sub-list for extension extendee 227 | 0, // [0:1] is the sub-list for field type_name 228 | } 229 | 230 | func init() { file_logs_proto_init() } 231 | func file_logs_proto_init() { 232 | if File_logs_proto != nil { 233 | return 234 | } 235 | if !protoimpl.UnsafeEnabled { 236 | file_logs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 237 | switch v := v.(*Log); i { 238 | case 0: 239 | return &v.state 240 | case 1: 241 | return &v.sizeCache 242 | case 2: 243 | return &v.unknownFields 244 | default: 245 | return nil 246 | } 247 | } 248 | file_logs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 249 | switch v := v.(*LogRequest); i { 250 | case 0: 251 | return &v.state 252 | case 1: 253 | return &v.sizeCache 254 | case 2: 255 | return &v.unknownFields 256 | default: 257 | return nil 258 | } 259 | } 260 | file_logs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 261 | switch v := v.(*LogResponse); i { 262 | case 0: 263 | return &v.state 264 | case 1: 265 | return &v.sizeCache 266 | case 2: 267 | return &v.unknownFields 268 | default: 269 | return nil 270 | } 271 | } 272 | } 273 | type x struct{} 274 | out := protoimpl.TypeBuilder{ 275 | File: protoimpl.DescBuilder{ 276 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 277 | RawDescriptor: file_logs_proto_rawDesc, 278 | NumEnums: 0, 279 | NumMessages: 3, 280 | NumExtensions: 0, 281 | NumServices: 1, 282 | }, 283 | GoTypes: file_logs_proto_goTypes, 284 | DependencyIndexes: file_logs_proto_depIdxs, 285 | MessageInfos: file_logs_proto_msgTypes, 286 | }.Build() 287 | File_logs_proto = out.File 288 | file_logs_proto_rawDesc = nil 289 | file_logs_proto_goTypes = nil 290 | file_logs_proto_depIdxs = nil 291 | } 292 | -------------------------------------------------------------------------------- /logger-service/logs/logs.proto: -------------------------------------------------------------------------------- 1 | // This file is used to determine what kind of source code should be produced. 2 | 3 | // .proto file is used to define the structure and format of the data that will be exchanged between different systems or services. 4 | 5 | //Once the .proto file is defined, it can be compiled into language-specific code (e.g., Go, Java, Python) using a protocol buffer compiler. The generated code provides convenient APIs to work with the defined messages and services in your programming language of choice. The generated code provides functions and methods to easily create, read, and send data according to the structure defined in the .proto file. 6 | 7 | // In summary, a .proto file is used to define the structure of data that will be exchanged between programs. It acts as a contract or agreement between programs, ensuring they understand each other's data format. The file is used to generate code in different languages, making it easier to work with the defined data structures and enabling communication between programs written in different languages. 8 | 9 | syntax = "proto3"; 10 | 11 | package logs; 12 | 13 | option go_package = "/logs"; 14 | 15 | // kinds of information that will be passed around (information we pass everytime we sends something to logger microservice) 16 | message Log{ 17 | string name = 1; 18 | string data = 2; 19 | } 20 | 21 | // LogRequest is the request to perform a log operation 22 | message LogRequest { 23 | Log logEntry = 1; 24 | } 25 | 26 | // what we send back in response to LogRequest 27 | message LogResponse { 28 | string result = 1; 29 | 30 | } 31 | 32 | // define the services 33 | service LogService { 34 | // define the name of the function that this gRPC system is going to have 35 | rpc WriteLog(LogRequest) returns (LogResponse); 36 | 37 | } 38 | 39 | // We run this command using the Protocol Buffers compiler (protoc) to generate Go code from the logs.proto file 40 | // protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative logs.proto -------------------------------------------------------------------------------- /logger-service/logs/logs_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.2.0 4 | // - protoc v3.12.4 5 | // source: logs.proto 6 | 7 | package logs 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | // LogServiceClient is the client API for LogService service. 22 | // 23 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 24 | type LogServiceClient interface { 25 | // define the name of the function that this gRPC system is going to have 26 | WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) 27 | } 28 | 29 | type logServiceClient struct { 30 | cc grpc.ClientConnInterface 31 | } 32 | 33 | func NewLogServiceClient(cc grpc.ClientConnInterface) LogServiceClient { 34 | return &logServiceClient{cc} 35 | } 36 | 37 | func (c *logServiceClient) WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) { 38 | out := new(LogResponse) 39 | err := c.cc.Invoke(ctx, "/logs.LogService/WriteLog", in, out, opts...) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return out, nil 44 | } 45 | 46 | // LogServiceServer is the server API for LogService service. 47 | // All implementations must embed UnimplementedLogServiceServer 48 | // for forward compatibility 49 | type LogServiceServer interface { 50 | // define the name of the function that this gRPC system is going to have 51 | WriteLog(context.Context, *LogRequest) (*LogResponse, error) 52 | mustEmbedUnimplementedLogServiceServer() 53 | } 54 | 55 | // UnimplementedLogServiceServer must be embedded to have forward compatible implementations. 56 | type UnimplementedLogServiceServer struct { 57 | } 58 | 59 | func (UnimplementedLogServiceServer) WriteLog(context.Context, *LogRequest) (*LogResponse, error) { 60 | return nil, status.Errorf(codes.Unimplemented, "method WriteLog not implemented") 61 | } 62 | func (UnimplementedLogServiceServer) mustEmbedUnimplementedLogServiceServer() {} 63 | 64 | // UnsafeLogServiceServer may be embedded to opt out of forward compatibility for this service. 65 | // Use of this interface is not recommended, as added methods to LogServiceServer will 66 | // result in compilation errors. 67 | type UnsafeLogServiceServer interface { 68 | mustEmbedUnimplementedLogServiceServer() 69 | } 70 | 71 | func RegisterLogServiceServer(s grpc.ServiceRegistrar, srv LogServiceServer) { 72 | s.RegisterService(&LogService_ServiceDesc, srv) 73 | } 74 | 75 | func _LogService_WriteLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 76 | in := new(LogRequest) 77 | if err := dec(in); err != nil { 78 | return nil, err 79 | } 80 | if interceptor == nil { 81 | return srv.(LogServiceServer).WriteLog(ctx, in) 82 | } 83 | info := &grpc.UnaryServerInfo{ 84 | Server: srv, 85 | FullMethod: "/logs.LogService/WriteLog", 86 | } 87 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 88 | return srv.(LogServiceServer).WriteLog(ctx, req.(*LogRequest)) 89 | } 90 | return interceptor(ctx, in, info, handler) 91 | } 92 | 93 | // LogService_ServiceDesc is the grpc.ServiceDesc for LogService service. 94 | // It's only intended for direct use with grpc.RegisterService, 95 | // and not to be introspected or modified (even as a copy) 96 | var LogService_ServiceDesc = grpc.ServiceDesc{ 97 | ServiceName: "logs.LogService", 98 | HandlerType: (*LogServiceServer)(nil), 99 | Methods: []grpc.MethodDesc{ 100 | { 101 | MethodName: "WriteLog", 102 | Handler: _LogService_WriteLog_Handler, 103 | }, 104 | }, 105 | Streams: []grpc.StreamDesc{}, 106 | Metadata: "logs.proto", 107 | } 108 | -------------------------------------------------------------------------------- /mail-service/cmd/api/handlers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func (app *Config) SendMail(w http.ResponseWriter, r *http.Request) { 9 | type mailMessage struct { 10 | From string `json:"from"` 11 | To string `json:"to"` 12 | Subject string `json:"subject"` 13 | Message string `json:"message"` 14 | } 15 | 16 | var requestPayload mailMessage 17 | 18 | err := app.readJSON(w, r, &requestPayload) 19 | if err != nil { 20 | app.errorJSON(w, err) 21 | return 22 | } 23 | 24 | msg := Message{ 25 | From: requestPayload.From, 26 | To: requestPayload.To, 27 | Subject: requestPayload.Message, 28 | Data: requestPayload.Message, 29 | } 30 | 31 | err = app.Mailer.SendSMTPMessage(msg) 32 | if err != nil { 33 | log.Println(err) 34 | app.errorJSON(w, err) 35 | return 36 | } 37 | 38 | payload := jsonResponse{ 39 | Error: false, 40 | Message: "sent to" + requestPayload.To, 41 | } 42 | 43 | app.writeJSON(w, http.StatusAccepted, payload) 44 | 45 | } 46 | -------------------------------------------------------------------------------- /mail-service/cmd/api/helpers.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type jsonResponse struct { 11 | Error bool `json:"errorrr"` 12 | Message string `json:"message"` 13 | Data any `json:"data,omitempty"` 14 | } 15 | 16 | // function to read json 17 | func (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error { 18 | maxBytes := 1048576 // 1 MB 19 | 20 | // limiting the size of incoming request bodies 21 | r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes)) 22 | 23 | dec := json.NewDecoder(r.Body) 24 | err := dec.Decode(data) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | // decode a JSON value into an empty struct{}. 30 | err = dec.Decode(&struct{}{}) 31 | // io.EOF is an error value indicating the end of the input source has been reached. 32 | // If err is not equal to io.EOF, it means that the decoding process did not reach the end of the input source, and there are additional JSON values remaining. 33 | if err != io.EOF { 34 | return errors.New("body must have only a single json value") 35 | } 36 | 37 | return nil 38 | } 39 | 40 | // function to write json 41 | // The ellipsis (...) before http.Header indicates that it can accept a variable number of arguments, allowing you to pass or store multiple http.Header values. 42 | func (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error { 43 | out, err := json.Marshal(data) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if len(headers) > 0 { 49 | for key, value := range headers[0] { 50 | w.Header()[key] = value 51 | } 52 | } 53 | 54 | w.Header().Set("Content-Type", "application/json") 55 | w.WriteHeader(status) 56 | 57 | _, err = w.Write(out) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // function to write error message as json 66 | func (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error { 67 | statusCode := http.StatusBadRequest 68 | 69 | if len(status) > 0 { 70 | statusCode = status[0] 71 | } 72 | 73 | var payload jsonResponse 74 | payload.Error = true 75 | payload.Message = err.Error() 76 | 77 | return app.writeJSON(w, statusCode, payload) 78 | } 79 | -------------------------------------------------------------------------------- /mail-service/cmd/api/mailer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "html/template" 6 | "time" 7 | 8 | "github.com/vanng822/go-premailer/premailer" 9 | 10 | mail "github.com/xhit/go-simple-mail/v2" 11 | ) 12 | 13 | // For the mail server we'll be communicating with 14 | type Mail struct { 15 | Domain string 16 | Host string 17 | Port int 18 | Username string 19 | Password string 20 | Encryption string 21 | FromAddress string 22 | FromName string 23 | } 24 | 25 | // Describe the kind of content that we'll use for an indiviual email message 26 | type Message struct { 27 | From string 28 | FromName string 29 | To string 30 | Subject string 31 | Attachments []string 32 | Data any 33 | DataMap map[string]any 34 | } 35 | 36 | // function to send email 37 | func (m *Mail) SendSMTPMessage(msg Message) error { 38 | if msg.From == "" { 39 | msg.From = m.FromAddress 40 | } 41 | 42 | if msg.FromName == "" { 43 | msg.FromName = m.FromName 44 | } 45 | 46 | data := map[string]any{ 47 | "message": msg.Data, 48 | } 49 | 50 | msg.DataMap = data 51 | 52 | formattedMessage, err := m.buildHTMLMessage(msg) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | // We have one version of our message ,now we want the plain text version of my message 58 | plainMessage, err := m.buildPlainTextMessage(msg) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | // create our mail server 64 | server := mail.NewSMTPClient() 65 | 66 | // Describe the server 67 | server.Host = m.Host 68 | server.Port = m.Port 69 | server.Username = m.Username 70 | server.Password = m.Password 71 | server.Encryption = m.getEncryption(m.Encryption) 72 | server.KeepAlive = false 73 | server.ConnectTimeout = 10 * time.Second 74 | server.SendTimeout = 10 * time.Second 75 | 76 | // Create a client and connect to the host 77 | smtpClient, err := server.Connect() 78 | if err != nil { 79 | return err 80 | } 81 | 82 | email := mail.NewMSG() 83 | email.SetFrom(msg.From).AddTo(msg.To).SetSubject(msg.Subject) 84 | 85 | email.SetBody(mail.TextPlain, plainMessage) 86 | email.AddAlternative(mail.TextHTML, formattedMessage) 87 | 88 | if len(msg.Attachments) > 0 { 89 | for _, x := range msg.Attachments { 90 | email.AddAttachment(x) 91 | } 92 | } 93 | 94 | // send the email 95 | err = email.Send(smtpClient) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (m *Mail) buildHTMLMessage(msg Message) (string, error) { 104 | templateToRender := "./templates/mail/mail.html.gohtml" 105 | 106 | t, err := template.New("email-html").ParseFiles(templateToRender) 107 | if err != nil { 108 | return "", err 109 | } 110 | 111 | var tpl bytes.Buffer 112 | if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil { 113 | return "", err 114 | } 115 | 116 | // get the formatted msg from tpl 117 | formattedMessage := tpl.String() 118 | formattedMessage, err = m.inlineCSS(formattedMessage) 119 | if err != nil { 120 | return "", err 121 | } 122 | 123 | return formattedMessage, nil 124 | 125 | } 126 | 127 | func (m *Mail) buildPlainTextMessage(msg Message) (string, error) { 128 | templateToRender := "./templates/mail/mail.plain.gohtml" 129 | 130 | t, err := template.New("email-plain").ParseFiles(templateToRender) 131 | if err != nil { 132 | return "", err 133 | } 134 | 135 | var tpl bytes.Buffer 136 | if err = t.ExecuteTemplate(&tpl, "body", msg.DataMap); err != nil { 137 | return "", err 138 | } 139 | 140 | plainMessage := tpl.String() 141 | 142 | return plainMessage, nil 143 | 144 | } 145 | 146 | func (m *Mail) inlineCSS(s string) (string, error) { 147 | options := premailer.Options{ 148 | RemoveClasses: false, 149 | CssToAttributes: false, 150 | KeepBangImportant: true, 151 | } 152 | 153 | prem, err := premailer.NewPremailerFromString(s, &options) 154 | if err != nil { 155 | return "", err 156 | } 157 | 158 | html, err := prem.Transform() 159 | if err != nil { 160 | return "", err 161 | } 162 | 163 | return html, nil 164 | } 165 | 166 | func (m *Mail) getEncryption(s string) mail.Encryption { 167 | switch s { 168 | case "tls": 169 | return mail.EncryptionSTARTTLS 170 | case "ssl": 171 | return mail.EncryptionSSLTLS 172 | case "none", "": 173 | return mail.EncryptionNone 174 | default: 175 | return mail.EncryptionSTARTTLS 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /mail-service/cmd/api/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | type Config struct { 12 | Mailer Mail 13 | } 14 | 15 | const webPort = "80" //listen on port 80 inside of docker 16 | 17 | func main() { 18 | app := Config{ 19 | Mailer: createMail(), 20 | } 21 | 22 | log.Println("Starting mail service on port", webPort) 23 | 24 | // DEfine a server that listens on port 80 and uses our routes 25 | srv := &http.Server{ 26 | Addr: fmt.Sprintf(":%s", webPort), 27 | Handler: app.routes(), 28 | } 29 | 30 | err := srv.ListenAndServe() 31 | if err != nil { 32 | log.Panic(err) 33 | } 34 | } 35 | 36 | func createMail() Mail { 37 | port, _ := strconv.Atoi(os.Getenv("MAIL_PORT")) 38 | m := Mail{ 39 | Domain: os.Getenv("MAIL_DOMAIN"), 40 | Host: os.Getenv("MAIL_HOST"), 41 | Port: port, 42 | Username: os.Getenv("MAIL_USERNAME"), 43 | Password: os.Getenv("MAIL_PASSWORD"), 44 | Encryption: os.Getenv("MAIL_ENCRYPTION"), 45 | FromName: os.Getenv("FROM_NAME"), 46 | FromAddress: os.Getenv("FROM_ADDRESS"), 47 | } 48 | 49 | return m 50 | } 51 | -------------------------------------------------------------------------------- /mail-service/cmd/api/routes.go: -------------------------------------------------------------------------------- 1 | // servemux (also known as a router) stores a mapping between the predefined URL paths for your application and the corresponding handlers. Usually you have one servemux for your application containing all your routes. 2 | 3 | // Handlers are responsible for carrying out your application logic and writing response headers and bodies. 4 | 5 | // http.ServeMux is An HTTP request multiplexer, often called a router, is responsible for routing incoming HTTP requests to the appropriate handler functions based on the request's URL or other criteria. 6 | 7 | // http.DefaultServeMux is the default instance of http.ServeMux created by Go's net/http package. When you register your handler functions using functions like http.HandleFunc without explicitly specifying a custom http.ServeMux, they are automatically registered with http.DefaultServeMux. 8 | 9 | package main 10 | 11 | import ( 12 | "net/http" 13 | 14 | "github.com/go-chi/chi/v5" 15 | "github.com/go-chi/chi/v5/middleware" 16 | "github.com/go-chi/cors" 17 | ) 18 | 19 | func (app *Config) routes() http.Handler { 20 | mux := chi.NewRouter() 21 | 22 | // specify who is allowed to connect 23 | mux.Use(cors.Handler(cors.Options{ 24 | AllowedOrigins: []string{"https://*", "http://*"}, 25 | AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 26 | AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}, 27 | ExposedHeaders: []string{"Link"}, 28 | AllowCredentials: true, 29 | MaxAge: 300, 30 | })) 31 | 32 | mux.Use(middleware.Heartbeat("/ping")) 33 | 34 | mux.Post("/send", app.SendMail) 35 | 36 | return mux 37 | } 38 | -------------------------------------------------------------------------------- /mail-service/go.mod: -------------------------------------------------------------------------------- 1 | module mailer-service 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.5.1 // indirect 7 | github.com/andybalholm/cascadia v1.1.0 // indirect 8 | github.com/go-chi/chi/v5 v5.0.8 // indirect 9 | github.com/go-chi/cors v1.2.1 // indirect 10 | github.com/gorilla/css v1.0.0 // indirect 11 | github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect 12 | github.com/vanng822/css v1.0.1 // indirect 13 | github.com/vanng822/go-premailer v1.20.2 // indirect 14 | github.com/xhit/go-simple-mail/v2 v2.13.0 // indirect 15 | golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /mail-service/go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= 2 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= 3 | github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= 4 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= 7 | github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= 8 | github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 9 | github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= 10 | github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= 11 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 12 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 13 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 14 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 15 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM= 17 | github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= 18 | github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= 19 | github.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8= 20 | github.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= 21 | github.com/vanng822/go-premailer v1.20.2 h1:vKs4VdtfXDqL7IXC2pkiBObc1bXM9bYH3Wa+wYw2DnI= 22 | github.com/vanng822/go-premailer v1.20.2/go.mod h1:RAxbRFp6M/B171gsKu8dsyq+Y5NGsUUvYfg+WQWusbE= 23 | github.com/vanng822/r2router v0.0.0-20150523112421-1023140a4f30/go.mod h1:1BVq8p2jVr55Ost2PkZWDrG86PiJ/0lxqcXoAcGxvWU= 24 | github.com/xhit/go-simple-mail/v2 v2.13.0 h1:OANWU9jHZrVfBkNkvLf8Ww0fexwpQVF/v/5f96fFTLI= 25 | github.com/xhit/go-simple-mail/v2 v2.13.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= 26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 27 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 28 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 29 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 30 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 31 | golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= 32 | golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 33 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 34 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 35 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 36 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 37 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 38 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 39 | -------------------------------------------------------------------------------- /mail-service/mail-service.dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN mkdir /app 4 | 5 | 6 | COPY mailApp /app 7 | COPY templates /templates 8 | 9 | 10 | CMD ["/app/mailApp"] -------------------------------------------------------------------------------- /mail-service/mailApp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayushthe1/go-micro/779757cab45e468c6ef663631916047495783e51/mail-service/mailApp -------------------------------------------------------------------------------- /mail-service/templates/mail/mail.html.gohtml: -------------------------------------------------------------------------------- 1 | {{define "body"}} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

{{.message}}

13 | 14 | 15 | 16 | 17 | 18 | {{end}} -------------------------------------------------------------------------------- /mail-service/templates/mail/mail.plain.gohtml: -------------------------------------------------------------------------------- 1 | {{define "body"}} 2 | 3 | {{.message}} 4 | 5 | {{end}} -------------------------------------------------------------------------------- /project/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | email you@gmail.com 3 | } 4 | 5 | (static) { 6 | @static { 7 | file 8 | path *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.json 9 | } 10 | header @static Cache-Control max-age=5184000 11 | } 12 | 13 | (security) { 14 | header { 15 | # enable HSTS 16 | Strict-Transport-Security max-age=31536000; 17 | # disable clients from sniffing the media type 18 | X-Content-Type-Options nosniff 19 | # keep referrer data off of HTTP connections 20 | Referrer-Policy no-referrer-when-downgrade 21 | } 22 | } 23 | 24 | localhost:80 { 25 | encode zstd gzip 26 | import static 27 | 28 | reverse_proxy http://front-end:8082 29 | } 30 | 31 | # Any request to port 80 for the server named backend ,send that to broker-service 32 | backend:80 { 33 | # uses a reverse proxy to forward those requests to a backend server running inside a Docker container with the address http://broker-service:8080. 34 | reverse_proxy http://broker-service:8080 # this is address of broker-service listening inside docker 35 | } 36 | 37 | # The key difference between a reverse proxy and a forward proxy is that a forward proxy enables computers isolated on a private network to connect to the public internet, while a reverse proxy enables computers on the internet to access a private subnet. 38 | 39 | # A forward proxy accepts connections from computers on a private network and forwards those requests to the public internet. It is the single point of exit for subnet users who want to access resources outside of their private network. 40 | 41 | # The reverse proxy acts as a single point of entry for external systems to access resources on a private subnet. -------------------------------------------------------------------------------- /project/Makefile: -------------------------------------------------------------------------------- 1 | FRONT_END_BINARY=frontApp 2 | BROKER_BINARY=brokerApp 3 | AUTH_BINARY=authApp 4 | LOGGER_BINARY=loggerServiceApp 5 | MAILER_BINARY=mailApp 6 | LISTENER_BINARY=listenerApp 7 | FRONT_BINARY=frontEndApp 8 | 9 | 10 | deploy_swarm: 11 | docker swarm init 12 | @echo "Deploying project on docker swarm...." 13 | docker stack deploy -c swarm.yml myMicroApp 14 | 15 | undeploy_swarm: 16 | @echo "Leaving the docker swarm...." 17 | docker swarm leave --force 18 | 19 | ## up: starts all containers in the background without forcing build 20 | up: 21 | @echo "Starting Docker images..." 22 | docker-compose up -d 23 | @echo "Docker images started!" 24 | 25 | ## up_build: stops docker-compose (if running), builds all projects and starts docker compose 26 | up_build: build_broker build_auth build_logger build_mail build_listener ## The make targets up_build depends on will be executed first 27 | @echo "Stopping docker images (if running...)" 28 | docker-compose down 29 | @echo "Building (when required) and starting docker images..." 30 | ## This command starts the Docker containers defined in the docker-compose.yml file. The --build flag ensures that the containers are built if any changes are detected. The -d flag runs the containers in the background (detached mode). 31 | docker-compose up --build -d 32 | @echo "Docker images built and started!" 33 | 34 | ## down: stop docker compose 35 | down: 36 | @echo "Stopping docker compose..." 37 | docker-compose down 38 | @echo "Done!" 39 | 40 | ## build_broker: builds the broker binary as a linux executable 41 | build_broker: 42 | @echo "Building broker binary..." 43 | cd ../broker-service && env GOOS=linux CGO_ENABLED=0 go build -o ${BROKER_BINARY} ./cmd/api 44 | @echo "Done!" 45 | 46 | ## build_front_linux: builds the front end binary as a linux executable 47 | build_front_linux: 48 | @echo "Building front end linux binary..." 49 | cd ../front-end && env GOOS=linux CGO_ENABLED=0 go build -o ${FRONT_BINARY} ./cmd/web 50 | @echo "Done!" 51 | 52 | ## build_logger: builds the logger binary as a linux executable 53 | build_logger: 54 | @echo "Building logger binary..." 55 | cd ../logger-service && env GOOS=linux CGO_ENABLED=0 go build -o ${LOGGER_BINARY} ./cmd/api 56 | @echo "Done!" 57 | 58 | ## build_listener: builds the listener binary as a linux executable 59 | build_listener: 60 | @echo "Building listener binary..." 61 | cd ../listener-service && env GOOS=linux CGO_ENABLED=0 go build -o ${LISTENER_BINARY} . 62 | @echo "Done!" 63 | 64 | ## build_auth: builds the auth binary as a linux executable 65 | build_auth: 66 | @echo "Building auth binary..." 67 | cd ../authentication-service && env GOOS=linux CGO_ENABLED=0 go build -o ${AUTH_BINARY} ./cmd/api 68 | @echo "Done!" 69 | 70 | ## build_front: builds the frone end binary 71 | build_front: 72 | @echo "Building front end binary..." 73 | cd ../front-end && env CGO_ENABLED=0 go build -o ${FRONT_END_BINARY} ./cmd/web 74 | @echo "Done!" 75 | 76 | ## build_mailer: builds the mailer-service binary 77 | build_mail: 78 | @echo "Building mail binary..." 79 | cd ../mail-service && env CGO_ENABLED=0 go build -o ${MAILER_BINARY} ./cmd/api 80 | @echo "Done!" 81 | 82 | ## start: starts the front end 83 | start: build_front 84 | @echo "Starting front end" 85 | cd ../front-end && ./${FRONT_END_BINARY} & 86 | 87 | ## stop: stop the front end 88 | stop: 89 | @echo "Stopping front end..." 90 | @-pkill -SIGTERM -f "./${FRONT_END_BINARY}" 91 | @echo "Stopped front end!" -------------------------------------------------------------------------------- /project/caddy.dockerfile: -------------------------------------------------------------------------------- 1 | FROM caddy:2.4.6-alpine 2 | 3 | COPY Caddyfile /etc/caddy/Caddyfile 4 | -------------------------------------------------------------------------------- /project/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | 5 | broker-service: 6 | # build section specifies how to build the container image for the service. 7 | build: 8 | # specifies the build context directory,where the build process should look for files to include in the container image. 9 | context: ./../broker-service 10 | # specifies the path to the Dockerfile that will be used to build the image 11 | dockerfile: ./../broker-service/broker-service.dockerfile 12 | restart: always 13 | ports: 14 | # map port 8080 on my localhost to port 8084 on the docker image 15 | # we should be able to hit the broker service on port 8080 from my front-end (or we can hit the broker-service on localhost:8080 on postman to check its response as all traffic on localhost:8080 is forwarded to port 8084 of container broker-service) 16 | # 8084 bcoz in broker-service/main.go ,we have configured broker service to listen on port 8084.So it will listen on port 8084 inside the container. 17 | - "8080:80" 18 | # The deploy section contains configuration options for deploying the service to a swarm cluster (Docker's native orchestration and clustering solution). 19 | deploy: 20 | mode: replicated 21 | # we can only have 1 replica in this case bcoz we can't listen to 2 docker images on port 8080 on localhost 22 | replicas: 1 23 | 24 | 25 | logger-service: 26 | build: 27 | context: ./../logger-service 28 | dockerfile: ./../logger-service/logger-service.dockerfile 29 | restart: always 30 | deploy: 31 | mode: replicated 32 | replicas: 1 33 | 34 | 35 | # Add the authentication service 36 | authentication-service: 37 | build: 38 | # The context field determines the directory path where the build context resides. The build context is the set of files and directories that are sent to the Docker daemon for building the Docker image of the service. It includes the Dockerfile and any files referenced by the Dockerfile during the build process. 39 | context: ./../authentication-service 40 | dockerfile: ./../authentication-service/authentication-service.dockerfile 41 | restart: always 42 | ports: 43 | # listen on port 8081 outside of docker, and on port 80 inside of docker. 44 | # Inside docker ,we can have multiple services listen on same port 45 | - "8081:80" 46 | deploy: 47 | mode: replicated 48 | replicas: 1 49 | environment: 50 | DSN: "host=postgres port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5" 51 | 52 | # Add the mailer service 53 | mailer-service: 54 | build: 55 | context: ./../mail-service 56 | dockerfile: ./../mail-service/mail-service.dockerfile 57 | restart: always 58 | deploy: 59 | mode: replicated 60 | replicas: 1 61 | environment: 62 | MAIL_DOMAIN: localhost 63 | MAIL_HOST: mailhog 64 | MAIL_PORT: 1025 65 | MAIL_USERNAME: "" 66 | MAIL_PASSWORD: "" 67 | MAIL_ENCRYPTION: none 68 | FROM_NAME: "John Smith" 69 | FROM_ADDRESS: john.smith@example.com 70 | 71 | 72 | 73 | # Add a Postegres service 74 | postgres: 75 | image: 'postgres:14.2' 76 | ports: 77 | - "5432:5432" # map port 5444 on my local machine to 5432 on my docker container 78 | restart: always 79 | deploy: 80 | mode: replicated 81 | replicas: 1 82 | environment: 83 | POSTGRES_USER: postgres 84 | POSTGRES_PASSWORD: password 85 | POSTGRES_DB: users 86 | volumes: # volumes so that the data persists 87 | - ./db-data/postgres/:/var/lib/postgresql/data/ 88 | 89 | mongo: 90 | image: 'mongo:4.2.16-bionic' 91 | ports: 92 | - "27017:27017" 93 | environment: 94 | MONGO_INITDB_DATABASE: logs 95 | MONGO_INITDB_ROOT_USERNAME: admin 96 | MONGO_INITDB_ROOT_PASSWORD: password 97 | volumes: 98 | - ./db-data/mongo/:/data/db 99 | 100 | 101 | mailhog: 102 | image: 'mailhog/mailhog:latest' 103 | ports: 104 | - "1025:1025" 105 | - "8025:8025" 106 | 107 | # get the rabbitmq image from dockerhub 108 | rabbitmq: 109 | image: 'rabbitmq:3.9-alpine' 110 | ports: 111 | - "5672:5672" 112 | deploy: 113 | mode: replicated 114 | replicas: 1 115 | volumes: 116 | - ./db-data/rabbitmq/:/var/lib/rabbitmq/ 117 | 118 | 119 | listener-service: 120 | build: 121 | context: ./../listener-service 122 | dockerfile: ./../listener-service/listener-service.dockerfile 123 | deploy: 124 | mode: replicated 125 | replicas: 1 126 | 127 | -------------------------------------------------------------------------------- /project/ingress.yml: -------------------------------------------------------------------------------- 1 | # an Ingress is an API resource that serves as an entry point to our cluster from the external world. It allows you to expose HTTP and HTTPS routes to services running inside our Kubernetes cluster. In simple terms, Ingress enables external access to our cluster's services and provides a way to route incoming traffic to different services based on rules defined in the Ingress configuration. 2 | 3 | # After creating this file ,we have to edit the /etc/host file and add '127.0.0.1 front-end.info broker-service.info' . 4 | 5 | # By mapping front-end.info and broker-service.info to 127.0.0.1, any request made to these domain names from our local machine will be redirected back to the machine itself. In other words, any attempt to access front-end.info or broker-service.info will be directed to the local machine's network stack. /etc/hosts file acts as a local DNS resolver, and by adding these entries, you are effectively bypassing any public DNS resolution for these domain names 6 | 7 | apiVersion: networking.k8s.io/v1 8 | kind: Ingress 9 | metadata: 10 | name: my-ingress 11 | annotations: 12 | nginx.ingress.kubernetes.io/rewrite-target: /$1 13 | spec: 14 | rules: 15 | - host: front-end.info 16 | http: 17 | paths: 18 | - path: / 19 | pathType: Prefix 20 | backend: 21 | service: 22 | name: front-end # name of the pod to route to 23 | port: 24 | number: 8082 25 | - host: broker-service.info 26 | http: 27 | paths: 28 | - path: /(.*) 29 | pathType: Prefix 30 | backend: 31 | service: 32 | name: broker-service 33 | port: 34 | number: 8080 35 | -------------------------------------------------------------------------------- /project/k8s/authentication.yml: -------------------------------------------------------------------------------- 1 | # This YAML file can be used with the Kubernetes kubectl command-line tool to create the Deployment and Service in a Kubernetes cluster. Once applied, Kubernetes will ensure that the specified number of replicas (in this case, one) of the containerized application (specified by the Docker image) are running, and a stable endpoint is provided through the Service to access these replicas. The Service acts as a load balancer, distributing traffic among the pods managed by the Deployment, making the application highly available and scalable. 2 | # The host.minikube.internal hostname is used to access services running inside the Minikube cluster from your host machine. 3 | 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | name: authentication-service 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: authentication-service 13 | template: 14 | metadata: 15 | labels: 16 | app: authentication-service 17 | spec: 18 | containers: 19 | - name: authentication-service 20 | image: "ayushthe1/authentication-service:2.0.0" 21 | resources: 22 | requests: 23 | memory: "64Mi" 24 | cpu: "250m" 25 | limits: 26 | memory: "128Mi" 27 | cpu: "500m" 28 | env: 29 | - name: DSN 30 | value: "host=host.minikube.internal port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5" 31 | ports: 32 | - containerPort: 80 #This indicates that the container listens on port 80 33 | 34 | --- 35 | 36 | # A Service in Kubernetes provides a stable endpoint to access the pods managed by the Deployment. 37 | 38 | apiVersion: v1 39 | kind: Service 40 | metadata: 41 | name: authentication-service 42 | # The spec section defines the desired state for the Service 43 | spec: 44 | # The selector allows the Service to determine which pods to target. 45 | selector: 46 | # The Service will target pods with the label "app: authentication-service" (matching the Deployment's selector) 47 | app: authentication-service 48 | ports: 49 | - protocol: TCP 50 | name: main-port 51 | port: 80 # port number on which the Service should listen internally. It represents the port on which the Service is exposed internally within the cluster. 52 | targetPort: 80 # port number on which the backend Pod is listening. It represents the port on which the actual application or service is running inside the Pod. When a request is sent to the Service, it will forward the traffic to the backend Pods on this targetPort. 53 | 54 | 55 | # The `minikube tunnel` command creates a network tunnel to expose services of type LoadBalancer to your local machine. -------------------------------------------------------------------------------- /project/k8s/broker.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: broker-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: broker-service 10 | template: 11 | metadata: 12 | labels: 13 | app: broker-service 14 | spec: 15 | containers: 16 | - name: broker-service 17 | image: 'ayushthe1/broker-service:1.0.1' 18 | resources: 19 | limits: 20 | memory: "128Mi" 21 | cpu: "500m" 22 | ports: 23 | - containerPort: 8080 # This indicates that the container listens on port 8080. 24 | 25 | --- 26 | 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | name: broker-service 31 | spec: 32 | selector: 33 | app: broker-service 34 | ports: 35 | - protocol: TCP 36 | name: main-port 37 | port: 8080 # The port number exposed by the Service 38 | targetPort: 8080 # The port number to which traffic should be forwarded to the pods. 39 | 40 | -------------------------------------------------------------------------------- /project/k8s/front-end.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: front-end 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: front-end 10 | template: 11 | metadata: 12 | labels: 13 | app: front-end 14 | spec: 15 | containers: 16 | - name: front-end 17 | image: "ayushthe1/front-end:1.1.2" 18 | resources: 19 | requests: 20 | memory: "64Mi" 21 | cpu: "250m" 22 | limits: 23 | memory: "128Mi" 24 | cpu: "500m" 25 | env: 26 | - name: BROKER_URL 27 | value: "http://broker-service.info" 28 | ports: 29 | - containerPort: 8082 30 | 31 | --- 32 | 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: front-end 37 | spec: 38 | selector: 39 | app: front-end 40 | ports: 41 | - protocol: TCP 42 | name: main-port 43 | port: 8082 44 | targetPort: 8082 -------------------------------------------------------------------------------- /project/k8s/listener.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: listener-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: listener-service 10 | template: 11 | metadata: 12 | labels: 13 | app: listener-service 14 | spec: 15 | containers: 16 | - name: listener-service 17 | image: "ayushthe1/listener-service:1.0.0" 18 | resources: 19 | requests: 20 | memory: "64Mi" 21 | cpu: "250m" 22 | limits: 23 | memory: "128Mi" 24 | cpu: "500m" 25 | ports: 26 | - containerPort: 80 27 | 28 | --- 29 | 30 | apiVersion: v1 31 | kind: Service 32 | metadata: 33 | name: listener-service 34 | spec: 35 | selector: 36 | app: listener-service 37 | ports: 38 | - protocol: TCP 39 | name: web-port 40 | port: 80 41 | targetPort: 80 -------------------------------------------------------------------------------- /project/k8s/logger.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: logger-service 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: logger-service 10 | template: 11 | metadata: 12 | labels: 13 | app: logger-service 14 | spec: 15 | containers: 16 | - name: logger-service 17 | image: "ayushthe1/logger-service:1.0.0" 18 | resources: 19 | requests: 20 | memory: "64Mi" 21 | cpu: "250m" 22 | limits: 23 | memory: "128Mi" 24 | cpu: "500m" 25 | ports: 26 | - containerPort: 80 # port we're posting to using JSON 27 | - containerPort: 5001 # port for RPC 28 | - containerPort: 50001 # port for GRPC 29 | 30 | --- 31 | 32 | apiVersion: v1 33 | kind: Service 34 | metadata: 35 | name: logger-service 36 | spec: 37 | selector: 38 | app: logger-service 39 | ports: 40 | - protocol: TCP 41 | name: web-port 42 | port: 80 43 | targetPort: 80 44 | - protocol: TCP 45 | name: rpc-port 46 | port: 5001 47 | targetPort: 5001 48 | - protocol: TCP 49 | name: grpc-port 50 | port: 50001 51 | targetPort: 50001 -------------------------------------------------------------------------------- /project/k8s/mail.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mailer-service # we use this as mailServiceURL in broker-service/handlers.go 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: mailer-service 9 | template: 10 | metadata: 11 | labels: 12 | app: mailer-service 13 | spec: 14 | containers: 15 | - name: mailer-service 16 | image: 'ayushthe1/mail-service:1.0.0' 17 | resources: 18 | limits: 19 | memory: "128Mi" 20 | cpu: "500m" 21 | env: 22 | - name: MAIL_DOMAIN 23 | value: "" 24 | - name: MAIL_HOST 25 | value: "localhost" 26 | - name: MAIL_PORT 27 | value: "1025" 28 | - name: MAIL_ENCRYPTION 29 | value: "none" 30 | - name: MAIL_USERNAME 31 | value: "" 32 | - name: MAIL_PASSWORD 33 | value: "" 34 | - name: FROM_NAME 35 | value: "John Smith" 36 | - name: FROM_ADDRESS 37 | value: "admin@example.com" 38 | ports: 39 | - containerPort: 80 40 | 41 | --- 42 | 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | name: mailer-service 47 | spec: 48 | selector: 49 | app: mailer-service 50 | ports: 51 | - protocol: TCP 52 | name: main-port 53 | port: 80 54 | targetPort: 80 55 | 56 | -------------------------------------------------------------------------------- /project/k8s/mailhog.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mailhog 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: mailhog 9 | template: 10 | metadata: 11 | labels: 12 | app: mailhog 13 | spec: 14 | containers: 15 | - name: mailhog 16 | image: 'mailhog/mailhog:latest' 17 | resources: 18 | limits: 19 | memory: "128Mi" 20 | cpu: "500m" 21 | ports: 22 | - containerPort: 1025 23 | - containerPort: 8025 24 | 25 | --- 26 | 27 | apiVersion: v1 28 | kind: Service 29 | metadata: 30 | name: mailhog 31 | spec: 32 | selector: 33 | app: mailhog 34 | ports: 35 | - protocol: TCP 36 | name: smtp-port 37 | port: 1025 38 | targetPort: 1025 39 | - protocol: TCP 40 | name: web-port # port for the web interface 41 | port: 8025 42 | targetPort: 8025 43 | 44 | -------------------------------------------------------------------------------- /project/k8s/mongo.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mongo 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: mongo 10 | template: 11 | metadata: 12 | labels: 13 | app: mongo 14 | spec: 15 | containers: 16 | - name: mongo 17 | image: "mongo:4.2.17-bionic" 18 | env: 19 | - name: MONGO_INITDB_DATABASE 20 | value: 'logs' 21 | - name: MONGO_INITDB_ROOT_USERNAME 22 | value: "admin" 23 | - name: MONGO_INITDB_ROOT_PASSWORD 24 | value: "password" 25 | ports: 26 | - containerPort: 27017 27 | 28 | --- 29 | # Define the service that's associated with this deployment 30 | 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: mongo 35 | spec: 36 | selector: 37 | app: mongo # This line specifies the label name: mongo, which matches the label used in the Deployment's selector. 38 | ports: 39 | - protocol: TCP 40 | name: main-port 41 | port: 27017 42 | targetPort: 27017 -------------------------------------------------------------------------------- /project/k8s/rabbit.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: rabbitmq 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: rabbitmq 10 | template: 11 | metadata: 12 | labels: 13 | app: rabbitmq 14 | spec: 15 | containers: 16 | - name: rabbitmq 17 | image: "rabbitmq:3.9-alpine" 18 | ports: 19 | - containerPort: 5672 20 | 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | # It's important to use rabbitmq here bcoz thats how we're referring to it in the actual code for our microservice. So we have to use the same name here that we used in the URLs we specified when we connect to RabbitMQ in our go code. 26 | name: rabbitmq 27 | spec: 28 | selector: 29 | app: rabbitmq 30 | ports: 31 | - protocol: TCP 32 | name: main-port 33 | port: 5672 34 | targetPort: 5672 35 | 36 | 37 | -------------------------------------------------------------------------------- /project/postgres.yml: -------------------------------------------------------------------------------- 1 | # This is a docker compose file .We will setup postgres outside of our cluster and not with other microservice.. 2 | 3 | # This Docker Compose file can be used with the docker-compose command-line tool to manage the PostgreSQL database service and its dependencies. By running docker-compose up, it will create and start the Postgres container with the specified configurations. The data will be persisted on the host machine due to the volume mount, ensuring that the data is available across container restarts and even if the entire application stack is removed and redeployed. 4 | 5 | version: '3' 6 | 7 | services: 8 | # Add a Postegres service 9 | postgres: 10 | image: 'postgres:14.2' 11 | ports: 12 | - "5432:5432" # map port 5432 on my local machine to 5432 on my docker container 13 | restart: always 14 | deploy: 15 | mode: replicated 16 | replicas: 1 17 | environment: 18 | POSTGRES_USER: postgres 19 | POSTGRES_PASSWORD: password 20 | POSTGRES_DB: users 21 | volumes: # volumes so that the data persists 22 | - ./db-data/postgres/:/var/lib/postgresql/data/ 23 | 24 | 25 | -------------------------------------------------------------------------------- /project/swarm.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | # We should name the services the same as we did in our docker-compose file, so that we don't have to change the urls we call for each of those services 5 | 6 | caddy: 7 | image: ayushthe1/micro-caddy:1.0.0 8 | deploy: 9 | mode: replicated 10 | replicas: 1 11 | ports: 12 | - '80:80' 13 | - '443:443' 14 | volumes: 15 | - caddy_data:/data 16 | - caddy_config:/config 17 | 18 | front-end: 19 | image: ayushthe1/front-end:1.1.2 20 | deploy: 21 | mode: replicated 22 | replicas: 1 23 | environment: 24 | # This evironment 25 | BROKER_URL: "http://backend" 26 | 27 | broker-service: 28 | image: ayushthe1/broker-service:1.0.1 29 | # ports: 30 | # # map port 8080 on my machine to port 80 in docker 31 | # - "8080:80" 32 | deploy: 33 | mode: replicated 34 | replicas: 1 35 | 36 | listener-service: 37 | image: ayushthe1/listener-service:1.0.0 38 | deploy: 39 | mode: replicated 40 | replicas: 1 41 | 42 | authentication-service: 43 | image: ayushthe1/authentication-service:1.0.0 44 | deploy: 45 | mode: replicated 46 | replicas: 1 47 | # add environment variables 48 | environment: 49 | DSN: "host=postgres port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5" 50 | 51 | logger-service: 52 | image: ayushthe1/logger-service:1.0.0 53 | deploy: 54 | mode: replicated 55 | replicas: 1 56 | 57 | mailer-service: 58 | image: ayushthe1/mail-service:1.0.0 59 | deploy: 60 | mode: replicated 61 | replicas: 1 62 | environment: 63 | MAIL_DOMAIN: localhost 64 | MAIL_HOST: mailhog 65 | MAIL_PORT: 1025 66 | MAIL_USERNAME: "" 67 | MAIL_PASSWORD: "" 68 | MAIL_ENCRYPTION: none 69 | FROM_NAME: "John Smith" 70 | FROM_ADDRESS: john.smith@example.com 71 | 72 | 73 | rabbitmq: 74 | image: 'rabbitmq:3.9-alpine' 75 | deploy: 76 | # global means keep one instance of this service running on every node of the swarm 77 | mode: global 78 | 79 | 80 | 81 | mailhog: 82 | image: 'mailhog/mailhog:latest' 83 | ports: 84 | - '8025:8025' 85 | deploy: 86 | mode: global # we only want 1 instance of mailhog to be running 87 | 88 | mongo: 89 | image: 'mongo:4.2.17-bionic' 90 | ports: 91 | - '27017:27017' 92 | deploy: 93 | mode: global 94 | environment: 95 | MONGO_INITDB_DATABASE: logs 96 | MONGO_INITDB_ROOT_USERNAME: admin 97 | MONGO_INITDB_ROOT_PASSWORD: password 98 | volumes: 99 | # When the image comes up ,map the local directory in the db-data to the appropriate directory in the docker image 100 | - ./db-data/mongo/:/data/db 101 | 102 | 103 | postgres: 104 | image: 'postgres:14.2' 105 | ports: 106 | - "5432:5432" # map port 5444 on my local machine to 5432 on my docker container 107 | restart: always 108 | deploy: 109 | mode: replicated 110 | replicas: 1 111 | environment: 112 | POSTGRES_USER: postgres 113 | POSTGRES_PASSWORD: password 114 | POSTGRES_DB: users 115 | volumes: # volumes so that the data persists 116 | - ./db-data/postgres/:/var/lib/postgresql/data/ 117 | 118 | # This is telling the swarm where to find the data 119 | volumes: 120 | caddy_data: 121 | external: true 122 | caddy_config: 123 | --------------------------------------------------------------------------------