Compare commits

..

5 Commits

7 changed files with 85 additions and 84 deletions

View File

@@ -1771,7 +1771,7 @@ const docTemplate = `{
}, },
"/health": { "/health": {
"get": { "get": {
"description": "Check the API health status along with database connectivity details", "description": "Check the API health status along with database connectivity and SMTP service details",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],

View File

@@ -1768,7 +1768,7 @@
}, },
"/health": { "/health": {
"get": { "get": {
"description": "Check the API health status along with database connectivity details", "description": "Check the API health status along with database connectivity and SMTP service details",
"consumes": [ "consumes": [
"application/json" "application/json"
], ],

View File

@@ -1387,7 +1387,8 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Check the API health status along with database connectivity details description: Check the API health status along with database connectivity and
SMTP service details
produces: produces:
- application/json - application/json
responses: responses:

View File

@@ -2,7 +2,6 @@ package integration
import ( import (
"bytes" "bytes"
"encoding/json"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strings" "strings"
@@ -63,13 +62,33 @@ func TestIntegration_Router_FullMiddlewareChain(t *testing.T) {
t.Run("DBMonitoring_Active", func(t *testing.T) { t.Run("DBMonitoring_Active", func(t *testing.T) {
request := makeGetRequest(t, router, "/health") request := makeGetRequest(t, router, "/health")
var response map[string]any response := assertJSONResponse(t, request, http.StatusOK)
if err := json.NewDecoder(request.Body).Decode(&response); err == nil { if response == nil {
if data, ok := response["data"].(map[string]any); ok { return
if _, exists := data["database_stats"]; !exists { }
t.Error("Expected database_stats in health response")
} data, ok := getDataFromResponse(response)
} if !ok {
t.Fatal("Expected data to be a map")
}
services, ok := data["services"].(map[string]any)
if !ok {
t.Fatal("Expected services in health response")
}
databaseService, ok := services["database"].(map[string]any)
if !ok {
t.Fatal("Expected database service in health response")
}
details, ok := databaseService["details"].(map[string]any)
if !ok {
t.Fatal("Expected database details in health response")
}
if _, exists := details["stats"]; !exists {
t.Error("Expected database stats in health response")
} }
}) })

View File

@@ -26,12 +26,7 @@ func NewCORSConfig() *CORSConfig {
} }
switch env { switch env {
case "production": case "production", "staging":
if origins := os.Getenv("CORS_ALLOWED_ORIGINS"); origins == "" {
config.AllowedOrigins = []string{}
}
config.AllowCredentials = true
case "staging":
if origins := os.Getenv("CORS_ALLOWED_ORIGINS"); origins == "" { if origins := os.Getenv("CORS_ALLOWED_ORIGINS"); origins == "" {
config.AllowedOrigins = []string{} config.AllowedOrigins = []string{}
} }
@@ -53,82 +48,66 @@ func NewCORSConfig() *CORSConfig {
return config return config
} }
func checkOrigin(origin string, allowedOrigins []string) (allowed bool, hasWildcard bool) {
for _, allowedOrigin := range allowedOrigins {
if allowedOrigin == "*" {
hasWildcard = true
allowed = true
break
}
if allowedOrigin == origin {
allowed = true
break
}
}
return
}
func setCORSHeaders(w http.ResponseWriter, origin string, hasWildcard bool, config *CORSConfig) {
if hasWildcard && !config.AllowCredentials {
w.Header().Set("Access-Control-Allow-Origin", "*")
} else {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
if config.AllowCredentials && !hasWildcard {
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
}
func CORSWithConfig(config *CORSConfig) func(http.Handler) http.Handler { func CORSWithConfig(config *CORSConfig) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin") origin := r.Header.Get("Origin")
if r.Method == "OPTIONS" { if origin == "" {
if origin != "" { if r.Method == "OPTIONS" {
allowed := false w.WriteHeader(http.StatusOK)
hasWildcard := false return
for _, allowedOrigin := range config.AllowedOrigins {
if allowedOrigin == "*" {
hasWildcard = true
allowed = true
break
}
if allowedOrigin == origin {
allowed = true
break
}
}
if !allowed {
http.Error(w, "Origin not allowed", http.StatusForbidden)
return
}
if hasWildcard && !config.AllowCredentials {
w.Header().Set("Access-Control-Allow-Origin", "*")
} else {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(config.AllowedMethods, ", "))
w.Header().Set("Access-Control-Allow-Headers", strings.Join(config.AllowedHeaders, ", "))
w.Header().Set("Access-Control-Max-Age", fmt.Sprintf("%d", config.MaxAge))
if config.AllowCredentials && !hasWildcard {
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
} }
next.ServeHTTP(w, r)
return
}
allowed, hasWildcard := checkOrigin(origin, config.AllowedOrigins)
if !allowed {
http.Error(w, "Origin not allowed", http.StatusForbidden)
return
}
if r.Method == "OPTIONS" {
setCORSHeaders(w, origin, hasWildcard, config)
w.Header().Set("Access-Control-Allow-Methods", strings.Join(config.AllowedMethods, ", "))
w.Header().Set("Access-Control-Allow-Headers", strings.Join(config.AllowedHeaders, ", "))
w.Header().Set("Access-Control-Max-Age", fmt.Sprintf("%d", config.MaxAge))
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
return return
} }
if origin != "" { setCORSHeaders(w, origin, hasWildcard, config)
allowed := false
hasWildcard := false
for _, allowedOrigin := range config.AllowedOrigins {
if allowedOrigin == "*" {
hasWildcard = true
allowed = true
break
}
if allowedOrigin == origin {
allowed = true
break
}
}
if !allowed {
http.Error(w, "Origin not allowed", http.StatusForbidden)
return
}
if hasWildcard && !config.AllowCredentials {
w.Header().Set("Access-Control-Allow-Origin", "*")
} else {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
if config.AllowCredentials && !hasWildcard {
w.Header().Set("Access-Control-Allow-Credentials", "true")
}
}
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }

View File

@@ -1,3 +1,5 @@
# screenshot
In this folder, you will find screenshots of the app. In this folder, you will find screenshots of the app.
Two kinds of screenshot here: Two kinds of screenshot here:

View File

@@ -6,13 +6,13 @@ if [ "$EUID" -ne 0 ]; then
exit 1 exit 1
fi fi
read -s "Do you want to install PostgreSQL 18? [y/N] " INSTALL_PG read -rp "Do you want to install PostgreSQL 18? [y/N] " INSTALL_PG
if [ "$INSTALL_PG" != "y" ]; then if [ "$INSTALL_PG" != "y" ]; then
echo "PostgreSQL 18 will not be installed" echo "PostgreSQL 18 will not be installed"
exit 0 exit 0
fi fi
read -s -p "Enter password for PostgreSQL user 'goyco': " GOYCO_PWD read -rsp "Enter password for PostgreSQL user 'goyco': " GOYCO_PWD
echo echo
apt-get update apt-get update