Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ENV GOPROXY=direct
RUN apk add --no-cache make postgresql-client git curl

COPY go.mod go.sum ./
RUN go mod tidy
RUN go mod download

# Development Mode
Expand Down
6 changes: 4 additions & 2 deletions docker-compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
services:
app:
build:
build:
context: .
target: dev
ports:
- "9898:9898"
depends_on:
- db
env_file:
- .env
environment:
- DB_HOST=db
- DB_USER=${DB_USER:-nymeria}
Expand All @@ -31,4 +33,4 @@ services:
restart: unless-stopped

volumes:
postgres_data_dev:
postgres_data_dev:
2 changes: 1 addition & 1 deletion docker-compose.prod.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
app:
build:
build:
context: .
target: prod
ports:
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.23.0
require (
github.com/gin-contrib/cors v1.7.5
github.com/gin-gonic/gin v1.10.1
github.com/google/uuid v1.6.0
github.com/rs/zerolog v1.34.0
github.com/stretchr/testify v1.10.0
gorm.io/gorm v1.30.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
Expand Down
215 changes: 215 additions & 0 deletions internal/api/applications.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package api

import (
"net/http"

"github.com/gin-gonic/gin"

database "github.com/sdslabs/nymeria/internal/database/applications"
"github.com/sdslabs/nymeria/internal/database/schema"
"github.com/sdslabs/nymeria/internal/utils"
)

func HandleGetApplicationFlow(c *gin.Context) {
csrfToken, err := utils.GenerateCSRFToken(c.GetHeader("X-User-ID")) // TODO: Get user ID from session
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": "Failed to generate CSRF token",
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "create application flow",
"csrf_token": csrfToken,
})
}

func HandleFetchAllApplicationsFlow(c *gin.Context) {
apps, err := database.GetAllApplications()

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "applications fetched",
"data": apps,
})
}

func HandleFetchApplicationByIDFlow(c *gin.Context) {
id := c.Param("id")

app, err := database.GetApplicationByID(id)

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "application fetched",
"data": app,
})
}

func HandleCreateApplicationFlow(c *gin.Context) {
var req CreateApplicationRequest

err := c.ShouldBindJSON(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

clientKey, err := utils.GenerateRandomString(16)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": "Failed to generate client key",
})
return
}

clientSecret, err := utils.GenerateRandomString(32)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": "Failed to generate client secret",
})
return
}

app := schema.Application{
Name: req.Name,
AllowedOrigins: req.AllowedOrigins,
RedirectURIs: req.RedirectURIs,
ClientKey: clientKey,
ClientSecret: clientSecret,
}

err = database.CreateApplication(app)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "application created",
"data": app,
})
}

func HandleDeleteApplicationFlow(c *gin.Context) {
id := c.Param("id")

err := database.DeleteApplication(id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "application deleted",
})
}

func HandleUpdateApplicationFlow(c *gin.Context) {
var req UpdateApplicationRequest

err := c.ShouldBindJSON(&req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

app, err := database.GetApplicationByID(req.ApplicationID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

if req.NewKeyFlag {
clientKey, err := utils.GenerateRandomString(16)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

clientSecret, err := utils.GenerateRandomString(32)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

app.ClientKey = clientKey
app.ClientSecret = clientSecret
}

if req.ApplicationURL != "" {
app.ApplicationURL = req.ApplicationURL
}

if len(req.AllowedOrigins) > 0 {
app.AllowedOrigins = req.AllowedOrigins
}

if len(req.RedirectURIs) > 0 {
app.RedirectURIs = req.RedirectURIs
}

err = database.UpdateApplication(req.ApplicationID, app)

if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "error",
"message": err.Error(),
})
return
}

c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "application updated",
"data": gin.H{
"application_id": req.ApplicationID,
"client_key": app.ClientKey,
"client_secret": app.ClientSecret,
},
})
}
41 changes: 32 additions & 9 deletions internal/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,53 +9,76 @@ import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"

"github.com/sdslabs/nymeria/internal/config"
"github.com/sdslabs/nymeria/internal/database"
"github.com/sdslabs/nymeria/internal/log"
"github.com/sdslabs/nymeria/internal/logger"
"github.com/sdslabs/nymeria/internal/middlewares"
)

func Start() {
// Initialize global configuration
config.Init()

if err := database.Init(); err != nil {
panic(err)
}

r := gin.Default()

// Use custom logging middleware
r.Use(log.LoggerMiddleware(log.Logger))
r.Use(logger.Middleware(logger.Logger))

// CORS configuration
config := cors.New(cors.Config{
corsConfig := cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000"}, // TODO: change in prod
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Authorization", "Content-Type"},
AllowHeaders: []string{"Authorization", "Content-Type", "X-CSRF-Token", "X-User-ID"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * 3600, // 12 hours in seconds
})

r.Use(config)
r.Use(corsConfig)

r.GET("/", func(c *gin.Context) {
log.Logger.Info().Msg("welcome")
logger.Info().Msg("welcome")
c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "welcome",
})
})

r.GET("/ping", func(c *gin.Context) {
log.Logger.Info().Msg("ping")
logger.Info().Msg("ping")
logger.Info().Msg(config.AppConfig.DBHost)
c.JSON(http.StatusOK, gin.H{
"status": "success",
"message": "pong",
})
})

r.GET("/register", HandleGetRegistrationFlow)
r.POST("/register", HandlePostRegistrationFlow)
r.POST("/register", middlewares.CSRFMiddleware(), HandlePostRegistrationFlow)

r.GET("/login", HandleGetLoginFlow)
r.POST("/login", HandlePostLoginFlow)
r.POST("/login", middlewares.CSRFMiddleware(), HandlePostLoginFlow)

r.GET("/verification", HandleGetVerificationFlow)
r.POST("/verification", HandlePostVerificationCodeFlow)
r.POST("/verification/code", HandlePostVerifyEmailFlow)

r.GET("/applications", HandleGetApplicationFlow)
r.POST("/applications", HandleFetchAllApplicationsFlow)
r.POST("/applications/:id", HandleFetchApplicationByIDFlow)

r.GET("/applications/create", HandleGetApplicationFlow)
r.POST("/applications/create", middlewares.CSRFMiddleware(), HandleCreateApplicationFlow)

r.GET("/applications/update", HandleGetApplicationFlow)
r.POST("/applications/update", middlewares.CSRFMiddleware(), HandleUpdateApplicationFlow)

r.GET("/applications/delete", HandleGetApplicationFlow)
r.DELETE("/applications/delete/:id", middlewares.CSRFMiddleware(), HandleDeleteApplicationFlow)

r.Run(":9898")
}
9 changes: 5 additions & 4 deletions internal/api/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
"github.com/gin-gonic/gin"

"github.com/sdslabs/nymeria/internal/database"
"github.com/sdslabs/nymeria/internal/log"
"github.com/sdslabs/nymeria/internal/database/schema"
"github.com/sdslabs/nymeria/internal/logger"
)

// HandleGetRegistrationFlow handles the GET request for the registration flow.
Expand All @@ -26,7 +27,7 @@ func HandlePostRegistrationFlow(c *gin.Context) {
var req RegistrationRequest
err := c.ShouldBindJSON(&req)
if err != nil {
log.Logger.Err(err).Msg("invalid json")
logger.Err(err).Msg("invalid json")
c.JSON(http.StatusBadRequest, gin.H{
"status": "error",
"message": err.Error(),
Expand Down Expand Up @@ -63,7 +64,7 @@ func HandlePostRegistrationFlow(c *gin.Context) {
return
}

user := database.User{
user := schema.User{
Username: req.Username,
Password: req.Password,
Email: req.Email,
Expand All @@ -77,7 +78,7 @@ func HandlePostRegistrationFlow(c *gin.Context) {
"message": "GitHub ID already exists",
})
} else {
log.Logger.Err(result.Error).Msg("failed to insert user")
logger.Err(result.Error).Msg("failed to insert user")
}
return
}
Expand Down
Loading
Loading