Compare commits
6 Commits
24537681ed
...
fbfa723289
Author | SHA1 | Date | |
---|---|---|---|
fbfa723289 | |||
e0bc064ee0 | |||
0bba3cd7d2 | |||
d670e87908 | |||
523f7f8280 | |||
46a36d0ab9 |
@ -14,7 +14,7 @@ import (
|
||||
_ "github.com/jackc/pgx/v4/stdlib"
|
||||
)
|
||||
|
||||
const webPort = "4000"
|
||||
const webPort = "80"
|
||||
|
||||
var counts int64
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
type RequestPayload struct {
|
||||
Action string `string:"action"`
|
||||
Auth AuthPayload `json:"auth,omitempty"`
|
||||
Log LogPayload `json:"log,omitempty"`
|
||||
}
|
||||
|
||||
type AuthPayload struct {
|
||||
@ -18,6 +19,11 @@ type AuthPayload struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type LogPayload struct {
|
||||
Name string `json:"name"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
func (app *Config) Broker(w http.ResponseWriter, r *http.Request) {
|
||||
payload := jsonResponse{
|
||||
Error: false,
|
||||
@ -39,14 +45,54 @@ func (app *Config) HandleSubmission(w http.ResponseWriter, r *http.Request) {
|
||||
switch requestPayload.Action {
|
||||
case "auth":
|
||||
app.authenticate(w, requestPayload.Auth)
|
||||
case "log":
|
||||
app.LogItem(w, requestPayload.Log)
|
||||
default:
|
||||
app.errorJSON(w, errors.New("unknown action"))
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) {
|
||||
// create some json we'll send to the auth microservice
|
||||
authPayload, err := json.MarshalIndent(a, "", "\t")
|
||||
authService := Microservice{
|
||||
Input: a,
|
||||
Addr: "http://authentication-service/authenticate",
|
||||
ErrCode: statusError{
|
||||
Code: http.StatusUnauthorized,
|
||||
Err: errors.New("invalid credentials"),
|
||||
},
|
||||
SuccessMsg: "Authenticated",
|
||||
}
|
||||
app.callService(w, authService)
|
||||
}
|
||||
|
||||
func (app *Config) LogItem(w http.ResponseWriter, entry LogPayload) {
|
||||
loggerService := Microservice{
|
||||
Input: entry,
|
||||
Addr: "http://logger-service/log",
|
||||
ErrCode: statusError{
|
||||
Code: http.StatusInternalServerError,
|
||||
Err: errors.New("internal error"),
|
||||
},
|
||||
SuccessMsg: "Logged!",
|
||||
}
|
||||
app.callService(w, loggerService)
|
||||
}
|
||||
|
||||
type statusError struct {
|
||||
Code int
|
||||
Err error
|
||||
}
|
||||
|
||||
type Microservice struct {
|
||||
Input any
|
||||
Addr string
|
||||
ErrCode statusError
|
||||
SuccessMsg string
|
||||
}
|
||||
|
||||
func (app *Config) callService(w http.ResponseWriter, ms Microservice) {
|
||||
// create some json we'll send to the microservice
|
||||
inputPayload, err := json.MarshalIndent(ms.Input, "", "\t")
|
||||
if err != nil {
|
||||
app.errorJSON(w, err, http.StatusBadRequest)
|
||||
return
|
||||
@ -55,8 +101,8 @@ func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) {
|
||||
// call the service
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
"http://authentication-service:4000/authenticate",
|
||||
bytes.NewBuffer(authPayload),
|
||||
ms.Addr,
|
||||
bytes.NewBuffer(inputPayload),
|
||||
)
|
||||
if err != nil {
|
||||
app.errorJSON(w, err, http.StatusBadRequest)
|
||||
@ -75,7 +121,7 @@ func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) {
|
||||
|
||||
// make sure we get back the correct status code
|
||||
if resp.StatusCode != http.StatusAccepted {
|
||||
app.errorJSON(w, errors.New("invalid credentials"))
|
||||
app.errorJSON(w, ms.ErrCode.Err, ms.ErrCode.Code)
|
||||
return
|
||||
}
|
||||
|
||||
@ -89,13 +135,13 @@ func (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) {
|
||||
}
|
||||
|
||||
if respPayload.Error {
|
||||
app.errorJSON(w, errors.New(respPayload.Message), http.StatusUnauthorized)
|
||||
app.errorJSON(w, errors.New(respPayload.Message))
|
||||
return
|
||||
}
|
||||
|
||||
var payload jsonResponse
|
||||
payload.Error = false
|
||||
payload.Message = "Authenticated!"
|
||||
payload.Message = ms.SuccessMsg
|
||||
payload.Data = respPayload.Data
|
||||
|
||||
app.writeJSON(w, http.StatusOK, payload)
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const webPort = "4000"
|
||||
const webPort = "80"
|
||||
|
||||
type Config struct{}
|
||||
|
||||
|
40
logger-service/cmd/api/handlers.go
Normal file
40
logger-service/cmd/api/handlers.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"logger/data"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type JSONPayload struct {
|
||||
Name string `json:"name"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
func (app *Config) WriteLog(w http.ResponseWriter, r *http.Request) {
|
||||
// read json into var
|
||||
var requestPayload JSONPayload
|
||||
err := app.readJSON(w, r, &requestPayload)
|
||||
if err != nil {
|
||||
app.errorJSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// insert data
|
||||
event := data.LogEntry{
|
||||
Name: requestPayload.Name,
|
||||
Data: requestPayload.Data,
|
||||
}
|
||||
|
||||
err = app.Models.LogEntry.Insert(event)
|
||||
if err != nil {
|
||||
app.errorJSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := jsonResponse{
|
||||
Error: false,
|
||||
Message: "logged",
|
||||
}
|
||||
|
||||
app.writeJSON(w, http.StatusAccepted, resp)
|
||||
}
|
75
logger-service/cmd/api/helpers.go
Normal file
75
logger-service/cmd/api/helpers.go
Normal file
@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type jsonResponse struct {
|
||||
Error bool `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error {
|
||||
maxBytes := 1048576 // one megabyte
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))
|
||||
|
||||
dec := json.NewDecoder(r.Body)
|
||||
|
||||
err := dec.Decode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = dec.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
return errors.New("body must have only a single JSON value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *Config) writeJSON(
|
||||
w http.ResponseWriter,
|
||||
status int,
|
||||
data any,
|
||||
headers ...http.Header,
|
||||
) error {
|
||||
out, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(headers) > 0 {
|
||||
for key, value := range headers[0] {
|
||||
w.Header()[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_, err = w.Write(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error {
|
||||
statusCode := http.StatusBadRequest
|
||||
|
||||
if len(status) > 0 {
|
||||
statusCode = status[0]
|
||||
}
|
||||
|
||||
var payload jsonResponse
|
||||
payload.Error = true
|
||||
payload.Message = err.Error()
|
||||
|
||||
return app.writeJSON(w, statusCode, payload)
|
||||
}
|
@ -2,7 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"logger/data"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
@ -18,7 +21,9 @@ const (
|
||||
|
||||
var client *mongo.Client
|
||||
|
||||
type Config struct{}
|
||||
type Config struct {
|
||||
Models data.Models
|
||||
}
|
||||
|
||||
func main() {
|
||||
// connect to mongo
|
||||
@ -37,7 +42,24 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
client = &mongo.Client{}
|
||||
app := Config{
|
||||
Models: data.New(client),
|
||||
}
|
||||
|
||||
// start web server
|
||||
log.Println("Starting service on port", webPort)
|
||||
app.serve()
|
||||
}
|
||||
|
||||
func (app *Config) serve() {
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%s", webPort),
|
||||
Handler: app.routes(),
|
||||
}
|
||||
err := srv.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func connectToMongo() (*mongo.Client, error) {
|
||||
@ -53,5 +75,7 @@ func connectToMongo() (*mongo.Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Println("Connected to Mongo")
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
29
logger-service/cmd/api/routes.go
Normal file
29
logger-service/cmd/api/routes.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
func (app *Config) routes() http.Handler {
|
||||
mux := chi.NewRouter()
|
||||
|
||||
// specify who is allowed to connect
|
||||
mux.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"https://*", "http://*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
ExposedHeaders: []string{"Link"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
|
||||
mux.Use(middleware.Heartbeat("/ping"))
|
||||
|
||||
mux.Post("/log", app.WriteLog)
|
||||
|
||||
return mux
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
@ -76,3 +77,62 @@ func (l *LogEntry) All() ([]*LogEntry, error) {
|
||||
}
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (l *LogEntry) GetOne(id string) (*LogEntry, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
collection := client.Database("logs").Collection("logs")
|
||||
|
||||
docID, err := primitive.ObjectIDFromHex(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entry LogEntry
|
||||
err = collection.FindOne(ctx, bson.M{"_id": docID}).Decode(&entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
func (l *LogEntry) DropCollection() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
collection := client.Database("logs").Collection("logs")
|
||||
|
||||
if err := collection.Drop(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LogEntry) Update() (*mongo.UpdateResult, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
collection := client.Database("logs").Collection("logs")
|
||||
|
||||
docID, err := primitive.ObjectIDFromHex(l.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := collection.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"_id": docID},
|
||||
bson.D{
|
||||
{Key: "$set", Value: bson.D{
|
||||
{Key: "name", Value: l.Name},
|
||||
{Key: "data", Value: l.Data},
|
||||
{Key: "updated_at", Value: time.Now()},
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@ -2,7 +2,11 @@ module logger
|
||||
|
||||
go 1.22.5
|
||||
|
||||
require go.mongodb.org/mongo-driver v1.16.1
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.1.0
|
||||
github.com/go-chi/cors v1.2.1
|
||||
go.mongodb.org/mongo-driver v1.16.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
|
@ -1,5 +1,9 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
|
7
logger-service/logger-service.dockerfile
Normal file
7
logger-service/logger-service.dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM alpine:latest
|
||||
|
||||
RUN mkdir /app
|
||||
|
||||
COPY loggerApp /app
|
||||
|
||||
CMD ["/app/loggerApp"]
|
@ -1,6 +1,7 @@
|
||||
FRONT_END_BINARY=frontApp
|
||||
BROKER_BINARY=brokerApp
|
||||
AUTH_BINARY=authApp
|
||||
LOGGER_BINARY=loggerApp
|
||||
|
||||
## up: starts all containers in the background without forcing build
|
||||
up:
|
||||
@ -9,7 +10,7 @@ up:
|
||||
@echo "Docker images started!"
|
||||
|
||||
## up_build: stops docker-compose (if running), builds all projects and starts docker compose
|
||||
up_build: build_broker build_auth
|
||||
up_build: build_broker build_auth build_logger
|
||||
@echo "Stopping docker images (if running...)"
|
||||
docker compose down
|
||||
@echo "Building (when required) and starting docker images..."
|
||||
@ -34,6 +35,12 @@ build_auth:
|
||||
cd ../authentication-service && env GOOS=linux CGO_ENABLED=0 go build -o ${AUTH_BINARY} ./cmd/api
|
||||
@echo "Done!"
|
||||
|
||||
## build_logger: builds the logger binary as a linux executable
|
||||
build_logger:
|
||||
@echo "Building logger binary..."
|
||||
cd ../logger-service && env GOOS=linux CGO_ENABLED=0 go build -o ${LOGGER_BINARY} ./cmd/api
|
||||
@echo "Done!"
|
||||
|
||||
## build_front: builds the frone end binary
|
||||
build_front:
|
||||
@echo "Building front end binary..."
|
||||
|
@ -5,7 +5,7 @@ services:
|
||||
dockerfile: ./../broker-service/broker-service.dockerfile
|
||||
restart: always
|
||||
ports:
|
||||
- "8080:4000"
|
||||
- "8080:80"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
@ -15,13 +15,22 @@ services:
|
||||
context: ./../authentication-service/
|
||||
dockerfile: ./../authentication-service/authentication-service.dockerfile
|
||||
ports:
|
||||
- "8081:4000"
|
||||
- "8081:80"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
environment:
|
||||
DSN: "host=postgres port=5432 user=postgres password=password dbname=users timezone=UTC connect_timeout=5"
|
||||
|
||||
logger-service:
|
||||
build:
|
||||
context: ./../logger-service/
|
||||
dockerfile: ./../logger-service/logger-service.dockerfile
|
||||
restart: always
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
postgres:
|
||||
image: postgres
|
||||
ports:
|
||||
@ -37,6 +46,17 @@ services:
|
||||
volumes:
|
||||
- ./db-data/postgres:/var/lib/postgresql/data
|
||||
|
||||
mongo:
|
||||
image: mongo
|
||||
ports:
|
||||
- "27017:27017"
|
||||
environment:
|
||||
MONGO_INITDB_DATABASE: logs
|
||||
MONGO_INITDB_USERNAME: admin
|
||||
MONGO_INITDB_PASSWORD: password
|
||||
volumes:
|
||||
- ./db-data/mongo:/data/db
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: always
|
||||
|
Loading…
x
Reference in New Issue
Block a user