This commit is contained in:
mm 2024-09-07 16:42:56 +05:00
commit 812c9605f5
22 changed files with 798 additions and 0 deletions

44
.github/workflows/CI-CD.yml vendored Normal file
View File

@ -0,0 +1,44 @@
name: CI/CD
on:
push:
branches:
- main
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Docker Build
uses: docker/setup-buildx-action@v1
- name: Build and push the Docker image
uses: docker/build-push-action@v3
with:
push: true
tags: |
dockerhub-st.ru/donat-widget:latest
dockerhub-st.ru/donat-widget:${{ github.sha }}
# deploy:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v2
#
# - name: Install kubectl
# uses: azure/setup-kubectl@v1
# with:
# version: 'v1.21.0'
#
# - name: Configure kube-config
# run: |
# mkdir -p $HOME/.kube
# echo "${{ secrets.KUBE_CONFIG }}" | base64 --decode > $HOME/.kube/config
# export KUBECONFIG=$HOME/.kube/config
#
# - name: Update deployment
# run: |
# kubectl restart rollout deployment donat-user-deployment

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
config/config.yaml

8
Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM golang:1.23-alpine
WORKDIR /root
COPY . .
RUN go install github.com/swaggo/swag/cmd/swag@latest
RUN go mod tidy && swag init -g cmd/main.go -o cmd/docs
CMD go run cmd/main.go

99
cmd/docs/docs.go Normal file
View File

@ -0,0 +1,99 @@
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
const docTemplate = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"contact": {},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/api/user/register": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Auth"
],
"parameters": [
{
"description": "Register user",
"name": "input",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/register.requestModel"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/register.responseModel"
}
}
}
}
}
},
"definitions": {
"register.requestModel": {
"type": "object",
"required": [
"password"
],
"properties": {
"email": {
"type": "string"
},
"login": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"register.responseModel": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"userId": {
"type": "string"
}
}
}
}
}`
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{
Version: "1.0",
Host: "localhost",
BasePath: "/v2",
Schemes: []string{},
Title: "Template",
Description: "Описание.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate,
LeftDelim: "{{",
RightDelim: "}}",
}
func init() {
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
}

75
cmd/docs/swagger.json Normal file
View File

@ -0,0 +1,75 @@
{
"swagger": "2.0",
"info": {
"description": "Описание.",
"title": "Template",
"contact": {},
"version": "1.0"
},
"host": "localhost",
"basePath": "/v2",
"paths": {
"/api/user/register": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Auth"
],
"parameters": [
{
"description": "Register user",
"name": "input",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/register.requestModel"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/register.responseModel"
}
}
}
}
}
},
"definitions": {
"register.requestModel": {
"type": "object",
"required": [
"password"
],
"properties": {
"email": {
"type": "string"
},
"login": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"register.responseModel": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"userId": {
"type": "string"
}
}
}
}
}

48
cmd/docs/swagger.yaml Normal file
View File

@ -0,0 +1,48 @@
basePath: /v2
definitions:
register.requestModel:
properties:
email:
type: string
login:
type: string
password:
type: string
required:
- password
type: object
register.responseModel:
properties:
token:
type: string
userId:
type: string
type: object
host: localhost
info:
contact: {}
description: Описание.
title: Template
version: "1.0"
paths:
/api/user/register:
post:
consumes:
- application/json
parameters:
- description: Register user
in: body
name: input
required: true
schema:
$ref: '#/definitions/register.requestModel'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/register.responseModel'
tags:
- Auth
swagger: "2.0"

View File

@ -0,0 +1,46 @@
package create
import (
"context"
"github.com/labstack/echo/v4"
"log/slog"
"net/http"
)
// Register @Description Register user
//
// @Tags Auth
// @Accept json
// @Produce json
// @Param input body requestModel true "Register user"
// @Success 200 {object} responseModel
// @Router /api/user/register [post]
func Register(request echo.Context) error {
var registerData requestModel
// Получение данных
if err := request.Bind(&registerData); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
// Валидаций
err := request.Validate(&registerData)
if err != nil {
slog.Error(err.Error())
return request.JSON(422, err)
}
// reg
err = RegUserService(
context.Background(),
registerData,
)
if err != nil {
slog.Error(err.Error())
return request.JSON(500, err)
}
slog.Info("User Register success")
return request.JSON(http.StatusOK, "ok")
}

View File

@ -0,0 +1,34 @@
package create
import (
"context"
"fmt"
"github.com/jackc/pgx/v5"
pg "template/db/pg"
)
type User struct {
Id int `json:"id"`
Login string `json:"login"`
}
func RegUserRepository(
ctx context.Context,
login, email *string,
password string,
) error {
query := `
INSERT INTO users (login, password)
VALUES (@login, @password);
`
args := pgx.NamedArgs{
"login": login,
"password": password,
}
result, err := pg.Pool.Db.Exec(ctx, query, args)
if err != nil {
return err
}
fmt.Println(result)
return nil
}

View File

@ -0,0 +1,12 @@
package create
type requestModel struct {
Password string `json:"password" validate:"required"`
Login *string `json:"login"`
Email *string `json:"email"`
}
type responseModel struct {
Token string `json:"token"`
UserId string `json:"userId"`
}

View File

@ -0,0 +1,21 @@
package create
import (
"context"
)
func RegUserService(
ctx context.Context,
registerData requestModel,
) error {
err := RegUserRepository(
ctx,
registerData.Login,
registerData.Email,
registerData.Password,
)
if err != nil {
return err
}
return nil
}

10
cmd/handlers/router.go Normal file
View File

@ -0,0 +1,10 @@
package handlers
import (
"github.com/labstack/echo/v4"
"template/cmd/handlers/view"
)
func IncludeRouters(app *echo.Echo) {
app.POST("/api/widget/view", view.BannerView)
}

View File

@ -0,0 +1,63 @@
package view
import (
"context"
"github.com/labstack/echo/v4"
"log/slog"
"net/http"
)
// BannerView
//
// @Description View Widget
// @Tags Auth
// @Accept json
// @Produce json
// @Param input body requestModel true "Register user"
// @Success 200 {object} responseModel
// @Router /api/widget/view [post]
func BannerView(request echo.Context) error {
var registerData requestModel
// Получение данных
if err := request.Bind(&registerData); err != nil {
slog.Error(err.Error())
return echo.NewHTTPError(400, err.Error())
}
// Валидаций
err := request.Validate(&registerData)
if err != nil {
slog.Error(err.Error())
return request.JSON(422, err)
}
// reg
err = RegUserService(
context.Background(),
registerData,
)
if err != nil {
slog.Error(err.Error())
return request.JSON(500, err)
}
banner := `
<!DOCTYPE html>
<html>
<body>
<div id="content"></div>
<script>
function fetchData() {
const contentDiv = document.getElementById('content');
const img = document.createElement('img');
img.src = "https://filesharing-st.ru/api/storage/file/download/RATHFvTY/9";
contentDiv.appendChild(img);
}
setInterval(fetchData, 5000);
</script>
</body>
</html>
`
slog.Info("User Register success")
return request.HTML(http.StatusOK, banner)
}

View File

@ -0,0 +1,34 @@
package view
import (
"context"
"fmt"
"github.com/jackc/pgx/v5"
pg "template/db/pg"
)
type User struct {
Id int `json:"id"`
Login string `json:"login"`
}
func RegUserRepository(
ctx context.Context,
login, email *string,
password string,
) error {
query := `
INSERT INTO users (login, password)
VALUES (@login, @password);
`
args := pgx.NamedArgs{
"login": login,
"password": password,
}
result, err := pg.Pool.Db.Exec(ctx, query, args)
if err != nil {
return err
}
fmt.Println(result)
return nil
}

View File

@ -0,0 +1,12 @@
package view
type requestModel struct {
Password string `json:"password" validate:"required"`
Login *string `json:"login"`
Email *string `json:"email"`
}
type responseModel struct {
Token string `json:"token"`
UserId string `json:"userId"`
}

View File

@ -0,0 +1,21 @@
package view
import (
"context"
)
func RegUserService(
ctx context.Context,
registerData requestModel,
) error {
err := RegUserRepository(
ctx,
registerData.Login,
registerData.Email,
registerData.Password,
)
if err != nil {
return err
}
return nil
}

37
cmd/main.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"github.com/labstack/echo/v4"
"log/slog"
"os"
"template/cmd/handlers"
)
import (
_ "template/cmd/docs"
"template/lib/validator"
)
// @title Template
// @version 1.0
// @description Описание.
// @host localhost
// @BasePath /v2
func init() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
}
func main() {
app := initApp()
app.Logger.Fatal(app.Start(":8000"))
}
func initApp() *echo.Echo {
app := echo.New()
app.Validator = validator.NewValidator()
handlers.IncludeRouters(app)
return app
}

37
config/config.go Normal file
View File

@ -0,0 +1,37 @@
package config
import (
"gopkg.in/yaml.v2"
"os"
)
type Cfg struct {
Database DatabaseConfig `yaml:"db"`
}
type DatabaseConfig struct {
Username string `yaml:"username"`
Password string `yaml:"password"`
Host string `yaml:"host"`
Port string `yaml:"port"`
DBName string `yaml:"dbname"`
}
var Config Cfg
func InitConfig() error {
data, err := os.ReadFile("config/config.yaml")
if err != nil {
panic(err)
return err
}
err = yaml.Unmarshal(data, &Config)
if err != nil {
panic(err)
return err
}
return nil
}
var err = InitConfig()

61
db/pg/connection.go Normal file
View File

@ -0,0 +1,61 @@
package pg
import (
"context"
"fmt"
"github.com/jackc/pgx/v5/pgxpool"
"sync"
"template/config"
)
type Postgres struct {
Db *pgxpool.Pool
}
var (
pgInstance *Postgres
pgOnce sync.Once
)
func NewPgPool(
ctx context.Context,
username, password, host, port, dbname string,
) (*Postgres, error) {
connString := "postgres://" + username + ":" + password + "@" + host + ":" + port + "/" + dbname + "?pool_max_conns=100"
pgOnce.Do(func() {
db, err := pgxpool.New(ctx, connString)
if err != nil {
panic(err)
}
pgInstance = &Postgres{db}
})
return pgInstance, nil
}
func (pg *Postgres) Ping(ctx context.Context) error {
err := pg.Db.Ping(ctx)
return err
}
func (pg *Postgres) Close() {
pg.Db.Close()
}
func (pg *Postgres) CreateTable(ctx context.Context) error {
result, err := pg.Db.Exec(ctx, createTableQuery)
if err != nil {
return err
}
fmt.Println(result)
return nil
}
var Pool, _ = NewPgPool(
context.Background(),
config.Config.Database.Username,
config.Config.Database.Password,
config.Config.Database.Host,
config.Config.Database.Port,
config.Config.Database.DBName,
)

12
db/pg/models.go Normal file
View File

@ -0,0 +1,12 @@
package pg
var createTableQuery = `
CREATE TABLE users (
id INTEGER PRIMARY KEY,
login VARCHAR(255) UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
key_2fa VARCHAR(255) DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)`

46
go.mod Normal file
View File

@ -0,0 +1,46 @@
module template
go 1.23.0
require (
github.com/go-playground/validator/v10 v10.22.0
github.com/jackc/pgx/v5 v5.6.0
github.com/jcobhams/echovalidate/v2 v2.0.3
github.com/labstack/echo/v4 v4.12.0
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/swag v1.16.3
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

50
go.sum Normal file
View File

@ -0,0 +1,50 @@
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jcobhams/echovalidate/v2 v2.0.3/go.mod h1:I9VVW+j88qwoJC0ugb9B2Olvc2w+RN66oDyrVaT+Gns=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,25 @@
package validator
import (
"errors"
"strings"
validator "github.com/go-playground/validator/v10"
)
type Validator struct {
validator *validator.Validate
}
func NewValidator() *Validator {
return &Validator{validator: validator.New()}
}
func (val *Validator) Validate(i interface{}) error {
err := val.validator.Struct(i)
if err == nil {
return nil
}
err = errors.New(strings.Replace(err.Error(), "\n", ", ", -1))
return err
}