From 0392ac49fb2b93932f73cfc210cdb9b91c3e5b31 Mon Sep 17 00:00:00 2001 From: Thomas Peetz Date: Sat, 3 Jan 2026 02:53:50 +0100 Subject: [PATCH] add project kontor-api-echo --- docker-compose.yml | 38 ++++- {kontor-api-go => kontor-api-echo}/Dockerfile | 0 kontor-api-echo/cmd/kontor/main.go | 52 +++++++ kontor-api-echo/go.mod | 39 +++++ kontor-api-echo/go.sum | 68 +++++++++ kontor-api-echo/pkg/handler/auth.go | 66 +++++++++ kontor-api-echo/pkg/handler/comics.go | 78 ++++++++++ kontor-api-echo/pkg/handler/health.go | 17 +++ kontor-api-echo/pkg/handler/media.go | 67 +++++++++ .../pkg/schema/auth.go | 0 .../pkg/schema/comics.go | 0 .../pkg/schema/comics_test.go | 0 .../pkg/schema/database.go | 0 .../pkg/schema/media.go | 0 .../pkg/utils/auth.go | 0 .../pkg/utils/token.go | 2 +- kontor-api-fiber/Dockerfile | 28 ++++ .../cmd/kontor/main.go | 6 +- {kontor-api-go => kontor-api-fiber}/go.mod | 2 +- {kontor-api-go => kontor-api-fiber}/go.sum | 0 .../pkg/handler/auth.go | 4 +- .../pkg/handler/comics.go | 2 +- .../pkg/handler/health.go | 0 .../pkg/handler/media.go | 2 +- kontor-api-fiber/pkg/schema/auth.go | 62 ++++++++ kontor-api-fiber/pkg/schema/comics.go | 140 ++++++++++++++++++ kontor-api-fiber/pkg/schema/comics_test.go | 101 +++++++++++++ kontor-api-fiber/pkg/schema/database.go | 50 +++++++ kontor-api-fiber/pkg/schema/media.go | 79 ++++++++++ kontor-api-fiber/pkg/utils/auth.go | 8 + kontor-api-fiber/pkg/utils/token.go | 38 +++++ kontor-api/Dockerfile | 2 +- kontor-api/src/main.py | 24 ++- 33 files changed, 949 insertions(+), 26 deletions(-) rename {kontor-api-go => kontor-api-echo}/Dockerfile (100%) create mode 100644 kontor-api-echo/cmd/kontor/main.go create mode 100644 kontor-api-echo/go.mod create mode 100644 kontor-api-echo/go.sum create mode 100644 kontor-api-echo/pkg/handler/auth.go create mode 100644 kontor-api-echo/pkg/handler/comics.go create mode 100644 kontor-api-echo/pkg/handler/health.go create mode 100644 kontor-api-echo/pkg/handler/media.go rename {kontor-api-go => kontor-api-echo}/pkg/schema/auth.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/schema/comics.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/schema/comics_test.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/schema/database.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/schema/media.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/utils/auth.go (100%) rename {kontor-api-go => kontor-api-echo}/pkg/utils/token.go (96%) create mode 100644 kontor-api-fiber/Dockerfile rename {kontor-api-go => kontor-api-fiber}/cmd/kontor/main.go (90%) rename {kontor-api-go => kontor-api-fiber}/go.mod (98%) rename {kontor-api-go => kontor-api-fiber}/go.sum (100%) rename {kontor-api-go => kontor-api-fiber}/pkg/handler/auth.go (94%) rename {kontor-api-go => kontor-api-fiber}/pkg/handler/comics.go (98%) rename {kontor-api-go => kontor-api-fiber}/pkg/handler/health.go (100%) rename {kontor-api-go => kontor-api-fiber}/pkg/handler/media.go (97%) create mode 100644 kontor-api-fiber/pkg/schema/auth.go create mode 100644 kontor-api-fiber/pkg/schema/comics.go create mode 100644 kontor-api-fiber/pkg/schema/comics_test.go create mode 100644 kontor-api-fiber/pkg/schema/database.go create mode 100644 kontor-api-fiber/pkg/schema/media.go create mode 100644 kontor-api-fiber/pkg/utils/auth.go create mode 100644 kontor-api-fiber/pkg/utils/token.go diff --git a/docker-compose.yml b/docker-compose.yml index 3fc8ad8..0942861 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,7 +42,7 @@ services: image: kontor-api:0.2.0-SNAPSHOT restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-f", "http://kontor-api:8800/health"] + test: ["CMD", "curl", "-f", "http://kontor-api:8500/health"] interval: 10s timeout: 5s retries: 3 @@ -51,22 +51,22 @@ services: - integration - frontend ports: - - 8800:8800 + - 8500:8500 volumes: - images-data:/data/images depends_on: postgres: condition: service_healthy - kontor-api-go: + kontor-api-fiber: build: - context: ./kontor-api-go + context: ./kontor-api-fiber dockerfile: Dockerfile tags: - - kontor-api-go:0.2.0-SNAPSHOT - image: kontor-api-go:0.2.0-SNAPSHOT + - kontor-api-fiber:0.2.0-SNAPSHOT + image: kontor-api-fiber:0.2.0-SNAPSHOT restart: unless-stopped healthcheck: - test: ["CMD", "curl", "-f", "http://kontor-api-go:8900/health"] + test: ["CMD", "curl", "-f", "http://kontor-api-fiber:8600/health"] interval: 10s timeout: 5s retries: 3 @@ -75,7 +75,29 @@ services: - integration - frontend ports: - - 8900:8900 + - 8600:8600 + depends_on: + postgres: + condition: service_healthy + kontor-api-echo: + build: + context: ./kontor-api-echo + dockerfile: Dockerfile + tags: + - kontor-api-echo:0.2.0-SNAPSHOT + image: kontor-api-echo:0.2.0-SNAPSHOT + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://kontor-api-echo:8700/health"] + interval: 10s + timeout: 5s + retries: 3 + networks: + - database + - integration + - frontend + ports: + - 8700:8700 depends_on: postgres: condition: service_healthy diff --git a/kontor-api-go/Dockerfile b/kontor-api-echo/Dockerfile similarity index 100% rename from kontor-api-go/Dockerfile rename to kontor-api-echo/Dockerfile diff --git a/kontor-api-echo/cmd/kontor/main.go b/kontor-api-echo/cmd/kontor/main.go new file mode 100644 index 0000000..ced8c79 --- /dev/null +++ b/kontor-api-echo/cmd/kontor/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "kontor-api-echo/pkg/handler" + "kontor-api-echo/pkg/schema" + "log" + + // jwtware "github.com/gofiber/contrib/jwt" + // "github.com/gofiber/fiber/v2/middleware/logger" + echojwt "github.com/labstack/echo-jwt/v4" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +func main() { + log.Println("Kontor started") + + if err := schema.Connect(); err != nil { + log.Fatal(err) + } + + e := echo.New() + e.GET("/health", handler.GetHealth) + e.POST("/login", handler.Login) + + skipper := func(c echo.Context) bool { + // Skip health check endpoint + return c.Request().URL.Path == "/health" + } + e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ + LogStatus: true, + LogURI: true, + Skipper: skipper, + BeforeNextFunc: func(c echo.Context) { + c.Set("customValueFromContext", 42) + }, + LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { + value, _ := c.Get("customValueFromContext").(int) + fmt.Printf("REQUEST: uri: %v, status: %v, custom-value: %v\n", v.URI, v.Status, value) + return nil + }, + })) + + group := e.Group("/api") + group.Use(echojwt.WithConfig(echojwt.Config{SigningKey: []byte("secret")})) + handler.SetupComicRoutes(group) + handler.SetupMediaRoutes(group) + + e.Logger.Fatal(e.Start(":8700")) + log.Println("Kontor finished") +} diff --git a/kontor-api-echo/go.mod b/kontor-api-echo/go.mod new file mode 100644 index 0000000..d0b183c --- /dev/null +++ b/kontor-api-echo/go.mod @@ -0,0 +1,39 @@ +module kontor-api-echo + +go 1.24.2 + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/labstack/echo-jwt/v4 v4.4.0 // indirect + github.com/labstack/echo/v4 v4.15.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/uptrace/bun v1.2.16 // indirect + github.com/uptrace/bun/dialect/pgdialect v1.2.16 // indirect + github.com/uptrace/bun/driver/pgdriver v1.2.16 // indirect + github.com/uptrace/bun/extra/bundebug v1.2.16 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + mellium.im/sasl v0.3.2 // indirect +) diff --git a/kontor-api-echo/go.sum b/kontor-api-echo/go.sum new file mode 100644 index 0000000..bb2a707 --- /dev/null +++ b/kontor-api-echo/go.sum @@ -0,0 +1,68 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/labstack/echo-jwt/v4 v4.4.0 h1:nrXaEnJupfc2R4XChcLRDyghhMZup77F8nIzHnBK19U= +github.com/labstack/echo-jwt/v4 v4.4.0/go.mod h1:kYXWgWms9iFqI3ldR+HAEj/Zfg5rZtR7ePOgktG4Hjg= +github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24= +github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= +github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= +github.com/uptrace/bun v1.2.16 h1:QlObi6ZIK5Ao7kAALnh91HWYNZUBbVwye52fmlQM9kc= +github.com/uptrace/bun v1.2.16/go.mod h1:jMoNg2n56ckaawi/O/J92BHaECmrz6IRjuMWqlMaMTM= +github.com/uptrace/bun/dialect/pgdialect v1.2.16 h1:KFNZ0LxAyczKNfK/IJWMyaleO6eI9/Z5tUv3DE1NVL4= +github.com/uptrace/bun/dialect/pgdialect v1.2.16/go.mod h1:IJdMeV4sLfh0LDUZl7TIxLI0LipF1vwTK3hBC7p5qLo= +github.com/uptrace/bun/driver/pgdriver v1.2.16 h1:b1kpXKUxtTSGYow5Vlsb+dKV3z0R7aSAJNfMfKp61ZU= +github.com/uptrace/bun/driver/pgdriver v1.2.16/go.mod h1:H6lUZ9CBfp1X5Vq62YGSV7q96/v94ja9AYFjKvdoTk0= +github.com/uptrace/bun/extra/bundebug v1.2.16 h1:3OXAfHTU4ydu2+4j05oB1BxPx6+ypdWIVzTugl/7zl0= +github.com/uptrace/bun/extra/bundebug v1.2.16/go.mod h1:vk6R/1i67/S2RvUI5AH/m3P5e67mOkfDCmmCsAPUumo= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0= +mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY= diff --git a/kontor-api-echo/pkg/handler/auth.go b/kontor-api-echo/pkg/handler/auth.go new file mode 100644 index 0000000..dce89bb --- /dev/null +++ b/kontor-api-echo/pkg/handler/auth.go @@ -0,0 +1,66 @@ +package handler + +import ( + "context" + "kontor-api-echo/pkg/schema" + "kontor-api-echo/pkg/utils" + "net/http" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +type jwtCustomClaims struct { + Name string `json:"name"` + Admin bool `json:"admin"` + jwt.RegisteredClaims +} + +func Login(c echo.Context) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + var profile schema.Profile + var err error + var db *bun.DB + ctx := context.Background() + + db, _ = schema.GetDatabase() + err = db.NewSelect().Model(&profile).Where("email = ?", user).Scan(ctx) + if err != nil { + return c.String(http.StatusInternalServerError, err.Error()) + } + + if !utils.ComparePassword(profile.Password, pass) { + return echo.ErrUnauthorized + } + + // Set custom claims + claims := &jwtCustomClaims{ + "Jon Snow", + true, + jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 72)), + }, + } + + // Create token with claims + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte("secret")) + if err != nil { + return err + } + + return c.JSON(http.StatusOK, echo.Map{"token": t}) +} + +func restricted(c echo.Context) error { + user := c.Get("user").(*jwt.Token) + claims := user.Claims.(*jwtCustomClaims) + name := claims.Name + return c.String(http.StatusOK, "Welcome "+name+"!") +} diff --git a/kontor-api-echo/pkg/handler/comics.go b/kontor-api-echo/pkg/handler/comics.go new file mode 100644 index 0000000..91b7e9c --- /dev/null +++ b/kontor-api-echo/pkg/handler/comics.go @@ -0,0 +1,78 @@ +package handler + +import ( + "context" + "kontor-api-echo/pkg/schema" + "log" + "net/http" + + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +func SetupComicRoutes(api *echo.Group) { + comics := api.Group("/comics") + comics.GET("/comics", GetAllComics) + comics.GET("/publishers", GetAllPublishers) + comics.GET("/comicworks", GetAllComicWorks) +} + +func GetAllComics(c echo.Context) error { + var comics []schema.Comic + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx) + if err != nil { + log.Fatal(err) + return echo.ErrInternalServerError + } + + return c.JSON(http.StatusOK, comics) +} + +func GetAllPublishers(c echo.Context) error { + var publishers []schema.Publisher + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx) + if err != nil { + log.Fatal(err) + return echo.ErrInternalServerError + } + + return c.JSON(http.StatusOK, publishers) +} + +func GetAllComicWorks(c echo.Context) error { + var comic_works []schema.ComicWork + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx) + if err != nil { + log.Fatal(err) + return echo.ErrInternalServerError + } + + return c.JSON(http.StatusOK, comic_works) +} diff --git a/kontor-api-echo/pkg/handler/health.go b/kontor-api-echo/pkg/handler/health.go new file mode 100644 index 0000000..d97ce24 --- /dev/null +++ b/kontor-api-echo/pkg/handler/health.go @@ -0,0 +1,17 @@ +package handler + +import ( + "net/http" + + "github.com/labstack/echo/v4" +) + +type Status struct { + Status string `json:"status"` +} + +func GetHealth(c echo.Context) error { + status := new(Status) + status.Status = "ok" + return c.JSON(http.StatusOK, status) +} diff --git a/kontor-api-echo/pkg/handler/media.go b/kontor-api-echo/pkg/handler/media.go new file mode 100644 index 0000000..46e7eba --- /dev/null +++ b/kontor-api-echo/pkg/handler/media.go @@ -0,0 +1,67 @@ +package handler + +import ( + "context" + "fmt" + "kontor-api-echo/pkg/schema" + "log" + "net/http" + "time" + + "github.com/google/uuid" + "github.com/labstack/echo/v4" + "github.com/uptrace/bun" +) + +func SetupMediaRoutes(api *echo.Group) { + media := api.Group("/media") + media.GET("/files", GetAllFiles) + media.POST("/files", AddFile) +} + +func GetAllFiles(c echo.Context) error { + var files []schema.MediaFile + var err error + var db *bun.DB + ctx := context.Background() + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + err = db.NewSelect().Model(&files).Relation("MediaActorFiles").Scan(ctx) + if err != nil { + log.Fatal(err) + return echo.ErrInternalServerError + } + + return c.JSON(http.StatusOK, files) +} + +type Link struct { + URL string `json:"url"` +} + +func AddFile(c echo.Context) error { + var err error + var db *bun.DB + ctx := context.Background() + link := new(Link) + if err = c.Bind(link); err != nil { + return err + } + log.Printf("URL %s has been sent", link) + + db, err = schema.GetDatabase() + if err != nil { + log.Fatal(err) + } + id := uuid.NewString() + timestamp := time.Now() + mediafile := &schema.MediaFile{ID: id, CreatedAt: timestamp, UpdatedAt: timestamp, WebLink: fmt.Sprintf("%s", link), Version: 1, ShouldDownload: true, Review: true} + _, err = db.NewInsert().Model(mediafile).Exec(ctx) + if err != nil { + return err + } + return c.String(http.StatusCreated, "Link created") +} diff --git a/kontor-api-go/pkg/schema/auth.go b/kontor-api-echo/pkg/schema/auth.go similarity index 100% rename from kontor-api-go/pkg/schema/auth.go rename to kontor-api-echo/pkg/schema/auth.go diff --git a/kontor-api-go/pkg/schema/comics.go b/kontor-api-echo/pkg/schema/comics.go similarity index 100% rename from kontor-api-go/pkg/schema/comics.go rename to kontor-api-echo/pkg/schema/comics.go diff --git a/kontor-api-go/pkg/schema/comics_test.go b/kontor-api-echo/pkg/schema/comics_test.go similarity index 100% rename from kontor-api-go/pkg/schema/comics_test.go rename to kontor-api-echo/pkg/schema/comics_test.go diff --git a/kontor-api-go/pkg/schema/database.go b/kontor-api-echo/pkg/schema/database.go similarity index 100% rename from kontor-api-go/pkg/schema/database.go rename to kontor-api-echo/pkg/schema/database.go diff --git a/kontor-api-go/pkg/schema/media.go b/kontor-api-echo/pkg/schema/media.go similarity index 100% rename from kontor-api-go/pkg/schema/media.go rename to kontor-api-echo/pkg/schema/media.go diff --git a/kontor-api-go/pkg/utils/auth.go b/kontor-api-echo/pkg/utils/auth.go similarity index 100% rename from kontor-api-go/pkg/utils/auth.go rename to kontor-api-echo/pkg/utils/auth.go diff --git a/kontor-api-go/pkg/utils/token.go b/kontor-api-echo/pkg/utils/token.go similarity index 96% rename from kontor-api-go/pkg/utils/token.go rename to kontor-api-echo/pkg/utils/token.go index 5562c66..bad0fbf 100644 --- a/kontor-api-go/pkg/utils/token.go +++ b/kontor-api-echo/pkg/utils/token.go @@ -1,7 +1,7 @@ package utils import ( - "kontor-api-go/pkg/schema" + "kontor-api-echo/pkg/schema" "time" "github.com/golang-jwt/jwt/v5" diff --git a/kontor-api-fiber/Dockerfile b/kontor-api-fiber/Dockerfile new file mode 100644 index 0000000..08b639f --- /dev/null +++ b/kontor-api-fiber/Dockerfile @@ -0,0 +1,28 @@ +# ---------- Stage 1: Build ---------- +FROM golang:1.25-alpine AS builder +# Set the working directory +WORKDIR /app +# Ensure a portable, static-ish binary +ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 +# Copy and download dependencies +COPY go.mod go.sum ./ +RUN go mod download +# Copy the source code +COPY . . +# Build the Go application (strip debug info for smaller size) +RUN go build -trimpath -ldflags="-s -w" -o kontor cmd/kontor/main.go + +# ---------- Stage 2: Final ---------- +FROM alpine:latest +# Set the working directory +WORKDIR /app +# Install runtime dependencies you actually need +RUN apk add --no-cache ca-certificates tzdata curl +# Create non-root user for security +RUN addgroup -S kontor && adduser -S -G kontor -H -s /sbin/nologin kontor +# Copy the binary and set ownership +COPY --from=builder --chown=kontor:kontor /app/kontor /app/kontor +# Run as non-root user +USER kontor +# Set the entrypoint command +ENTRYPOINT ["/app/kontor"] diff --git a/kontor-api-go/cmd/kontor/main.go b/kontor-api-fiber/cmd/kontor/main.go similarity index 90% rename from kontor-api-go/cmd/kontor/main.go rename to kontor-api-fiber/cmd/kontor/main.go index 9f5f96e..28ba3ea 100644 --- a/kontor-api-go/cmd/kontor/main.go +++ b/kontor-api-fiber/cmd/kontor/main.go @@ -1,8 +1,8 @@ package main import ( - "kontor-api-go/pkg/handler" - "kontor-api-go/pkg/schema" + "kontor-api-fiber/pkg/handler" + "kontor-api-fiber/pkg/schema" "log" jwtware "github.com/gofiber/contrib/jwt" @@ -35,7 +35,7 @@ func main() { handler.SetupComicRoutes(api) handler.SetupMediaRoutes(api) // Listen on port 8900 - app.Listen(":8900") + app.Listen(":8600") log.Println("Kontor finished") } diff --git a/kontor-api-go/go.mod b/kontor-api-fiber/go.mod similarity index 98% rename from kontor-api-go/go.mod rename to kontor-api-fiber/go.mod index dca5b4e..c968c83 100644 --- a/kontor-api-go/go.mod +++ b/kontor-api-fiber/go.mod @@ -1,4 +1,4 @@ -module kontor-api-go +module kontor-api-fiber go 1.24.2 diff --git a/kontor-api-go/go.sum b/kontor-api-fiber/go.sum similarity index 100% rename from kontor-api-go/go.sum rename to kontor-api-fiber/go.sum diff --git a/kontor-api-go/pkg/handler/auth.go b/kontor-api-fiber/pkg/handler/auth.go similarity index 94% rename from kontor-api-go/pkg/handler/auth.go rename to kontor-api-fiber/pkg/handler/auth.go index b0c9b0b..0dcf290 100644 --- a/kontor-api-go/pkg/handler/auth.go +++ b/kontor-api-fiber/pkg/handler/auth.go @@ -2,8 +2,8 @@ package handler import ( "context" - "kontor-api-go/pkg/schema" - "kontor-api-go/pkg/utils" + "kontor-api-fiber/pkg/schema" + "kontor-api-fiber/pkg/utils" "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v5" diff --git a/kontor-api-go/pkg/handler/comics.go b/kontor-api-fiber/pkg/handler/comics.go similarity index 98% rename from kontor-api-go/pkg/handler/comics.go rename to kontor-api-fiber/pkg/handler/comics.go index cc9c978..2d9c932 100644 --- a/kontor-api-go/pkg/handler/comics.go +++ b/kontor-api-fiber/pkg/handler/comics.go @@ -2,7 +2,7 @@ package handler import ( "context" - "kontor-api-go/pkg/schema" + "kontor-api-fiber/pkg/schema" "log" "github.com/gofiber/fiber/v2" diff --git a/kontor-api-go/pkg/handler/health.go b/kontor-api-fiber/pkg/handler/health.go similarity index 100% rename from kontor-api-go/pkg/handler/health.go rename to kontor-api-fiber/pkg/handler/health.go diff --git a/kontor-api-go/pkg/handler/media.go b/kontor-api-fiber/pkg/handler/media.go similarity index 97% rename from kontor-api-go/pkg/handler/media.go rename to kontor-api-fiber/pkg/handler/media.go index 6502312..be40c44 100644 --- a/kontor-api-go/pkg/handler/media.go +++ b/kontor-api-fiber/pkg/handler/media.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "kontor-api-go/pkg/schema" + "kontor-api-fiber/pkg/schema" "log" "time" diff --git a/kontor-api-fiber/pkg/schema/auth.go b/kontor-api-fiber/pkg/schema/auth.go new file mode 100644 index 0000000..0e39f9e --- /dev/null +++ b/kontor-api-fiber/pkg/schema/auth.go @@ -0,0 +1,62 @@ +package schema + +import ( + "time" + + "github.com/uptrace/bun" +) + +type Profile struct { + bun.BaseModel `bun:"table:profile"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + FirstName string `bun:"first_name"` + LastName string `bun:"last_name"` + UserName string `bun:"user_name,unique:user_name"` + Email string `bun:"email"` + Password string `bun:"password"` + Enabled bool `bun:"enabled"` + Assignments []Assignment `bun:"rel:has-many,join:id=profile_id"` + Tokens []Token `bun:"rel:has-many,join:id=profile_id"` +} + +type Permission struct { + bun.BaseModel `bun:"table:permission"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name"` + Assignments []Assignment `bun:"rel:has-many,join:id=permission_id"` +} + +type Token struct { + bun.BaseModel `bun:"table:token"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name"` + LastUsedAt time.Time `bun:"last_used_date,nullzero,notnull,default:current_timestamp"` + Enabled bool `bun:"enabled,default:true"` + ProfileID *string `bun:"profile_id"` + Profile *Profile `bun:"rel:belongs-to,join:profile_id=id"` +} + +type Assignment struct { + bun.BaseModel `bun:"table:assignment"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + ProfileID *string `bun:"profile_id"` + Profile *Profile `bun:"rel:belongs-to,join:profile_id=id"` + PermissionID *string `bun:"permission_id"` + Permission *Permission `bun:"rel:belongs-to,join:permission_id=id"` +} diff --git a/kontor-api-fiber/pkg/schema/comics.go b/kontor-api-fiber/pkg/schema/comics.go new file mode 100644 index 0000000..06b1a7e --- /dev/null +++ b/kontor-api-fiber/pkg/schema/comics.go @@ -0,0 +1,140 @@ +package schema + +import ( + "time" + + "github.com/uptrace/bun" +) + +type Publisher struct { + bun.BaseModel `bun:"table:publisher"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name"` + WebLink string `bun:"weblink"` + + ParentPublisherID *string `bun:"parent_publisher_id"` + ParentPublisher *Publisher `bun:"rel:belongs-to,join:parent_publisher_id=id"` + Imprints []Publisher `bun:"rel:has-many,join:id=parent_publisher_id"` + Comics []Comic `bun:"rel:has-many,join:id=publisher_id"` +} + +type Comic struct { + bun.BaseModel `bun:"table:comic"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Title string `bun:"title,unique:title,notnull"` + CurrentOrder bool `bun:"current_order"` + Completed bool `bun:"completed"` + WebLink string `bun:"weblink"` + + PublisherID *string `bun:"publisher_id"` + Publisher *Publisher `bun:"rel:belongs-to,join:publisher_id=id"` + Issues []Issue `bun:"rel:has-many,join:id=comic_id"` + StoryArcs []StoryArc `bun:"rel:has-many,join:id=comic_id"` + TradePaperbacks []TradePaperback `bun:"rel:has-many,join:id=comic_id"` + Volumes []Volume `bun:"rel:has-many,join:id=comic_id"` + ComicWorks []ComicWork `bun:"rel:has-many,join:id=comic_id"` +} + +type Artist struct { + bun.BaseModel `bun:"table:artist"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:title,notnull"` + WebLink string `bun:"weblink"` +} + +type Issue struct { + bun.BaseModel `bun:"table:issue"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + InStock bool `bun:"in_stock"` + IsRead bool `bun:"is_read"` + IssueNumber string `bu:"issue_number"` + Title string `bun:"title"` + PublishedOn time.Time `bun:"published_on"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + StoryArcID *string `bun:"story_arc_id"` + StoryArc *StoryArc `bun:"rel:belongs-to,join:story_arc_id=id"` + VolumeID *string `bun:"volume_id"` + Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"` +} + +type StoryArc struct { + bun.BaseModel `bun:"table:story_arc"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + VolumeID *string `bun:"volume_id"` + Volume *Volume `bun:"rel:belongs-to,join:volume_id=id"` +} + +type TradePaperback struct { + bun.BaseModel `bun:"table:trade_paperback"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + IssueStart int `bun:"issue_start"` + IssueEnd int `bun:"issue_end"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` +} + +type Volume struct { + bun.BaseModel `bun:"table:volume"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` +} + +type WorkType struct { + bun.BaseModel `bun:"table:worktype"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name,notnull"` +} + +type ComicWork struct { + bun.BaseModel `bun:"table:comic_work"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + ArtistID *string `bun:"artist_id"` + Artist *Artist `bun:"rel:belongs-to,join:artist_id=id"` + ComicID *string `bun:"comic_id"` + Comic *Comic `bun:"rel:belongs-to,join:comic_id=id"` + WorkTypeID *string `bun:"work_type_id"` + WorkType *WorkType `bun:"rel:belongs-to,join:work_type_id=id"` +} diff --git a/kontor-api-fiber/pkg/schema/comics_test.go b/kontor-api-fiber/pkg/schema/comics_test.go new file mode 100644 index 0000000..ea2505e --- /dev/null +++ b/kontor-api-fiber/pkg/schema/comics_test.go @@ -0,0 +1,101 @@ +package schema + +import ( + "context" + "log" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "database/sql" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/extra/bundebug" + + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/driver/pgdriver" +) + +func GetTestDatabase() (*bun.DB, error) { + var err error + + dsn := "postgres://kontor:kontor@localhost:5432/kontor?sslmode=disable" + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) + + sqldb.SetMaxOpenConns(4) + sqldb.SetMaxIdleConns(4) + + DB := bun.NewDB(sqldb, pgdialect.New()) + DB.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true))) + + if err = DB.Ping(); err != nil { + return nil, err + } + + log.Println("Returned Database Connection") + return DB, nil +} + +func TestMain(m *testing.M) { + log.Println("Setup Test") + exitCode := m.Run() + os.Exit(exitCode) +} + +func TestSelectComics(t *testing.T) { + var comics []Comic + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + require.NoError(t, err) + + err = db.NewSelect().Model(&comics).Relation("Publisher").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 168, len(comics)) +} + +func TestSelectPublishers(t *testing.T) { + var publishers []Publisher + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + require.NoError(t, err) + + err = db.NewSelect().Model(&publishers).Relation("ParentPublisher").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 19, len(publishers)) +} + +func TestSelectWorkTypes(t *testing.T) { + var comic_works []ComicWork + var err error + var db *bun.DB + ctx := context.Background() + + db, err = GetTestDatabase() + if err != nil { + log.Fatal(err) + } + + err = db.NewSelect().Model(&comic_works).Relation("Comic").Relation("Artist").Relation("WorkType").Scan(ctx) + if err != nil { + log.Fatal(err) + } + + require.NoError(t, err) + assert.Equal(t, 59, len(comic_works)) +} diff --git a/kontor-api-fiber/pkg/schema/database.go b/kontor-api-fiber/pkg/schema/database.go new file mode 100644 index 0000000..6f5d573 --- /dev/null +++ b/kontor-api-fiber/pkg/schema/database.go @@ -0,0 +1,50 @@ +package schema + +import ( + "database/sql" + "log" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/pgdialect" + "github.com/uptrace/bun/driver/pgdriver" +) + +var DB *bun.DB + +func Connect() error { + var err error + + dsn := "postgres://kontor:kontor@postgres:5432/kontor?sslmode=disable" + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) + + sqldb.SetMaxOpenConns(4) + sqldb.SetMaxIdleConns(4) + + DB := bun.NewDB(sqldb, pgdialect.New()) + + if err = DB.Ping(); err != nil { + return err + } + + log.Println("Connection Opened to Database") + return nil +} + +func GetDatabase() (*bun.DB, error) { + var err error + + dsn := "postgres://kontor:kontor@postgres:5432/kontor?sslmode=disable" + sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn))) + + sqldb.SetMaxOpenConns(4) + sqldb.SetMaxIdleConns(4) + + DB := bun.NewDB(sqldb, pgdialect.New()) + + if err = DB.Ping(); err != nil { + return nil, err + } + + log.Println("Returned Database Connection") + return DB, nil +} diff --git a/kontor-api-fiber/pkg/schema/media.go b/kontor-api-fiber/pkg/schema/media.go new file mode 100644 index 0000000..d8977e0 --- /dev/null +++ b/kontor-api-fiber/pkg/schema/media.go @@ -0,0 +1,79 @@ +package schema + +import ( + "time" + + "github.com/uptrace/bun" +) + +type MediaFile struct { + bun.BaseModel `bun:"table:media_file"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + CloudLink string `bun:"cloud_link"` + FileName string `bun:"file_name"` + Path string `bun:"path"` + Review bool `bun:"review"` + Title string `bun:"title"` + WebLink string `bun:"url,unique:url"` + ShouldDownload bool `bun:"should_download"` + + MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_file_id"` +} + +type MediaActor struct { + bun.BaseModel `bun:"table:media_actor"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Name string `bun:"name,unique:name"` + WebLink string `bun:"url,unique:url"` + + MediaActorFiles []MediaActorFile `bun:"rel:has-many,join:id=media_actor_id"` +} + +type MediaActorFile struct { + bun.BaseModel `bun:"table:media_actor_file"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + MediaActorID *string `bun:"media_actor_id"` + MediaActor *MediaActor `bun:"rel:belongs-to,join:media_actor_id=id"` + MediaFileID *string `bun:"media_file_id"` + MediaFile *MediaFile `bun:"rel:belongs-to,join:media_file_id=id"` +} + +type MediaArticle struct { + bun.BaseModel `bun:"table:media_article"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + Review bool `bun:"review"` + Title string `bun:"title"` + WebLink string `bun:"url,unique:url"` +} + +type MediaVideo struct { + bun.BaseModel `bun:"table:media_article"` + + ID string `bun:"id,pk"` + CreatedAt time.Time `bun:"created_date,nullzero,notnull,default:current_timestamp"` + UpdatedAt time.Time `bun:"last_modified_date,nullzero,notnull,default:current_timestamp"` + Version int `bun:"version,default:0"` + CloudLink string `bun:"cloud_link"` + FileName string `bun:"file_name"` + Path string `bun:"path"` + Review bool `bun:"review"` + Title string `bun:"title"` + WebLink string `bun:"url,unique:url"` + ShouldDownload bool `bun:"should_download"` +} diff --git a/kontor-api-fiber/pkg/utils/auth.go b/kontor-api-fiber/pkg/utils/auth.go new file mode 100644 index 0000000..11caccc --- /dev/null +++ b/kontor-api-fiber/pkg/utils/auth.go @@ -0,0 +1,8 @@ +package utils + +import "golang.org/x/crypto/bcrypt" + +func ComparePassword(hashedPassword, password string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) + return err == nil +} diff --git a/kontor-api-fiber/pkg/utils/token.go b/kontor-api-fiber/pkg/utils/token.go new file mode 100644 index 0000000..f67bc0d --- /dev/null +++ b/kontor-api-fiber/pkg/utils/token.go @@ -0,0 +1,38 @@ +package utils + +import ( + "kontor-api-fiber/pkg/schema" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +func GenerateToken(user schema.Profile) (string, error) { + // Create the Claims + claims := jwt.MapClaims{ + "name": user.FirstName + ", " + user.LastName, + "admin": true, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte("secret")) + if err != nil { + return "", err + } + return t, nil +} + +func VerifyToken(tokenString string) (bool, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return []byte("secret"), nil + }) + if err != nil { + return false, err + } + + return token.Valid, nil +} diff --git a/kontor-api/Dockerfile b/kontor-api/Dockerfile index 15a0b5a..df9de03 100644 --- a/kontor-api/Dockerfile +++ b/kontor-api/Dockerfile @@ -51,5 +51,5 @@ ENV PATH="/app/.venv/bin:$PATH" EXPOSE $PORT # Start the application with Uvicorn in production mode, using environment variable references -CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8800"] +CMD ["uvicorn", "src.main:kontor", "--log-level", "info", "--host", "0.0.0.0" , "--port", "8500"] diff --git a/kontor-api/src/main.py b/kontor-api/src/main.py index 154363a..46dcc4e 100644 --- a/kontor-api/src/main.py +++ b/kontor-api/src/main.py @@ -1,17 +1,17 @@ from contextlib import asynccontextmanager from fastapi import FastAPI -from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from src.apis.base import api_router from src.apis.version1.healthcheck import health_router +from src.core.config import settings from src.core.log_conf import logger +from src.db.models.base import Base from src.db.session import engine from src.db.utils import check_db_connected, check_db_disconnected from src.webapps.base import api_router as web_app_router -from src.core.config import settings -from src.db.models.base import Base @asynccontextmanager @@ -20,33 +20,41 @@ async def lifespan(app: FastAPI): yield await check_db_disconnected() + def include_router(app: FastAPI): app.include_router(api_router) app.include_router(web_app_router) app.include_router(health_router) + def configure_static(app: FastAPI): app.mount("/static", StaticFiles(directory="src/static"), name="static") + def add_middle_ware(app: FastAPI): app.add_middleware( - CORSMiddleware, - allow_origins=['*'], + CORSMiddleware, + allow_origins=["*"], allow_credentials=True, - allow_methods=['*'], - allow_headers=['*'], + allow_methods=["*"], + allow_headers=["*"], ) + def create_tables(): Base.metadata.create_all(bind=engine) + def start_application(log): log.info(f"using database: {settings.DATABASE_URL}") - app = FastAPI(title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, lifespan=lifespan) + app = FastAPI( + title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, lifespan=lifespan + ) include_router(app) configure_static(app) add_middle_ware(app) create_tables() return app + kontor = start_application(logger)