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
10 changes: 5 additions & 5 deletions back-end/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
! .env.example

# 🏗️ Build Binaries (Go)
api
api_server
verify
verify_transfer
worker
/api
/api_server
/verify
/verify_transfer
/worker
/bin/
*.exe
*.exe~
Expand Down
33 changes: 33 additions & 0 deletions back-end/cmd/api/middleware/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package middleware

import (
"os"

"github.com/gin-gonic/gin"
)

// CORSMiddleware sets CORS headers. Allowed origin is read from CORS_ORIGIN env var,
// defaults to the production domain.
func CORSMiddleware() gin.HandlerFunc {
allowedOrigin := os.Getenv("CORS_ORIGIN")
if allowedOrigin == "" {
allowedOrigin = "https://paycif.com"
}

return func(c *gin.Context) {
origin := c.GetHeader("Origin")
if origin == allowedOrigin || allowedOrigin == "*" {
c.Header("Access-Control-Allow-Origin", origin)
}
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Device-Id, X-Device-Signature, X-Request-ID")
c.Header("Access-Control-Max-Age", "86400")

if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}

c.Next()
}
}
21 changes: 21 additions & 0 deletions back-end/cmd/api/middleware/recovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package middleware

import (
"log/slog"
"net/http"

"github.com/gin-gonic/gin"
)

// Recovery returns a middleware that recovers from panics and returns 500.
func Recovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
slog.Error("Panic recovered", "error", err, "path", c.Request.URL.Path)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
}
}()
c.Next()
}
}
15 changes: 15 additions & 0 deletions back-end/cmd/api/middleware/security_headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package middleware

import "github.com/gin-gonic/gin"

// SecurityHeadersMiddleware adds standard security headers to every response.
func SecurityHeadersMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("X-XSS-Protection", "1; mode=block")
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
c.Header("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
c.Next()
}
}
3 changes: 3 additions & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BACKEND_URL=https://api.paycif.com
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your_anon_key_here