Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/84/connect-frontend-form-to-api #85

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
20 changes: 20 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# App configuration
APP_ENV=development
PORT=6969

# Database configuration
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=postgres
DB_HOST=localhost
DB_PORT=5432
DB_SSLMODE=disable

# JWT configuration
JWT_SECRET=your-jwt-secret

# AWS configuration
AWS_REGION=us-west-2
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_S3_BUCKET=your-bucket-name
28 changes: 28 additions & 0 deletions backend/db/db.go
AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ require (
)

require (
github.com/aws/aws-sdk-go-v2 v1.32.6 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.6 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect
github.com/aws/smithy-go v1.22.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
Expand All @@ -23,6 +41,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
Expand Down
40 changes: 40 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4=
github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo=
github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko=
github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw=
github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI=
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw=
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8=
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -28,6 +64,9 @@ github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
Expand Down Expand Up @@ -81,6 +120,7 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
15 changes: 15 additions & 0 deletions backend/internal/middleware/req_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package middleware
import (
"fmt"
"net/http"
"os"
"reflect"
"strings"

"KonferCA/SPUR/db"
"github.com/go-playground/validator/v10"
Expand Down Expand Up @@ -33,6 +35,7 @@ func NewRequestBodyValidator() *RequestBodyValidator {
v := validator.New()
v.RegisterValidation("valid_user_role", validateUserRole)
v.RegisterValidation("non_admin_role", validateNonAdminRole)
v.RegisterValidation("s3_url", validateS3URL)
return &RequestBodyValidator{validator: v}
}

Expand Down Expand Up @@ -105,3 +108,15 @@ func validateNonAdminRole(fl validator.FieldLevel) bool {

return false
}

// validateS3URL ensures that file URLs point to our S3 bucket
func validateS3URL(fl validator.FieldLevel) bool {
url := fl.Field().String()
bucket := os.Getenv("AWS_S3_BUCKET")
if bucket == "" {
log.Warn().Msg("AWS_S3_BUCKET environment variable not set")
return false
}
expectedPrefix := fmt.Sprintf("https://%s.s3.us-east-1.amazonaws.com/", bucket)
return strings.HasPrefix(url, expectedPrefix)
}
22 changes: 22 additions & 0 deletions backend/internal/middleware/upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package middleware

import (
"net/http"

"github.com/labstack/echo/v4"
)

func UploadMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Check if the request is multipart/form-data
if c.Request().Header.Get("Content-Type") != "" && c.Request().Method == "POST" {
err := c.Request().ParseMultipartForm(10 << 20) // 10 MB
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Failed to parse multipart form")
}
}
return next(c)
}
}
}
119 changes: 75 additions & 44 deletions backend/internal/server/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"fmt"
"net/http"
"os"
"reflect"
"time"
Expand All @@ -13,6 +14,7 @@ import (
"KonferCA/SPUR/common"
"KonferCA/SPUR/db"
"KonferCA/SPUR/internal/middleware"
"KonferCA/SPUR/storage"
)

type Server struct {
Expand All @@ -22,30 +24,32 @@ type Server struct {
apiV1 *echo.Group
authLimiter *middleware.RateLimiter
apiLimiter *middleware.RateLimiter
Storage *storage.Storage
}

// Create a new Server instance and registers all routes and middlewares.
// Initialize database pool connection.
func New(testing bool) (*Server, error) {
connStr := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_NAME"),
os.Getenv("DB_SSLMODE"),
)
pool, err := db.NewPool(connStr)
if err != nil {
return nil, err
AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved
s := &Server{}
AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved

if !testing {
// Initialize database connection
dbPool, err := db.Connect()
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %v", err)
}
s.DBPool = dbPool
s.queries = db.New(dbPool)

AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved
// Initialize storage
storage, err := storage.NewStorage()
if err != nil {
return nil, fmt.Errorf("failed to initialize storage: %v", err)
}
s.Storage = storage
}

// Initialize queries
queries := db.New(pool)

e := echo.New()

e.Debug = true

// create rate limiters
Expand All @@ -67,38 +71,15 @@ func New(testing bool) (*Server, error) {
)
}

// setup error handler and middlewares
e.HTTPErrorHandler = globalErrorHandler

if os.Getenv("APP_ENV") == common.DEVELOPMENT_ENV {
// use default cors config, allow everything in development
e.Use(echoMiddleware.CORS())
} else if os.Getenv("APP_ENV") == common.PRODUCTION_ENV {
e.Use(echoMiddleware.CORSWithConfig(echoMiddleware.CORSConfig{
AllowOrigins: []string{"https://spur.konfer.ca"},
}))
} else {
e.Use(echoMiddleware.CORSWithConfig(echoMiddleware.CORSConfig{
AllowOrigins: []string{"https://nk-preview.konfer.ca"},
}))
}

e.Use(middleware.Logger())
e.Use(echoMiddleware.Recover())
e.Use(apiLimiter.RateLimit()) // global rate limit
AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved

customValidator := middleware.NewRequestBodyValidator()
e.Validator = customValidator

server := &Server{
DBPool: pool,
queries: queries,
DBPool: s.DBPool,
queries: s.queries,
echoInstance: e,
authLimiter: authLimiter,
apiLimiter: apiLimiter,
}

// setup api routes
// setup api routes first
server.setupV1Routes()
server.setupAuthRoutes()
server.setupCompanyRoutes()
Expand All @@ -112,8 +93,57 @@ func New(testing bool) (*Server, error) {
server.setupFundingTransactionRoutes()
server.setupMeetingRoutes()
server.setupHealthRoutes()
server.setupStorageRoutes()

// setup error handler and middlewares after routes
e.HTTPErrorHandler = globalErrorHandler

if os.Getenv("APP_ENV") == common.DEVELOPMENT_ENV {
e.Use(echoMiddleware.CORSWithConfig(echoMiddleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowHeaders: []string{
echo.HeaderOrigin,
echo.HeaderContentType,
echo.HeaderAccept,
echo.HeaderContentLength,
"X-Request-ID",
},
}))
AmirAgassi marked this conversation as resolved.
Show resolved Hide resolved
} else if os.Getenv("APP_ENV") == common.PRODUCTION_ENV {
e.Use(echoMiddleware.CORSWithConfig(echoMiddleware.CORSConfig{
AllowOrigins: []string{"https://spur.konfer.ca"},
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowHeaders: []string{
echo.HeaderOrigin,
echo.HeaderContentType,
echo.HeaderAccept,
echo.HeaderContentLength,
"X-Request-ID",
},
}))
} else {
e.Use(echoMiddleware.CORSWithConfig(echoMiddleware.CORSConfig{
AllowOrigins: []string{"https://nk-preview.konfer.ca"},
AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete},
AllowHeaders: []string{
echo.HeaderOrigin,
echo.HeaderContentType,
echo.HeaderAccept,
echo.HeaderContentLength,
"X-Request-ID",
},
}))
}

e.Use(middleware.Logger())
e.Use(echoMiddleware.Recover())
e.Use(apiLimiter.RateLimit()) // global rate limit

customValidator := middleware.NewRequestBodyValidator()
e.Validator = customValidator

// setup static routes
// setup static routes last
server.setupStaticRoutes()

return server, nil
Expand All @@ -122,8 +152,9 @@ func New(testing bool) (*Server, error) {
func (s *Server) setupV1Routes() {
s.apiV1 = s.echoInstance.Group("/api/v1")

fmt.Println("Registered routes:")
for _, route := range s.echoInstance.Routes() {
s.echoInstance.Logger.Printf("Route: %s %s", route.Method, route.Path)
fmt.Printf("Route: %s %s\n", route.Method, route.Path)
}
}

Expand Down
Loading
Loading