Compare commits

...

6 Commits

Author SHA1 Message Date
fbfa723289 update broker to use logger service 2024-08-29 14:28:30 +02:00
e0bc064ee0 Add logger into the chain 2024-08-29 13:57:16 +02:00
0bba3cd7d2 change 4000 to 80 2024-08-29 13:51:52 +02:00
d670e87908 Add mongo to docker and test logger 2024-08-29 13:48:47 +02:00
523f7f8280 finish logger-service 2024-08-29 13:24:36 +02:00
46a36d0ab9 Finishing up the logger data models 2024-08-29 13:12:38 +02:00
13 changed files with 332 additions and 16 deletions

View File

@ -14,7 +14,7 @@ import (
_ "github.com/jackc/pgx/v4/stdlib"
)
const webPort = "4000"
const webPort = "80"
var counts int64

View File

@ -10,7 +10,8 @@ import (
type RequestPayload struct {
Action string `string:"action"`
Auth AuthPayload ` json:"auth,omitempty"`
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)

View File

@ -6,7 +6,7 @@ import (
"net/http"
)
const webPort = "4000"
const webPort = "80"
type Config struct{}

View 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)
}

View 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)
}

View File

@ -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
}

View 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
}

View File

@ -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
}

View File

@ -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

View File

@ -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=

View File

@ -0,0 +1,7 @@
FROM alpine:latest
RUN mkdir /app
COPY loggerApp /app
CMD ["/app/loggerApp"]

View File

@ -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..."

View File

@ -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