first commit
This commit is contained in:
131
internal/pkg/response/response.go
Normal file
131
internal/pkg/response/response.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"math"
|
||||
"net/http"
|
||||
|
||||
"cardverse/internal/pkg/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Response adalah struktur baku untuk seluruh response API
|
||||
type Response struct {
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Meta interface{} `json:"meta,omitempty"`
|
||||
Error interface{} `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Pagination berisi metadata pagination yang dikirim di field "meta"
|
||||
type Pagination struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
TotalData int64 `json:"total_data"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
HasNext bool `json:"has_next"`
|
||||
HasPrev bool `json:"has_prev"`
|
||||
}
|
||||
|
||||
// NewPagination menghitung metadata pagination dari page, limit, dan total data.
|
||||
// Dipakai di service/handler setelah query COUNT(*) ke database.
|
||||
func NewPagination(page, limit int, totalData int64) Pagination {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if limit < 1 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
totalPages := int(math.Ceil(float64(totalData) / float64(limit)))
|
||||
if totalPages < 1 {
|
||||
totalPages = 1
|
||||
}
|
||||
|
||||
return Pagination{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
TotalData: totalData,
|
||||
TotalPages: totalPages,
|
||||
HasNext: page < totalPages,
|
||||
HasPrev: page > 1,
|
||||
}
|
||||
}
|
||||
|
||||
func Success(c *gin.Context, statusCode int, message string, data interface{}) {
|
||||
c.JSON(statusCode, Response{
|
||||
Message: message,
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
func SuccessWithPagination(c *gin.Context, statusCode int, message string, data interface{}, pagination Pagination) {
|
||||
c.JSON(statusCode, Response{
|
||||
Message: message,
|
||||
Data: data,
|
||||
Meta: pagination,
|
||||
})
|
||||
}
|
||||
|
||||
var defaultErrorMessages = map[int]string{
|
||||
http.StatusUnauthorized: "Anda tidak memiliki akses, silakan login kembali",
|
||||
http.StatusForbidden: "Anda tidak memiliki izin untuk melakukan aksi ini",
|
||||
http.StatusNotFound: "Data yang diminta tidak ditemukan",
|
||||
http.StatusConflict: "Data sudah ada atau bertentangan dengan data yang tersimpan",
|
||||
http.StatusUnprocessableEntity: "Data yang dikirim tidak dapat diproses",
|
||||
http.StatusTooManyRequests: "Terlalu banyak request, silakan coba lagi nanti",
|
||||
http.StatusInternalServerError: "Terjadi kesalahan pada server, silakan coba lagi nanti",
|
||||
http.StatusBadGateway: "Layanan sedang tidak tersedia, silakan coba lagi nanti",
|
||||
http.StatusServiceUnavailable: "Layanan sedang tidak tersedia, silakan coba lagi nanti",
|
||||
}
|
||||
|
||||
const fallbackErrorMessage = "Terjadi kesalahan, silakan coba lagi nanti"
|
||||
|
||||
// Error mengirim response gagal ke client.
|
||||
//
|
||||
// - Untuk 400 Bad Request: message & err (data) dikirim apa adanya ke client — biasanya
|
||||
// cuma detail validasi input, aman untuk membantu client memperbaiki request-nya.
|
||||
// - Untuk status lain (401, 403, 404, 409, 500, dst): client HANYA menerima pesan generik
|
||||
// dari defaultErrorMessages, TIDAK menerima err/message asli sama sekali. Detail asli
|
||||
// (message & err yang dikirim caller) dicatat lewat logger supaya tetap bisa di-debug
|
||||
// dari log server tanpa membocorkannya ke response API.
|
||||
func Error(c *gin.Context, statusCode int, message string, err interface{}) {
|
||||
if statusCode == http.StatusBadRequest {
|
||||
c.JSON(statusCode, Response{
|
||||
Message: message,
|
||||
Error: err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
logFields := logger.Fields{
|
||||
"method": c.Request.Method,
|
||||
"path": c.Request.URL.Path,
|
||||
"status": statusCode,
|
||||
"ip": c.ClientIP(),
|
||||
"detail": message,
|
||||
}
|
||||
if err != nil {
|
||||
logFields["error"] = err
|
||||
}
|
||||
|
||||
if statusCode >= http.StatusInternalServerError {
|
||||
logger.Error("request gagal diproses", logFields)
|
||||
} else {
|
||||
logger.Warn("request ditolak", logFields)
|
||||
}
|
||||
|
||||
clientMessage := message
|
||||
|
||||
if clientMessage == "" {
|
||||
if msg, ok := defaultErrorMessages[statusCode]; ok {
|
||||
clientMessage = msg
|
||||
} else {
|
||||
clientMessage = fallbackErrorMessage
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(statusCode, Response{
|
||||
Message: clientMessage,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user