Initial commit: dating app backend (Go/Gin/GORM/Postgres)

Phone+OTP auth, staged onboarding, admin approval workflow, and a
full discovery/swipe/match/chat system with photo upload and
approximate distance (haversine) based on user location.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 23:23:47 +03:30
commit 2823fb45f5
49 changed files with 4582 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
"dating-app/internal/models"
"dating-app/internal/utils"
)
// AdminRequired باید بعد از AuthRequired استفاده بشه. فقط کاربرانی که IsAdmin=true
// دارن اجازه‌ی عبور دارن.
func AdminRequired() gin.HandlerFunc {
return func(c *gin.Context) {
userVal, exists := c.Get(ContextUserKey)
if !exists {
utils.Fail(c, http.StatusUnauthorized, "احراز هویت لازم است", nil)
c.Abort()
return
}
user, ok := userVal.(*models.User)
if !ok || !user.IsAdmin {
utils.Fail(c, http.StatusForbidden, "دسترسی فقط برای ادمین است", nil)
c.Abort()
return
}
c.Next()
}
}
+51
View File
@@ -0,0 +1,51 @@
package middleware
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"dating-app/internal/repository"
"dating-app/internal/utils"
)
const (
ContextUserKey = "current_user"
)
// AuthRequired توکن access رو از هدر Authorization می‌خونه، اعتبارسنجی می‌کنه
// و کاربر مربوطه رو توی context قرار می‌ده.
func AuthRequired(jwtManager *utils.JWTManager, userRepo *repository.UserRepository) gin.HandlerFunc {
return func(c *gin.Context) {
header := c.GetHeader("Authorization")
if header == "" || !strings.HasPrefix(header, "Bearer ") {
utils.Fail(c, http.StatusUnauthorized, "توکن احراز هویت ارسال نشده است", nil)
c.Abort()
return
}
tokenStr := strings.TrimPrefix(header, "Bearer ")
claims, err := jwtManager.ParseAccessToken(tokenStr)
if err != nil {
utils.Fail(c, http.StatusUnauthorized, "توکن نامعتبر یا منقضی شده است", nil)
c.Abort()
return
}
user, err := userRepo.FindByID(claims.UserID)
if err != nil {
utils.Fail(c, http.StatusUnauthorized, "کاربر یافت نشد", nil)
c.Abort()
return
}
if !user.IsActive {
utils.Fail(c, http.StatusForbidden, "حساب کاربری غیرفعال است", nil)
c.Abort()
return
}
c.Set(ContextUserKey, user)
c.Next()
}
}
+23
View File
@@ -0,0 +1,23 @@
package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
// CORS یک میان‌افزار ساده برای اجازه دادن به درخواست‌های Cross-Origin
// (مناسب برای توسعه؛ در پروداکشن origin ها رو محدود کن)
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization")
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}