package api
import (
"net/http"
"unicode"
"github.com/gin-gonic/gin"
)
var CONFIG Config
var MAIL_CONFIG MailConfig
type MailConfig struct {
IsEnabled bool
SmtpAddress string `toml:"smtp_address"`
AddressFrom string `toml:"address_from"`
Login string `toml:"login"`
Password string `toml:"password"`
}
type Config struct {
Domain string `toml:"domain"`
JwtSecret string `toml:"jwt_secret"`
PasswordResetSecret string `toml:"password_reset_secret"`
FreeSMSUser string `toml:"free_sms_user"`
FreeSMSPass string `toml:"free_sms_pass"`
}
func SetConfig(config Config) {
CONFIG = config
JWT_SECRET = []byte(config.JwtSecret)
PASSWORD_RESET_SECRET = []byte(config.PasswordResetSecret)
}
func SetMailConfig(config MailConfig) {
MAIL_CONFIG = config
if config.Login != "" && config.AddressFrom != "" && config.Password != "" && config.SmtpAddress != "" {
MAIL_CONFIG.IsEnabled = true
ConfigureMailTemplateEngine()
}
}
type Error struct {
Code ErrorCode
UserMessage string
err error
}
func (e Error) Err() error {
if e.err != nil {
return e
}
return nil
}
func (e Error) Error() string {
if e.err != nil {
return e.err.Error()
}
return ""
}
func ErrorIs(err error, code ErrorCode) bool {
if err == nil {
return false
}
if apiError, ok := err.(*Error); !ok {
return false
} else {
return apiError.Code == code
}
}
func NewInternalError(err error) *Error {
if apiError, ok := err.(*Error); ok {
return apiError
}
return &Error{InternalError, "internal error", err}
}
func ToSnake(in string) string {
runes := []rune(in)
length := len(runes)
var out []rune
for i := 0; i < length; i++ {
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
out = append(out, '_')
}
out = append(out, unicode.ToLower(runes[i]))
}
return string(out)
}
type Response struct {
StatusCode Status `json:"-"`
ErrorCode ErrorCode `json:"-"`
Status string `json:"status"`
Code string `json:"code,omitempty"`
Response interface{} `json:"response,omitempty"`
Message string `json:"message,omitempty"`
}
func (r Response) populateStatus() Response {
r.Status = ToSnake(r.StatusCode.String())
if r.ErrorCode != 0 {
r.Code = ToSnake(r.ErrorCode.String())
}
return r
}
func ErrorResponse(code ErrorCode, message string) Response {
return Response{
StatusCode: NOK,
ErrorCode: code,
Message: message,
}
}
func SuccessResponse(data interface{}) Response {
return Response{
StatusCode: OK,
Response: data,
}
}
func WriteJsonResponse(response Response, c *gin.Context) {
response = response.populateStatus()
c.JSON(StatusToHttpCode(response.StatusCode, response.ErrorCode), response)
}
func WriteBinary(contentType string, b []byte, c *gin.Context) {
c.Data(http.StatusOK, contentType, b)
}
type Middleware func(*gin.Context) *Error
func M(handler Middleware) gin.HandlerFunc {
return func(c *gin.Context) {
err := handler(c)
if err != nil {
WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c)
c.Error(err)
c.Abort()
} else {
c.Next()
}
}
}
type Query interface {
ValidateParams() *Error
Run() (interface{}, *Error)
}
func RunQuery(query Query, c *gin.Context) {
if err := query.ValidateParams(); err != nil {
WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c)
c.Error(err)
return
}
if out, err := query.Run(); err != nil {
WriteJsonResponse(ErrorResponse(err.Code, err.UserMessage), c)
c.Error(err)
} else {
WriteJsonResponse(SuccessResponse(out), c)
}
}