init
This commit is contained in:
@@ -0,0 +1,246 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"authorization/db"
|
||||
"authorization/docs"
|
||||
"authorization/helper"
|
||||
"authorization/models"
|
||||
"authorization/redisclient"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"authorization/routes"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
// @swagger: "2.0"
|
||||
// @title UESS Authentication Microservice
|
||||
// @version 1.0
|
||||
|
||||
// @description This is the API for Authentication Microservice for UESS. It doesn't support OAS 3.0 and is only for documentation purposes. The library used doesn't support @server annotation.
|
||||
// @contact.name Darrel Israel
|
||||
// @contact.email d.israel.psa@gmail.com
|
||||
|
||||
// @BasePath /
|
||||
|
||||
// @securityDefinitions.apikey BearerToken
|
||||
// @in header
|
||||
// @name Authorization
|
||||
|
||||
var (
|
||||
dbOpenConnections = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_open_connections",
|
||||
Help: "Number of open database connections",
|
||||
})
|
||||
dbInUseConnections = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_in_use_connections",
|
||||
Help: "Number of in-use database connections",
|
||||
})
|
||||
dbIdleConnections = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_idle_connections",
|
||||
Help: "Number of idle database connections",
|
||||
})
|
||||
)
|
||||
|
||||
var (
|
||||
httpRequestsTotal = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "http_requests_total",
|
||||
Help: "Total number of HTTP requests",
|
||||
},
|
||||
[]string{"path", "method"},
|
||||
)
|
||||
httpRequestDuration = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "http_request_duration_seconds",
|
||||
Help: "Duration of HTTP requests in seconds",
|
||||
Buckets: prometheus.DefBuckets,
|
||||
},
|
||||
[]string{"path", "method"},
|
||||
)
|
||||
httpRequestSize = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "http_request_size_bytes",
|
||||
Help: "Size of HTTP requests in bytes",
|
||||
Buckets: prometheus.ExponentialBuckets(100, 10, 8),
|
||||
},
|
||||
[]string{"path", "method"},
|
||||
)
|
||||
httpResponseSize = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "http_response_size_bytes",
|
||||
Help: "Size of HTTP responses in bytes",
|
||||
Buckets: prometheus.ExponentialBuckets(100, 10, 8),
|
||||
},
|
||||
[]string{"path", "method"},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(httpRequestsTotal)
|
||||
prometheus.MustRegister(httpRequestDuration)
|
||||
prometheus.MustRegister(httpRequestSize)
|
||||
prometheus.MustRegister(httpResponseSize)
|
||||
prometheus.MustRegister(dbOpenConnections)
|
||||
prometheus.MustRegister(dbInUseConnections)
|
||||
prometheus.MustRegister(dbIdleConnections)
|
||||
}
|
||||
|
||||
func loggingMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
|
||||
if r.URL.Path != metricsPath {
|
||||
helper.LogInfo(fmt.Sprintf("INFO: Started %s %s", r.Method, r.URL.Path))
|
||||
}
|
||||
|
||||
httpRequestsTotal.WithLabelValues(r.URL.Path, r.Method).Inc()
|
||||
|
||||
requestSize := float64(r.ContentLength)
|
||||
if requestSize < 0 {
|
||||
requestSize = 0
|
||||
}
|
||||
httpRequestSize.WithLabelValues(r.URL.Path, r.Method).Observe(requestSize)
|
||||
|
||||
rw := &models.ResponseWriter{ResponseWriter: w}
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
duration := time.Since(start).Seconds()
|
||||
httpRequestDuration.WithLabelValues(r.URL.Path, r.Method).Observe(duration)
|
||||
httpResponseSize.WithLabelValues(r.URL.Path, r.Method).Observe(float64(rw.Size))
|
||||
|
||||
// Log completion for non-metrics endpoints
|
||||
if r.URL.Path != metricsPath {
|
||||
helper.LogInfo(fmt.Sprintf("INFO: Completed %s %s in %.3f seconds", r.Method, r.URL.Path, duration))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func collectDBMetrics(database *sql.DB) {
|
||||
for {
|
||||
stats := database.Stats()
|
||||
dbOpenConnections.Set(float64(stats.OpenConnections))
|
||||
dbInUseConnections.Set(float64(stats.InUse))
|
||||
dbIdleConnections.Set(float64(stats.Idle))
|
||||
time.Sleep(10 * time.Second) // Adjust the interval as needed
|
||||
}
|
||||
}
|
||||
|
||||
func allowOnlyGrafana(next http.Handler, allowedIP string) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if remoteIP == allowedIP {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file first
|
||||
// Get current working directory for debugging
|
||||
cwd, _ := os.Getwd()
|
||||
log.Printf("Current working directory: %s", cwd)
|
||||
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Failed to load .env file from default location: %v", err)
|
||||
// Try with explicit path
|
||||
err = godotenv.Load(".env")
|
||||
if err != nil {
|
||||
log.Fatalf("FATAL: Could not load .env file: %v. Tried paths: default and ./.env", err)
|
||||
}
|
||||
log.Println(".env file loaded successfully from ./.env")
|
||||
} else {
|
||||
log.Println(".env file loaded successfully")
|
||||
}
|
||||
|
||||
// Verify GO_ENV is loaded
|
||||
goEnv := os.Getenv("GO_ENV")
|
||||
log.Printf("GO_ENV value after loading .env: '%s'", goEnv)
|
||||
|
||||
if goEnv == "" {
|
||||
log.Fatal("GO_ENV is not set in main. Please set the GO_ENV environment variable.")
|
||||
}
|
||||
|
||||
DSN := os.Getenv("DSN")
|
||||
if DSN == "" {
|
||||
log.Fatal("Sentry DSN is not set. Please set the DSN environment variable.")
|
||||
}
|
||||
|
||||
err = sentry.Init(sentry.ClientOptions{
|
||||
Dsn: os.Getenv("DSN"),
|
||||
TracesSampleRate: 1.0,
|
||||
Environment: goEnv,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("sentry.Init: %s", err)
|
||||
}
|
||||
defer sentry.Flush(2 * time.Second)
|
||||
|
||||
docs.SwaggerInfo.Host = "localhost:8080"
|
||||
docs.SwaggerInfo.Schemes = []string{"http"}
|
||||
|
||||
helper.LogInfo("INFO: Initializing database connection...")
|
||||
var database *sql.DB
|
||||
for {
|
||||
database, err = db.InitDB()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
helper.LogError(fmt.Errorf("ERROR: error initializing database: %v", err), "database initialization error")
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
go collectDBMetrics(database)
|
||||
router := mux.NewRouter()
|
||||
routes.SetupRoutes(router, database)
|
||||
helper.LogInfo("INFO: Database initialized successfully.")
|
||||
|
||||
allowedIP := os.Getenv("ALLOWED_IP")
|
||||
helper.LogInfo("INFO: Setting up routes...")
|
||||
router.Handle(metricsPath, allowOnlyGrafana(promhttp.Handler(), allowedIP))
|
||||
router.Use(loggingMiddleware)
|
||||
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"http://localhost:4173", "http://localhost:5173"}, // Your frontend URL
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"},
|
||||
AllowedHeaders: []string{"*"}, // Allow all headers temporarily
|
||||
AllowCredentials: true, // Critical for withCredentials requests
|
||||
MaxAge: 86400, // Cache preflight results
|
||||
})
|
||||
|
||||
handler := c.Handler(router)
|
||||
|
||||
redisclient.Init()
|
||||
|
||||
helper.LogInfo("INFO: Connected to Redis successfully!")
|
||||
|
||||
helper.LogInfo("WARNING: Ensure Redis is secured to prevent unauthorized access. Use a strong password and bind Redis to localhost or a secure network.")
|
||||
|
||||
helper.LogInfo("INFO: Authentication Microservice is running on http://localhost:8080")
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: handler,
|
||||
ReadTimeout: 15 * time.Second,
|
||||
WriteTimeout: 300 * time.Second,
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
log.Fatal(server.ListenAndServe())
|
||||
}
|
||||
Reference in New Issue
Block a user