Initial commit
This commit is contained in:
commit
c4912af8fb
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
||||
7
app/constants/errors.go
Normal file
7
app/constants/errors.go
Normal file
@ -0,0 +1,7 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
AUTH_FAILED = "User with specified email and password not found"
|
||||
NOT_FOUND = "Wrong request method or path"
|
||||
UNAUTHORIZED = "Unauthorized"
|
||||
)
|
||||
5
app/constants/misc.go
Normal file
5
app/constants/misc.go
Normal file
@ -0,0 +1,5 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
JWT_APP_ISS = "stc"
|
||||
)
|
||||
15
app/controllers/auth.go
Normal file
15
app/controllers/auth.go
Normal file
@ -0,0 +1,15 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"system-trace/core/auth"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func InitAuth(app *fiber.App, version *fiber.Router) {
|
||||
v := *version
|
||||
g := v.Group("/auth")
|
||||
{
|
||||
g.Post("/login", auth.ReqTokens)
|
||||
}
|
||||
}
|
||||
17
app/router/router.go
Normal file
17
app/router/router.go
Normal file
@ -0,0 +1,17 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"system-trace/core/app/controllers"
|
||||
_ "system-trace/core/docs"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/swagger"
|
||||
)
|
||||
|
||||
func Init(app *fiber.App) {
|
||||
app.Get("/swagger/*", swagger.HandlerDefault)
|
||||
v1 := app.Group("/v1")
|
||||
{
|
||||
controllers.InitAuth(app, &v1)
|
||||
}
|
||||
}
|
||||
51
app/server.go
Normal file
51
app/server.go
Normal file
@ -0,0 +1,51 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"system-trace/core/app/constants"
|
||||
"system-trace/core/app/router"
|
||||
"system-trace/core/auth/middlewares"
|
||||
"system-trace/core/environment"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/gofiber/fiber/v2/middleware/recover"
|
||||
)
|
||||
|
||||
var Instance *fiber.App
|
||||
|
||||
func Serve() {
|
||||
Instance = initServer()
|
||||
Instance.Use(recover.New())
|
||||
Instance.Use(cors.New())
|
||||
Instance.Use(middlewares.ValidateSession)
|
||||
router.Init(Instance)
|
||||
Instance.Use(func(c *fiber.Ctx) error {
|
||||
return c.
|
||||
Status(fiber.StatusNotFound).
|
||||
JSON(fiber.Map{
|
||||
"error": constants.NOT_FOUND,
|
||||
})
|
||||
})
|
||||
Instance.Listen(":" + os.Getenv("APP_PORT"))
|
||||
}
|
||||
|
||||
func initServer() *fiber.App {
|
||||
debug := environment.IsDebug()
|
||||
|
||||
return fiber.New(fiber.Config{
|
||||
JSONEncoder: json.Marshal,
|
||||
JSONDecoder: json.Unmarshal,
|
||||
CaseSensitive: true,
|
||||
EnablePrintRoutes: debug,
|
||||
Prefork: true,
|
||||
AppName: "System Trace API",
|
||||
ErrorHandler: func(c *fiber.Ctx, err error) error {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
11
auth/README.md
Normal file
11
auth/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
## Password requirements:
|
||||
- Minimum 1 uppercase character
|
||||
- Minimum 1 lowercase character
|
||||
- Minimum 1 digit
|
||||
- Minimum 1 special character
|
||||
- Minimal length 8 characters
|
||||
|
||||
### Auth request return Set-Cookie headers to set server-cookie on client
|
||||
### AccessToken lifetime is 1 hour
|
||||
### RefreshToken lifetime is 24 hours
|
||||
### When AccessToken expires, RefreshToken requests a new one. Old pair becomes revoked and creates new one
|
||||
77
auth/auth.go
Normal file
77
auth/auth.go
Normal file
@ -0,0 +1,77 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"system-trace/core/app/constants"
|
||||
"system-trace/core/users"
|
||||
"system-trace/core/validators"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
// ReqTokens godoc
|
||||
// @Summary Request pair of tokens
|
||||
// @Description Returns pair of access and refresh tokens
|
||||
// @Tags auth
|
||||
// @Produce json
|
||||
// @Param request body AuthBody true "Request body"
|
||||
// @Header 200 {string} Token "accessToken=..."
|
||||
// @Header 200 {string} Set-Cookie "refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /auth/login [post]
|
||||
func ReqTokens(c *fiber.Ctx) error {
|
||||
ab := new(AuthBody)
|
||||
if err := c.BodyParser(ab); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validators.Validate(c, ab); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u, err := users.FindByEmailAndPassword(ab.Email, ab.Password)
|
||||
if err != nil {
|
||||
e := err.Error()
|
||||
if e == "sql: no rows in result set" {
|
||||
e = constants.AUTH_FAILED
|
||||
}
|
||||
return c.
|
||||
Status(fiber.StatusBadRequest).
|
||||
JSON(fiber.Map{
|
||||
"error": e,
|
||||
})
|
||||
}
|
||||
if u != nil {
|
||||
p, err := genPair(u)
|
||||
if err != nil {
|
||||
return c.
|
||||
Status(fiber.StatusBadRequest).
|
||||
JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
setCookie(c, p)
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
return errors.New(constants.AUTH_FAILED)
|
||||
}
|
||||
|
||||
func setCookie(c *fiber.Ctx, p *PairTokens) {
|
||||
// Access token
|
||||
atc := new(fiber.Cookie)
|
||||
atc.Name = "accessToken"
|
||||
atc.Value = p.AccessToken
|
||||
atc.Expires = time.Now().Add(time.Duration(AccessTokenLifetime) * time.Hour)
|
||||
atc.Secure = true
|
||||
atc.HTTPOnly = true
|
||||
c.Cookie(atc)
|
||||
// Refresh token
|
||||
rtc := new(fiber.Cookie)
|
||||
rtc.Name = "refreshToken"
|
||||
rtc.Value = p.RefreshToken
|
||||
rtc.Expires = time.Now().Add(time.Duration(RefreshTokenLifetime) * time.Hour)
|
||||
rtc.Secure = true
|
||||
rtc.HTTPOnly = true
|
||||
c.Cookie(rtc)
|
||||
}
|
||||
40
auth/middlewares/session.go
Normal file
40
auth/middlewares/session.go
Normal file
@ -0,0 +1,40 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"system-trace/core/app/constants"
|
||||
"system-trace/core/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func ValidateSession(c *fiber.Ctx) error {
|
||||
header := c.GetReqHeaders()[http.CanonicalHeaderKey("Authorization")]
|
||||
if len(header) <= 0 || len(header[0]) <= 0 || !validateToken(c, header[0]) {
|
||||
return c.Status(http.StatusForbidden).JSON(fiber.Map{
|
||||
"error": constants.UNAUTHORIZED,
|
||||
})
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
func validateToken(c *fiber.Ctx, hash string) bool {
|
||||
splitted := strings.Split(hash, " ")
|
||||
if len(splitted) <= 1 {
|
||||
return false
|
||||
}
|
||||
claims, err := utils.ValidateJWT(splitted[1])
|
||||
fmt.Println(claims, err)
|
||||
// id, ok := claims["ID"].(string)
|
||||
// TODO validate date and check refresh token
|
||||
if err != nil || claims["iss"] != constants.JWT_APP_ISS {
|
||||
return false
|
||||
}
|
||||
|
||||
// c.Locals("userId", id)
|
||||
|
||||
return true
|
||||
}
|
||||
76
auth/tokens.go
Normal file
76
auth/tokens.go
Normal file
@ -0,0 +1,76 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"system-trace/core/app/constants"
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/database/entities"
|
||||
"system-trace/core/utils"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
const (
|
||||
AccessTokenLifetime int8 = 3
|
||||
RefreshTokenLifetime int8 = 24
|
||||
)
|
||||
|
||||
func genPair(u *entities.User) (*PairTokens, error) {
|
||||
at, rt, err := genTokens(u)
|
||||
|
||||
err = insertPair(u.ID, at, rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := PairTokens{
|
||||
AccessToken: at,
|
||||
RefreshToken: rt,
|
||||
}
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func genTokens(u *entities.User) (string, string, error) {
|
||||
at, err := genToken(fmt.Sprintf("%d", u.ID), AccessTokenLifetime)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
rt, err := genToken(at, RefreshTokenLifetime)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return at, rt, nil
|
||||
}
|
||||
|
||||
func genToken(sub string, hours int8) (string, error) {
|
||||
fmt.Println(sub, hours)
|
||||
c := jwt.MapClaims{
|
||||
"iss": constants.JWT_APP_ISS,
|
||||
"sub": sub,
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(time.Duration(hours) * time.Hour).Unix(),
|
||||
}
|
||||
a, err := utils.SignJWT(c)
|
||||
|
||||
return a, err
|
||||
}
|
||||
|
||||
func insertPair(id int32, at, rt string) error {
|
||||
aut := entities.AuthToken{
|
||||
UserID: id,
|
||||
AccessToken: at,
|
||||
RefreshToken: rt,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
_, err := database.PG.NewInsert().
|
||||
Model(&aut).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
11
auth/types.go
Normal file
11
auth/types.go
Normal file
@ -0,0 +1,11 @@
|
||||
package auth
|
||||
|
||||
type PairTokens struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
type AuthBody struct {
|
||||
Email string `json:"email" validate:"required,email" example:"john@proton.mail"`
|
||||
Password string `json:"password" validate:"required,password" example:"Aasdfg1!"`
|
||||
}
|
||||
80
database/database.go
Normal file
80
database/database.go
Normal file
@ -0,0 +1,80 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"system-trace/core/database/entities"
|
||||
"system-trace/core/environment"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect/pgdialect"
|
||||
"github.com/uptrace/bun/driver/pgdriver"
|
||||
"github.com/uptrace/bun/extra/bundebug"
|
||||
)
|
||||
|
||||
var PG *bun.DB
|
||||
|
||||
func Connect() {
|
||||
pgconn := pgdriver.NewConnector(
|
||||
pgdriver.WithNetwork("tcp"),
|
||||
pgdriver.WithAddr(fmt.Sprintf("%s:%s", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"))),
|
||||
pgdriver.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
|
||||
pgdriver.WithInsecure(true),
|
||||
pgdriver.WithUser(os.Getenv("DB_USER")),
|
||||
pgdriver.WithPassword(os.Getenv("DB_PASS")),
|
||||
pgdriver.WithDatabase(os.Getenv("DB_NAME")),
|
||||
pgdriver.WithApplicationName("st-controller"),
|
||||
pgdriver.WithTimeout(5*time.Second),
|
||||
pgdriver.WithDialTimeout(5*time.Second),
|
||||
pgdriver.WithReadTimeout(5*time.Second),
|
||||
pgdriver.WithWriteTimeout(5*time.Second),
|
||||
)
|
||||
|
||||
sqldb := sql.OpenDB(pgconn)
|
||||
db := bun.NewDB(sqldb, pgdialect.New())
|
||||
|
||||
if environment.IsDebug() {
|
||||
db.AddQueryHook(bundebug.NewQueryHook(
|
||||
bundebug.WithVerbose(true),
|
||||
bundebug.FromEnv("BUNDEBUG"),
|
||||
))
|
||||
}
|
||||
|
||||
if !fiber.IsChild() {
|
||||
err := createSchema(db)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
PG = db
|
||||
}
|
||||
|
||||
func createSchema(db *bun.DB) error {
|
||||
models := []interface{}{
|
||||
(*entities.Group)(nil),
|
||||
(*entities.GroupPermission)(nil),
|
||||
(*entities.User)(nil),
|
||||
(*entities.Session)(nil),
|
||||
(*entities.Server)(nil),
|
||||
(*entities.AuthToken)(nil),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
for _, model := range models {
|
||||
_, err := db.
|
||||
NewCreateTable().
|
||||
Model(model).
|
||||
IfNotExists().
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
8
database/entities/auth_token.go
Normal file
8
database/entities/auth_token.go
Normal file
@ -0,0 +1,8 @@
|
||||
package entities
|
||||
|
||||
type AuthToken struct {
|
||||
UserID int32
|
||||
AccessToken string `bun:"type:varchar"`
|
||||
RefreshToken string `bun:"type:varchar"`
|
||||
IsRevoked bool `bun:",default:false"`
|
||||
}
|
||||
16
database/entities/group.go
Normal file
16
database/entities/group.go
Normal file
@ -0,0 +1,16 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
type Group struct {
|
||||
ID int32 `bun:",pk,autoincrement"`
|
||||
IssuerID int32 `bun:",notnull"`
|
||||
Issuer *User `bun:"rel:belongs-to,join:issuer_id=id"`
|
||||
Name string `bun:",notnull,unique"`
|
||||
GroupID int32 `bun:",notnull"`
|
||||
Users []*User `bun:"rel:has-many,join:id=group_id"`
|
||||
Permissions []*GroupPermission `bun:"rel:has-many,join:id=group_id"`
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time `bun:",soft_delete,nullzero"`
|
||||
}
|
||||
6
database/entities/permission.go
Normal file
6
database/entities/permission.go
Normal file
@ -0,0 +1,6 @@
|
||||
package entities
|
||||
|
||||
type GroupPermission struct {
|
||||
GroupID int32 `bun:",notnull" json:"-"`
|
||||
Value int8 `bun:",notnull"`
|
||||
}
|
||||
30
database/entities/server.go
Normal file
30
database/entities/server.go
Normal file
@ -0,0 +1,30 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ID int64 `bun:",pk,autoincrement"`
|
||||
Hostname string `bun:",notnull"`
|
||||
IP string `bun:",notnull"`
|
||||
IsOnline bool `bun:",notnull,default:true"`
|
||||
LastOnline time.Time
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
var _ bun.BeforeAppendModelHook = (*Server)(nil)
|
||||
|
||||
func (s *Server) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
switch query.(type) {
|
||||
case *bun.InsertQuery:
|
||||
s.LastOnline = time.Now()
|
||||
case *bun.UpdateQuery:
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
29
database/entities/session.go
Normal file
29
database/entities/session.go
Normal file
@ -0,0 +1,29 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
ID int64 `bun:",pk,autoincrement"`
|
||||
IssuerID int32 `bun:",notnull"`
|
||||
Issuer *User `bun:"rel:belongs-to,join:issuer_id=id"`
|
||||
Configuration map[string]interface{} `bun:"type:jsonb"`
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp"`
|
||||
FinishedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time `bun:",soft_delete,nullzero"`
|
||||
}
|
||||
|
||||
var _ bun.BeforeAppendModelHook = (*Session)(nil)
|
||||
|
||||
func (s *Session) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
switch query.(type) {
|
||||
case *bun.UpdateQuery:
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
34
database/entities/user.go
Normal file
34
database/entities/user.go
Normal file
@ -0,0 +1,34 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int32 `bun:",pk,autoincrement"`
|
||||
Email string `bun:",notnull,unique"`
|
||||
PasswordHash string `bun:",notnull,type:varchar(64)"`
|
||||
PasswordLength int8 `bun:",notnull"`
|
||||
RealName string `bun:",notnull"`
|
||||
GroupID int32 `bun:",notnull"`
|
||||
Group *Group `bun:"rel:belongs-to,join:group_id=id"`
|
||||
IsRequiredToSetPassword bool `bun:",notnull,default:true"`
|
||||
IsActive bool `bun:",notnull,default:true"`
|
||||
LastLogin time.Time
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp"`
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time `bun:",soft_delete,nullzero"`
|
||||
}
|
||||
|
||||
var _ bun.BeforeAppendModelHook = (*User)(nil)
|
||||
|
||||
func (u *User) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
switch query.(type) {
|
||||
case *bun.UpdateQuery:
|
||||
u.UpdatedAt = time.Now()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
93
docs/docs.go
Normal file
93
docs/docs.go
Normal file
@ -0,0 +1,93 @@
|
||||
// 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": {
|
||||
"name": "https://peresvet.it"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/auth/login": {
|
||||
"post": {
|
||||
"description": "Returns pair of access and refresh tokens",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Request pair of tokens",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.AuthBody"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"auth.AuthBody": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john@proton.mail"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "Aasdfgh1!"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Bearer": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1.0",
|
||||
Host: "",
|
||||
BasePath: "/v1",
|
||||
Schemes: []string{},
|
||||
Title: "System Trace API",
|
||||
Description: "",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
67
docs/swagger.json
Normal file
67
docs/swagger.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"title": "System Trace API",
|
||||
"contact": {
|
||||
"name": "https://peresvet.it"
|
||||
},
|
||||
"version": "1.0"
|
||||
},
|
||||
"basePath": "/v1",
|
||||
"paths": {
|
||||
"/auth/login": {
|
||||
"post": {
|
||||
"description": "Returns pair of access and refresh tokens",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Request pair of tokens",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/auth.AuthBody"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"auth.AuthBody": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"email",
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john@proton.mail"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "Aasdfgh1!"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Bearer": {
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
||||
44
docs/swagger.yaml
Normal file
44
docs/swagger.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
basePath: /v1
|
||||
definitions:
|
||||
auth.AuthBody:
|
||||
properties:
|
||||
email:
|
||||
example: john@proton.mail
|
||||
type: string
|
||||
password:
|
||||
example: Aasdfgh1!
|
||||
type: string
|
||||
required:
|
||||
- email
|
||||
- password
|
||||
type: object
|
||||
info:
|
||||
contact:
|
||||
name: https://peresvet.it
|
||||
title: System Trace API
|
||||
version: "1.0"
|
||||
paths:
|
||||
/auth/login:
|
||||
post:
|
||||
description: Returns pair of access and refresh tokens
|
||||
parameters:
|
||||
- description: Request body
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/auth.AuthBody'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Request pair of tokens
|
||||
tags:
|
||||
- auth
|
||||
securityDefinitions:
|
||||
Bearer:
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
||||
19
environment/environment.go
Normal file
19
environment/environment.go
Normal file
@ -0,0 +1,19 @@
|
||||
package environment
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func Load() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
}
|
||||
|
||||
func IsDebug() bool {
|
||||
return os.Getenv("DEBUG") == "true"
|
||||
}
|
||||
67
go.mod
Normal file
67
go.mod
Normal file
@ -0,0 +1,67 @@
|
||||
module system-trace/core
|
||||
|
||||
go 1.22.3
|
||||
|
||||
require (
|
||||
github.com/go-playground/validator v9.31.0+incompatible
|
||||
github.com/go-playground/validator/v10 v10.20.0
|
||||
github.com/goccy/go-json v0.10.2
|
||||
github.com/gofiber/fiber v1.14.6
|
||||
github.com/gofiber/fiber/v2 v2.52.4
|
||||
github.com/gofiber/swagger v1.0.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/swaggo/swag v1.16.3
|
||||
github.com/uptrace/bun v1.2.1
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.1
|
||||
github.com/uptrace/bun/driver/pgdriver v1.2.1
|
||||
github.com/uptrace/bun/extra/bundebug v1.2.1
|
||||
github.com/wasilibs/go-re2 v1.5.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/gofiber/utils v0.0.10 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/schema v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/magefile/mage v1.14.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.7.0 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.52.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
174
go.sum
Normal file
174
go.sum
Normal file
@ -0,0 +1,174 @@
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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 h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
|
||||
github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o=
|
||||
github.com/gofiber/fiber v1.14.6/go.mod h1:Yw2ekF1YDPreO9V6TMYjynu94xRxZBdaa8X5HhHsjCM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc=
|
||||
github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg=
|
||||
github.com/gofiber/utils v0.0.10 h1:3Mr7X7JdCUo7CWf/i5sajSaDmArEDtti8bM1JUVso2U=
|
||||
github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
|
||||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
|
||||
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
|
||||
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
||||
github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ=
|
||||
github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk=
|
||||
github.com/uptrace/bun v1.2.1/go.mod h1:cNg+pWBUMmJ8rHnETgf65CEvn3aIKErrwOD6IA8e+Ec=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.1 h1:ceP99r03u+s8ylaDE/RzgcajwGiC76Jz3nS2ZgyPQ4M=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.1/go.mod h1:mv6B12cisvSc6bwKm9q9wcrr26awkZK8QXM+nso9n2U=
|
||||
github.com/uptrace/bun/driver/pgdriver v1.2.1 h1:Cp6c1tKzbTIyL8o0cGT6cOhTsmQZdsUNhgcV51dsmLU=
|
||||
github.com/uptrace/bun/driver/pgdriver v1.2.1/go.mod h1:jEd3WGx74hWLat3/IkesOoWNjrFNUDADK3nkyOFOOJM=
|
||||
github.com/uptrace/bun/extra/bundebug v1.2.1 h1:85MYpX3QESYI02YerKxUi1CD9mHuLrc2BXs1eOCtQus=
|
||||
github.com/uptrace/bun/extra/bundebug v1.2.1/go.mod h1:sfGKIi0HSGxsTC/sgIHGwpnYduHHYhdMeOIwurgSY+Y=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
|
||||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
||||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wasilibs/go-re2 v1.5.2 h1:fDO2TJrRzRrv3jD0gzOvmZ2UM4Yt9YXOEdLrlNc/Ies=
|
||||
github.com/wasilibs/go-re2 v1.5.2/go.mod h1:UqqxQ1O99boQUm1r61H/IYGiGQOS/P88K7hU5nLNkEg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
23
main.go
Normal file
23
main.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"system-trace/core/app"
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/environment"
|
||||
"system-trace/core/validators"
|
||||
)
|
||||
|
||||
// @title System Trace API
|
||||
// @version 1.0
|
||||
// @contact.name https://peresvet.it
|
||||
// @BasePath /v1
|
||||
// @securityDefinitions.apikey Bearer
|
||||
// @in header
|
||||
// @name Authorization
|
||||
// @externalDocs.description OpenAPI
|
||||
func main() {
|
||||
environment.Load()
|
||||
validators.RegisterValidators()
|
||||
database.Connect()
|
||||
app.Serve()
|
||||
}
|
||||
22
users/sql.go
Normal file
22
users/sql.go
Normal file
@ -0,0 +1,22 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"context"
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/database/entities"
|
||||
"system-trace/core/utils"
|
||||
)
|
||||
|
||||
func FindByEmailAndPassword(email, password string) (*entities.User, error) {
|
||||
passwordHash := utils.SHA256(password)
|
||||
ctx := context.Background()
|
||||
u := new(entities.User)
|
||||
err := database.PG.NewSelect().
|
||||
Model(u).
|
||||
Where("email = ?", email).
|
||||
Where("password_hash = ?", passwordHash).
|
||||
Column("id").
|
||||
Scan(ctx)
|
||||
|
||||
return u, err
|
||||
}
|
||||
9
users/users.go
Normal file
9
users/users.go
Normal file
@ -0,0 +1,9 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Get(c *fiber.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
13
utils/crypto.go
Normal file
13
utils/crypto.go
Normal file
@ -0,0 +1,13 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func SHA256(s string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(s))
|
||||
bs := h.Sum(nil)
|
||||
return fmt.Sprintf("%x", bs)
|
||||
}
|
||||
28
utils/jwt.go
Normal file
28
utils/jwt.go
Normal file
@ -0,0 +1,28 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func SignJWT(c jwt.Claims) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
|
||||
ts, err := token.SignedString([]byte("123"))
|
||||
|
||||
return ts, err
|
||||
}
|
||||
|
||||
func ValidateJWT(h string) (jwt.MapClaims, error) {
|
||||
token, err := jwt.Parse(h, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte("123"), nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
|
||||
return claims, err
|
||||
}
|
||||
28
validators/password.go
Normal file
28
validators/password.go
Normal file
@ -0,0 +1,28 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func regPassword(v *XValidator) {
|
||||
v.validator.RegisterValidation("password", func(fl validator.FieldLevel) bool {
|
||||
tests := []string{
|
||||
`[a-z]`,
|
||||
`[A-Z]`,
|
||||
`[\d]`,
|
||||
`[@$!%*?&]`,
|
||||
`[A-Za-z\d@$!%*?&]`,
|
||||
`.{8,}`,
|
||||
}
|
||||
password := fl.Field().String()
|
||||
for _, test := range tests {
|
||||
t, _ := regexp.MatchString(test, password)
|
||||
if !t {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
68
validators/validator.go
Normal file
68
validators/validator.go
Normal file
@ -0,0 +1,68 @@
|
||||
package validators
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type (
|
||||
ErrorResponse struct {
|
||||
Error bool
|
||||
FailedField string
|
||||
Tag string
|
||||
}
|
||||
XValidator struct {
|
||||
validator *validator.Validate
|
||||
}
|
||||
)
|
||||
|
||||
var Validator *XValidator
|
||||
var validate = validator.New()
|
||||
|
||||
func RegisterValidators() {
|
||||
Validator = &XValidator{
|
||||
validator: validate,
|
||||
}
|
||||
regPassword(Validator)
|
||||
}
|
||||
|
||||
func Validate(c *fiber.Ctx, data interface{}) error {
|
||||
if errs := Validator.validateFields(data); len(errs) > 0 && errs[0].Error {
|
||||
errMsgs := make([]string, 0)
|
||||
|
||||
for _, err := range errs {
|
||||
errMsgs = append(errMsgs, fmt.Sprintf(
|
||||
"field '%s' needs to implement '%s' rule",
|
||||
err.FailedField,
|
||||
err.Tag,
|
||||
))
|
||||
}
|
||||
|
||||
return errors.New(strings.Join(errMsgs, " || "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v XValidator) validateFields(data interface{}) []ErrorResponse {
|
||||
validationErrors := []ErrorResponse{}
|
||||
|
||||
errs := validate.Struct(data)
|
||||
if errs != nil {
|
||||
for _, err := range errs.(validator.ValidationErrors) {
|
||||
elem := ErrorResponse{
|
||||
FailedField: err.Field(),
|
||||
Tag: err.ActualTag(),
|
||||
Error: true,
|
||||
}
|
||||
|
||||
validationErrors = append(validationErrors, elem)
|
||||
}
|
||||
}
|
||||
|
||||
return validationErrors
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user