Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c4eaf6809 | ||
|
|
327208b333 | ||
|
|
9d95c05457 | ||
|
|
1f9a95897e | ||
|
|
61fe7dbea5 | ||
|
|
5e3ebdc825 | ||
|
|
50b19bf5a3 | ||
|
|
b2b2562e09 | ||
|
|
13154a1b4e |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.env
|
||||
tmp
|
||||
tmp
|
||||
.DS_Store
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "plugins/dictionary"]
|
||||
path = plugins/dictionary
|
||||
url = ../../plugins-dictionary
|
||||
32
amqp/amqp.go
Normal file
32
amqp/amqp.go
Normal file
@ -0,0 +1,32 @@
|
||||
package amqp
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"system-trace/core/environment"
|
||||
"system-trace/core/utils"
|
||||
|
||||
"github.com/wagslane/go-rabbitmq"
|
||||
)
|
||||
|
||||
var Broker *rabbitmq.Conn
|
||||
|
||||
func InitConn() *rabbitmq.Conn {
|
||||
var conn *rabbitmq.Conn
|
||||
conn, err := rabbitmq.NewConn(
|
||||
os.Getenv("AMQP_URL"),
|
||||
rabbitmq.WithConnectionOptionsLogging,
|
||||
rabbitmq.WithConnectionOptionsReconnectInterval(environment.ReconnectionInterval),
|
||||
)
|
||||
if err != nil {
|
||||
log.Println("[AMQP]", err)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
to := environment.ReconnectionInterval
|
||||
utils.WaitTimeout(&wg, to)
|
||||
return InitConn()
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
43
amqp/types/agent.go
Normal file
43
amqp/types/agent.go
Normal file
@ -0,0 +1,43 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
AGENT_HELLO uint8 = iota // 0
|
||||
AGENT_ERROR // 1
|
||||
AGENT_STATUS // 2
|
||||
AGENT_PING // 3
|
||||
)
|
||||
|
||||
const (
|
||||
TASK_STATUS_PREPARING uint8 = iota // 0
|
||||
TASK_STATUS_RUNNING // 1
|
||||
TASK_STATUS_STOPPED // 2
|
||||
TASK_STATUS_FINISHED // 3
|
||||
TASK_STATUS_ERROR // 4
|
||||
)
|
||||
|
||||
type AgentMessage struct {
|
||||
Message uint8 `json:"message"`
|
||||
Data *agentMessageData `json:"data"`
|
||||
}
|
||||
|
||||
type agentMessageData struct {
|
||||
agentHelloData
|
||||
agentErrorData
|
||||
agentStatusData
|
||||
}
|
||||
|
||||
type agentErrorData struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type agentStatusData struct {
|
||||
Status uint8 `json:"status"`
|
||||
TaskID int64 `json:"taskId"`
|
||||
Progress uint8 `json:"progress"` // 0-100, for TASK_STATUS_RUNNING
|
||||
}
|
||||
|
||||
type agentHelloData struct {
|
||||
Hostname string `json:"hostname"`
|
||||
Version string `json:"version"`
|
||||
Interfaces []string `json:"interfaces"`
|
||||
}
|
||||
36
amqp/types/controller.go
Normal file
36
amqp/types/controller.go
Normal file
@ -0,0 +1,36 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"system-trace/core/types/constructor"
|
||||
)
|
||||
|
||||
const (
|
||||
CONTROLLER_TASK_START uint8 = iota // 0
|
||||
CONTROLLER_TASK_PAUSE // 1
|
||||
CONTROLLER_TASK_STOP // 2
|
||||
CONTROLLER_PONG // 3
|
||||
CONTROLLER_GET_TASKS_STATUS // 4
|
||||
)
|
||||
|
||||
type ControllerMessage struct {
|
||||
Message uint8 `json:"message" validate:"required,min=0,max=255"`
|
||||
Data *controllerMessageData `json:"data" validate:"required"`
|
||||
}
|
||||
|
||||
type controllerMessageData struct {
|
||||
taskStartData
|
||||
taskStatusData
|
||||
getTasksStatus
|
||||
}
|
||||
|
||||
type taskStatusData struct {
|
||||
TaskID int64 `json:"taskId" validate:"required"`
|
||||
}
|
||||
|
||||
type getTasksStatus struct {
|
||||
Tasks []int64 `json:"tasks" validate:"required"`
|
||||
}
|
||||
|
||||
type taskStartData struct {
|
||||
Data *constructor.TaskData `json:"data" validate:"required"`
|
||||
}
|
||||
8
amqp/types/queues_map.go
Normal file
8
amqp/types/queues_map.go
Normal file
@ -0,0 +1,8 @@
|
||||
package types
|
||||
|
||||
import "github.com/wagslane/go-rabbitmq"
|
||||
|
||||
type QueuesPair struct {
|
||||
Publisher *rabbitmq.Publisher
|
||||
Consumer *rabbitmq.Consumer
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
type Agent struct {
|
||||
ID int64 `bun:",pk,autoincrement"`
|
||||
Hostname string `bun:",notnull" json:"hostname"`
|
||||
IP string `bun:",notnull"`
|
||||
@ -17,9 +17,9 @@ type Server struct {
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
var _ bun.BeforeAppendModelHook = (*Server)(nil)
|
||||
var _ bun.BeforeAppendModelHook = (*Agent)(nil)
|
||||
|
||||
func (s *Server) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
func (s *Agent) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
switch query.(type) {
|
||||
case *bun.InsertQuery:
|
||||
s.LastOnline = time.Now()
|
||||
@ -17,8 +17,9 @@ import (
|
||||
)
|
||||
|
||||
var PG *bun.DB
|
||||
var ctx = context.Background()
|
||||
|
||||
func Connect() {
|
||||
func Connect() *bun.DB {
|
||||
pgconn := pgdriver.NewConnector(
|
||||
pgdriver.WithNetwork("tcp"),
|
||||
pgdriver.WithAddr(fmt.Sprintf("%s:%s", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"))),
|
||||
@ -50,7 +51,7 @@ func Connect() {
|
||||
}
|
||||
}
|
||||
|
||||
PG = db
|
||||
return db
|
||||
}
|
||||
|
||||
func createSchema(db *bun.DB) error {
|
||||
@ -58,8 +59,8 @@ func createSchema(db *bun.DB) error {
|
||||
(*Group)(nil),
|
||||
(*GroupPermission)(nil),
|
||||
(*User)(nil),
|
||||
(*Session)(nil),
|
||||
(*Server)(nil),
|
||||
(*Task)(nil),
|
||||
(*Agent)(nil),
|
||||
(*AuthToken)(nil),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
type Group struct {
|
||||
ID int32 `bun:",pk,autoincrement"`
|
||||
IssuerID int32 `bun:",notnull" json:"issuerId"`
|
||||
IssuerID int32 `bun:",notnull" json:"issuerID"`
|
||||
Issuer *User `bun:"rel:belongs-to,join:issuer_id=id" json:"issuer"`
|
||||
Name string `bun:",notnull,unique" json:"name"`
|
||||
Users []*User `bun:"rel:has-many,join:id=group_id,array" json:"users"`
|
||||
@ -33,8 +33,7 @@ func (g *Group) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindGroups(s string, p *types.Pagination, ob *types.OrderBy) (groups *[]Group, cursor *types.Cursor, err error) {
|
||||
ctx := context.Background()
|
||||
func FindGroups(s *types.Search, p *types.Pagination, ob *types.OrderBy) (groups *[]Group, cursor *types.Cursor, err error) {
|
||||
groups = new([]Group)
|
||||
q := PG.NewSelect().
|
||||
Model(groups).
|
||||
@ -45,15 +44,15 @@ func FindGroups(s string, p *types.Pagination, ob *types.OrderBy) (groups *[]Gro
|
||||
Limit(p.Count).
|
||||
Order(fmt.Sprintf("%s %s", ob.Key, ob.Order))
|
||||
|
||||
if len(s) > 0 {
|
||||
if len(s.Input) > 0 {
|
||||
q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
if parsers.IsInt(s) {
|
||||
return q.Where(`"group"."id" = ?`, s)
|
||||
if parsers.IsInt(s.Input) {
|
||||
return q.Where(`"group"."id" = ?`, s.Input)
|
||||
} else {
|
||||
return q.
|
||||
Where(`"group"."name" LIKE ?`, "%"+s+"%").
|
||||
WhereOr(`"issuer"."email" LIKE ?`, "%"+s+"%").
|
||||
WhereOr(`"issuer"."real_name" LIKE ?`, "%"+s+"%")
|
||||
Where(`"group"."name" LIKE ?`, "%"+s.Input+"%").
|
||||
WhereOr(`"issuer"."email" LIKE ?`, "%"+s.Input+"%").
|
||||
WhereOr(`"issuer"."real_name" LIKE ?`, "%"+s.Input+"%")
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -69,7 +68,6 @@ func FindGroups(s string, p *types.Pagination, ob *types.OrderBy) (groups *[]Gro
|
||||
}
|
||||
|
||||
func FindGroupByID(id int) (group *Group, err error) {
|
||||
ctx := context.Background()
|
||||
group = new(Group)
|
||||
err = PG.NewSelect().
|
||||
Model(group).
|
||||
@ -80,17 +78,16 @@ func FindGroupByID(id int) (group *Group, err error) {
|
||||
}
|
||||
|
||||
func InsertGroup(g *Group) error {
|
||||
ctx := context.Background()
|
||||
_, err := PG.NewInsert().
|
||||
Model(g).
|
||||
Returning("NULL").
|
||||
Returning("id").
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateGroup(g *Group, cols []string) error {
|
||||
ctx := context.Background()
|
||||
cols = append(cols, "updated_at")
|
||||
_, err := PG.NewUpdate().
|
||||
Model(g).
|
||||
Column(cols...).
|
||||
@ -101,7 +98,6 @@ func UpdateGroup(g *Group, cols []string) error {
|
||||
}
|
||||
|
||||
func DeleteGroup(id int) error {
|
||||
ctx := context.Background()
|
||||
_, err := PG.NewDelete().
|
||||
Model(new(Group)).
|
||||
Where("id = ?", id).
|
||||
@ -109,3 +105,12 @@ func DeleteGroup(id int) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteManyGroups(ids []int) error {
|
||||
_, err := PG.NewDelete().
|
||||
Model(new(Group)).
|
||||
Where("id IN (?)", bun.In(ids)).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -4,3 +4,21 @@ type GroupPermission struct {
|
||||
GroupID int32 `bun:",notnull" json:"-"`
|
||||
Value int8 `bun:",notnull" json:"value"`
|
||||
}
|
||||
|
||||
func InsertGroupPermissions(gp []*GroupPermission) error {
|
||||
_, err := PG.NewInsert().
|
||||
Model(&gp).
|
||||
Returning("NULL").
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteGroupPermissions(groupid int32) error {
|
||||
_, err := PG.NewDelete().
|
||||
Model(new(GroupPermission)).
|
||||
Where("group_id = ?", groupid).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -7,10 +7,12 @@ import (
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
type Task struct {
|
||||
ID int64 `bun:",pk,autoincrement"`
|
||||
IssuerID int32 `bun:",notnull" json:"issuerId"`
|
||||
IssuerID int32 `bun:",notnull" json:"issuerID"`
|
||||
Issuer *User `bun:"rel:belongs-to,join:issuer_id=id" json:"issuer"`
|
||||
Status int8 `bun:"default:0" json:"status"`
|
||||
Error string `json:"error"`
|
||||
Configuration map[string]interface{} `bun:"type:jsonb" json:"configuration"`
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp" json:"createdAt"`
|
||||
FinishedAt time.Time `json:"finishedAt"`
|
||||
@ -18,9 +20,9 @@ type Session struct {
|
||||
DeletedAt time.Time `bun:",soft_delete,nullzero" json:"deletedAt"`
|
||||
}
|
||||
|
||||
var _ bun.BeforeAppendModelHook = (*Session)(nil)
|
||||
var _ bun.BeforeAppendModelHook = (*Task)(nil)
|
||||
|
||||
func (s *Session) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
func (s *Task) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
switch query.(type) {
|
||||
case *bun.UpdateQuery:
|
||||
s.UpdatedAt = time.Now()
|
||||
@ -18,13 +18,13 @@ type User struct {
|
||||
PasswordHash string `bun:",notnull,type:varchar(64)" json:"-"`
|
||||
PasswordLength int8 `bun:",notnull" json:"passwordLength"`
|
||||
RealName string `bun:",notnull" json:"realName"`
|
||||
GroupID int32 `bun:",notnull" json:"groupId"`
|
||||
GroupID int32 `bun:",notnull" json:"groupID"`
|
||||
Group *Group `bun:"rel:belongs-to,join:group_id=id" json:"group"`
|
||||
IsRequiredToSetPassword bool `bun:",notnull,default:true" json:"isRequiredToSetPassword"`
|
||||
IsActive bool `bun:",notnull,default:true" json:"isActive"`
|
||||
LastLogin time.Time `json:"lastLogin"`
|
||||
LastLogin time.Time `bun:",nullzero" json:"lastLogin"`
|
||||
CreatedAt time.Time `bun:",notnull,default:current_timestamp" json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
UpdatedAt time.Time `bun:",nullzero" json:"updatedAt"`
|
||||
DeletedAt time.Time `bun:",soft_delete,nullzero" json:"deletedAt"`
|
||||
}
|
||||
|
||||
@ -40,7 +40,6 @@ func (u *User) BeforeAppendModel(ctx context.Context, query bun.Query) error {
|
||||
|
||||
func FindByEmailAndPassword(email, password string) (*User, error) {
|
||||
passwordHash := utils.SHA256(password)
|
||||
ctx := context.Background()
|
||||
u := new(User)
|
||||
err := PG.NewSelect().
|
||||
Model(u).
|
||||
@ -51,8 +50,7 @@ func FindByEmailAndPassword(email, password string) (*User, error) {
|
||||
return u, err
|
||||
}
|
||||
|
||||
func FindUsers(s string, p *types.Pagination, ob *types.OrderBy) (users *[]User, cursor *types.Cursor, err error) {
|
||||
ctx := context.Background()
|
||||
func FindUsers(s *types.Search, p *types.Pagination, ob *types.OrderBy) (users *[]User, cursor *types.Cursor, err error) {
|
||||
users = new([]User)
|
||||
q := PG.NewSelect().
|
||||
Model(users).
|
||||
@ -62,18 +60,22 @@ func FindUsers(s string, p *types.Pagination, ob *types.OrderBy) (users *[]User,
|
||||
Limit(p.Count).
|
||||
Order(fmt.Sprintf("%s %s", ob.Key, ob.Order))
|
||||
|
||||
if len(s) > 0 {
|
||||
if len(s.Input) > 0 {
|
||||
q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
if parsers.IsInt(s) {
|
||||
return q.Where(`"user"."id" = ?`, s)
|
||||
if parsers.IsInt(s.Input) {
|
||||
return q.Where(`"user"."id" = ?`, s.Input)
|
||||
} else {
|
||||
return q.
|
||||
Where(`"user"."email" LIKE ?`, "%"+s+"%").
|
||||
WhereOr(`"user"."real_name" LIKE ?`, "%"+s+"%")
|
||||
Where(`"user"."email" LIKE ?`, "%"+s.Input+"%").
|
||||
WhereOr(`"user"."real_name" LIKE ?`, "%"+s.Input+"%")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if len(s.GroupID) > 0 && parsers.IsInt(s.GroupID) {
|
||||
q.Where(`"user"."group_id" = ?`, s.GroupID)
|
||||
}
|
||||
|
||||
count, err := q.ScanAndCount(ctx)
|
||||
|
||||
return users, &types.Cursor{
|
||||
@ -85,7 +87,6 @@ func FindUsers(s string, p *types.Pagination, ob *types.OrderBy) (users *[]User,
|
||||
}
|
||||
|
||||
func FindUserByID(id int) (user *User, err error) {
|
||||
ctx := context.Background()
|
||||
user = new(User)
|
||||
err = PG.NewSelect().
|
||||
Model(user).
|
||||
@ -96,7 +97,6 @@ func FindUserByID(id int) (user *User, err error) {
|
||||
}
|
||||
|
||||
func InsertUser(u *User) error {
|
||||
ctx := context.Background()
|
||||
_, err := PG.NewInsert().
|
||||
Model(u).
|
||||
Returning("NULL").
|
||||
@ -106,7 +106,7 @@ func InsertUser(u *User) error {
|
||||
}
|
||||
|
||||
func UpdateUser(u *User, cols []string) error {
|
||||
ctx := context.Background()
|
||||
cols = append(cols, "updated_at")
|
||||
_, err := PG.NewUpdate().
|
||||
Model(u).
|
||||
Column(cols...).
|
||||
@ -117,7 +117,6 @@ func UpdateUser(u *User, cols []string) error {
|
||||
}
|
||||
|
||||
func DeleteUser(id int) error {
|
||||
ctx := context.Background()
|
||||
_, err := PG.NewDelete().
|
||||
Model(new(User)).
|
||||
Where("id = ?", id).
|
||||
@ -125,3 +124,32 @@ func DeleteUser(id int) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteManyUsers(ids []int) error {
|
||||
_, err := PG.NewDelete().
|
||||
Model(new(User)).
|
||||
Where("id IN (?)", bun.In(ids)).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func BlockManyUsers(ids []int) error {
|
||||
_, err := PG.NewUpdate().
|
||||
Model(new(User)).
|
||||
Set("is_active = ?", false).
|
||||
Where("id IN (?)", bun.In(ids)).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func UnblockManyUsers(ids []int) error {
|
||||
_, err := PG.NewUpdate().
|
||||
Model(new(User)).
|
||||
Set("is_active = ?", true).
|
||||
Where("id IN (?)", bun.In(ids)).
|
||||
Exec(ctx)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
206
docs/docs.go
206
docs/docs.go
@ -108,6 +108,32 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete groups by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"groups"
|
||||
],
|
||||
"summary": "Delete many groups",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "Update group with specified data",
|
||||
"produces": [
|
||||
@ -210,6 +236,26 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/plugins": {
|
||||
"get": {
|
||||
"description": "Returns key-value map with plugins",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Get list of available plugins",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/constructor.Plugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"description": "Returns array of users and count",
|
||||
@ -267,6 +313,35 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.NewCredentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
@ -354,9 +429,109 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/block": {
|
||||
"patch": {
|
||||
"description": "Block users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Block many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/password/:id": {
|
||||
"patch": {
|
||||
"description": "Reset user password by user ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Reset user password",
|
||||
"parameters": [
|
||||
{
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.NewCredentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/unblock": {
|
||||
"patch": {
|
||||
"description": "Unblock users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Unblock many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"constructor.Plugin": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"onlyUdp": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"database.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -433,9 +608,6 @@ const docTemplate = `{
|
||||
"lastLogin": {
|
||||
"type": "string"
|
||||
},
|
||||
"passwordHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"passwordLength": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -475,6 +647,9 @@ const docTemplate = `{
|
||||
},
|
||||
"totalPages": {
|
||||
"type": "number"
|
||||
},
|
||||
"totalRows": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -486,6 +661,31 @@ const docTemplate = `{
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
},
|
||||
"types.ManyIDs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"array"
|
||||
],
|
||||
"properties": {
|
||||
"array": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"types.NewCredentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@ -100,6 +100,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete groups by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"groups"
|
||||
],
|
||||
"summary": "Delete many groups",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
"patch": {
|
||||
"description": "Update group with specified data",
|
||||
"produces": [
|
||||
@ -202,6 +228,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/plugins": {
|
||||
"get": {
|
||||
"description": "Returns key-value map with plugins",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Get list of available plugins",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/constructor.Plugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users": {
|
||||
"get": {
|
||||
"description": "Returns array of users and count",
|
||||
@ -259,6 +305,35 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.NewCredentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Delete many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
@ -346,9 +421,109 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/block": {
|
||||
"patch": {
|
||||
"description": "Block users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Block many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/password/:id": {
|
||||
"patch": {
|
||||
"description": "Reset user password by user ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Reset user password",
|
||||
"parameters": [
|
||||
{
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"description": "User ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.NewCredentials"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/users/unblock": {
|
||||
"patch": {
|
||||
"description": "Unblock users by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"users"
|
||||
],
|
||||
"summary": "Unblock many users",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Request body",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/types.ManyIDs"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"constructor.Plugin": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"onlyUdp": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"database.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -425,9 +600,6 @@
|
||||
"lastLogin": {
|
||||
"type": "string"
|
||||
},
|
||||
"passwordHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"passwordLength": {
|
||||
"type": "integer"
|
||||
},
|
||||
@ -467,6 +639,9 @@
|
||||
},
|
||||
"totalPages": {
|
||||
"type": "number"
|
||||
},
|
||||
"totalRows": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -478,6 +653,31 @@
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
},
|
||||
"types.ManyIDs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"array"
|
||||
],
|
||||
"properties": {
|
||||
"array": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"types.NewCredentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
basePath: /v1
|
||||
definitions:
|
||||
constructor.Plugin:
|
||||
properties:
|
||||
ID:
|
||||
type: integer
|
||||
name:
|
||||
type: string
|
||||
onlyUdp:
|
||||
type: boolean
|
||||
type: object
|
||||
database.Group:
|
||||
properties:
|
||||
createdAt:
|
||||
@ -50,8 +59,6 @@ definitions:
|
||||
type: boolean
|
||||
lastLogin:
|
||||
type: string
|
||||
passwordHash:
|
||||
type: string
|
||||
passwordLength:
|
||||
type: integer
|
||||
realName:
|
||||
@ -79,6 +86,8 @@ definitions:
|
||||
type: integer
|
||||
totalPages:
|
||||
type: number
|
||||
totalRows:
|
||||
type: number
|
||||
type: object
|
||||
types.JSONPagination:
|
||||
properties:
|
||||
@ -86,6 +95,22 @@ definitions:
|
||||
$ref: '#/definitions/types.Cursor'
|
||||
data: {}
|
||||
type: object
|
||||
types.ManyIDs:
|
||||
properties:
|
||||
array:
|
||||
items:
|
||||
type: integer
|
||||
type: array
|
||||
required:
|
||||
- array
|
||||
type: object
|
||||
types.NewCredentials:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact:
|
||||
name: https://peresvet.it
|
||||
@ -111,6 +136,23 @@ paths:
|
||||
tags:
|
||||
- auth
|
||||
/groups:
|
||||
delete:
|
||||
description: Delete groups by ID
|
||||
parameters:
|
||||
- description: Request body
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/types.ManyIDs'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Delete many groups
|
||||
tags:
|
||||
- groups
|
||||
get:
|
||||
description: Returns array of groups and count
|
||||
parameters:
|
||||
@ -220,7 +262,37 @@ paths:
|
||||
type: integer
|
||||
type: object
|
||||
summary: Get list of permissions
|
||||
/plugins:
|
||||
get:
|
||||
description: Returns key-value map with plugins
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/constructor.Plugin'
|
||||
type: array
|
||||
summary: Get list of available plugins
|
||||
/users:
|
||||
delete:
|
||||
description: Delete users by ID
|
||||
parameters:
|
||||
- description: Request body
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/types.ManyIDs'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Delete many users
|
||||
tags:
|
||||
- users
|
||||
get:
|
||||
description: Returns array of users and count
|
||||
parameters:
|
||||
@ -277,6 +349,8 @@ paths:
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/types.NewCredentials'
|
||||
summary: Create user
|
||||
tags:
|
||||
- users
|
||||
@ -317,6 +391,62 @@ paths:
|
||||
summary: Get user by ID
|
||||
tags:
|
||||
- users
|
||||
/users/block:
|
||||
patch:
|
||||
description: Block users by ID
|
||||
parameters:
|
||||
- description: Request body
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/types.ManyIDs'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Block many users
|
||||
tags:
|
||||
- users
|
||||
/users/password/:id:
|
||||
patch:
|
||||
description: Reset user password by user ID
|
||||
parameters:
|
||||
- description: User ID
|
||||
in: path
|
||||
minimum: 1
|
||||
name: id
|
||||
required: true
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/types.NewCredentials'
|
||||
summary: Reset user password
|
||||
tags:
|
||||
- users
|
||||
/users/unblock:
|
||||
patch:
|
||||
description: Unblock users by ID
|
||||
parameters:
|
||||
- description: Request body
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/types.ManyIDs'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
summary: Unblock many users
|
||||
tags:
|
||||
- users
|
||||
securityDefinitions:
|
||||
accessToken=...;refreshToken=...:
|
||||
in: header
|
||||
|
||||
@ -3,15 +3,25 @@ package environment
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
var ReconnectionInterval time.Duration
|
||||
|
||||
func Load() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
if recon, err := strconv.Atoi(os.Getenv("RECONNECT_INTERVAL")); err == nil {
|
||||
ReconnectionInterval = time.Duration(recon) * time.Millisecond
|
||||
} else {
|
||||
log.Fatal("[AMQP]", err)
|
||||
}
|
||||
}
|
||||
|
||||
func IsDebug() bool {
|
||||
|
||||
3
go.mod
3
go.mod
@ -10,11 +10,13 @@ require (
|
||||
github.com/gofiber/swagger v1.0.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb
|
||||
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/wagslane/go-rabbitmq v0.14.2
|
||||
github.com/wasilibs/go-re2 v1.5.2
|
||||
)
|
||||
|
||||
@ -45,6 +47,7 @@ require (
|
||||
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/rabbitmq/amqp091-go v1.10.0 // 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
|
||||
|
||||
7
go.sum
7
go.sum
@ -67,6 +67,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb h1:w1g9wNDIE/pHSTmAaUhv4TZQuPBS6GV3mMz5hkgziIU=
|
||||
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
|
||||
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=
|
||||
@ -88,6 +90,8 @@ github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2
|
||||
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/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
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=
|
||||
@ -130,11 +134,14 @@ github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IU
|
||||
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/wagslane/go-rabbitmq v0.14.2 h1:3l75Unsy0b8sb3ILqJxMTXkQLUPI67BOuubV9YBjGLE=
|
||||
github.com/wagslane/go-rabbitmq v0.14.2/go.mod h1:6sCLt2wZoxyC73G7u/yD6/RX/yYf+x5D8SQk8nsa4Lc=
|
||||
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/wasilibs/nottinygc v0.4.0 h1:h1TJMihMC4neN6Zq+WKpLxgd9xCFMw7O9ETLwY2exJQ=
|
||||
github.com/wasilibs/nottinygc v0.4.0/go.mod h1:oDcIotskuYNMpqMF23l7Z8uzD4TC0WXHK8jetlB3HIo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
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=
|
||||
|
||||
13
main.go
13
main.go
@ -1,8 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"system-trace/core/amqp"
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/environment"
|
||||
"system-trace/core/plugins"
|
||||
"system-trace/core/services/agents"
|
||||
"system-trace/core/validators"
|
||||
)
|
||||
|
||||
@ -16,7 +19,15 @@ import (
|
||||
// @externalDocs.description OpenAPI
|
||||
func main() {
|
||||
environment.Load()
|
||||
plugins.LoadPlugins()
|
||||
validators.RegisterValidators()
|
||||
database.Connect()
|
||||
|
||||
database.PG = database.Connect()
|
||||
|
||||
amqp.Broker = amqp.InitConn()
|
||||
{
|
||||
go agents.CreateMainQueueConsumer(amqp.Broker)
|
||||
}
|
||||
|
||||
serveApp()
|
||||
}
|
||||
|
||||
230
message_examples.json
Normal file
230
message_examples.json
Normal file
@ -0,0 +1,230 @@
|
||||
Messages from Agent
|
||||
- TO MAIN QUEUE:
|
||||
AGENT_HELLO:
|
||||
{
|
||||
"message": AGENT_HELLO (0),
|
||||
"data": {
|
||||
"hostname": "<agent_hostname>",
|
||||
"version": "<agent_version>",
|
||||
"interfaces": ["ens0p0","ens0p1"]
|
||||
}
|
||||
}
|
||||
|
||||
AGENT_ERROR: // for errors that are not related to tasks
|
||||
{
|
||||
"message": AGENT_ERROR (1),
|
||||
"data": {
|
||||
"error": "<error>"
|
||||
}
|
||||
}
|
||||
|
||||
- TO SEPARATED QUEUE:
|
||||
AGENT_STATUS:
|
||||
{
|
||||
"message": AGENT_STATUS (2),
|
||||
"data": {
|
||||
"status": TASK_STATUS_<STATUS>, // PREPARING (0), RUNNING (1), STOPPED (2), FINISHED (3), ERROR (4)
|
||||
"taskId": <task_id>,
|
||||
"progress": "Optional 0-100 for TASK_STATUS_RUNNIG",
|
||||
"error": "Optional for TASK_STATUS_ERROR"
|
||||
}
|
||||
}
|
||||
|
||||
AGENT_PING:
|
||||
{
|
||||
"message": AGENT_PING (3),
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Messages from Controller
|
||||
- TO SEPARATED QUEUE:
|
||||
CONTROLLER_PONG:
|
||||
{
|
||||
"message": CONTROLLER_PONG (3),
|
||||
}
|
||||
|
||||
CONTROLLER_GET_TASKS_STATUS:
|
||||
{
|
||||
"message": CONTROLLER_GET_TASKS_STATUS (4),
|
||||
"data": {
|
||||
"tasks": [123, 456, 678909], // []int64
|
||||
}
|
||||
}
|
||||
|
||||
CONTROLLER_TASK_PAUSE:
|
||||
{
|
||||
"message": CONTROLLER_TASK_PAUSE (1),
|
||||
"data": {
|
||||
"taskId": <task_id>
|
||||
}
|
||||
}
|
||||
|
||||
CONTROLLER_TASK_STOP:
|
||||
{
|
||||
"message": CONTROLLER_TASK_STOP (2),
|
||||
"data": {
|
||||
"taskId": <task_id>
|
||||
}
|
||||
}
|
||||
|
||||
CONTROLLER_TASK_START:
|
||||
{
|
||||
"message": CONTROLLER_TASK_START (0),
|
||||
"data": {
|
||||
"taskId": <task_id>,
|
||||
"data": {
|
||||
"type": TYPE_VFIO (0), // TYPE_POSIX (1)
|
||||
"mode": MODE_THROUGHPUT_BPS (0), // MODE_THROUGHPUT_PPS (1), MODE_TCP_CONNECTIONS (2), MODE_USERS (3),
|
||||
"time": <duration_in_seconds>,
|
||||
"sourceClient": [
|
||||
"enp0s0": [
|
||||
{
|
||||
"IPs": ["127.0.0.0/24", "120.0.1.1/32"], // SRC-IPs mask cannot be duplicated into two or more entities within 1 network interface
|
||||
"MACs": {
|
||||
"mode": IP_HASH (1),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
}
|
||||
],
|
||||
},
|
||||
"nextHops": {
|
||||
"mode": ROUND_ROBIN (0),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
"weight": 47,
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
"weight": 53,
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
"enp0s1": [
|
||||
{
|
||||
"IPs": ["121.0.1.1/32"],
|
||||
"MACs": {
|
||||
"mode": IP_HASH (1),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
}
|
||||
],
|
||||
}, // every packet receives round-robin balancing mac
|
||||
"nextHops": {
|
||||
"mode": ROUND_ROBIN (0),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
"weight": 47,
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
"weight": 53,
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"IPs": ["124.0.1.0/24", "127.0.1.0/16"],
|
||||
"MACs": {
|
||||
"mode": IP_HASH (1),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
}
|
||||
],
|
||||
}, // IPs hash, divide and bind to every IPv4/IPv6. MAC-адресов не может быть больше IPs-адресов
|
||||
"nextHops": {
|
||||
"mode": ROUND_ROBIN (0),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
"weight": 47,
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
"weight": 53,
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"IPs": ["129.0.1.1/32"],
|
||||
"MACs": {
|
||||
"mode": RANDOM (2), // random MAC every packet
|
||||
},
|
||||
"nextHops": {
|
||||
"mode": ROUND_ROBIN (0),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
"weight": 47,
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
"weight": 53,
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
"IPs": ["128.0.1.0/24"],
|
||||
"MACs": {
|
||||
"mode": GENERATE (3), // generate mac for every IPv4/IPv6 and bind it
|
||||
},
|
||||
"nextHops": {
|
||||
"mode": ROUND_ROBIN (0),
|
||||
"addresses": [
|
||||
{
|
||||
"address": "00-B0-D0-63-C2-26",
|
||||
"weight": 47,
|
||||
},
|
||||
{
|
||||
"address": "01-B0-D0-63-C2-26",
|
||||
"weight": 53,
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
],
|
||||
"sourceReceiver": [
|
||||
<same_as_source_client>
|
||||
],
|
||||
"plugins": [
|
||||
{
|
||||
"plugin": <plugin_id_from_dictionary>,
|
||||
"weight": <0-100 value>
|
||||
},
|
||||
{
|
||||
"plugin": <plugin_id_from_dictionary>,
|
||||
"weight": <0-100 value>
|
||||
}
|
||||
],
|
||||
"performance": {
|
||||
"maxBps": <optional uint64 depends on selected mode>,
|
||||
"maxPps": <optional uint32 depends on selected mode>,
|
||||
"maxTcpConnections": <optional uint32 depends on selected mode>, // max bps or max pps limit required with max tcp connections
|
||||
"maxUsers": <optional uint32 depends on selected mode>
|
||||
},
|
||||
"tweaks": [[0, 10], [10, 60], [20, 60], [30, 120], ...]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
"system-trace/core/constants"
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/modules/auth"
|
||||
"system-trace/core/services/auth"
|
||||
"system-trace/core/types"
|
||||
"system-trace/core/utils"
|
||||
|
||||
@ -20,7 +20,7 @@ func ValidateSession(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
if !validatePair(c, p) {
|
||||
return c.Status(http.StatusForbidden).JSON(fiber.Map{
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": constants.UNAUTHORIZED,
|
||||
})
|
||||
}
|
||||
@ -37,7 +37,7 @@ func validatePair(c *fiber.Ctx, p *types.PairTokens) bool {
|
||||
claims, err := utils.ValidateJWT(p.AccessToken)
|
||||
if (err != nil && strings.Contains(err.Error(), "token is expired")) || claims["iss"] != constants.JWT_APP_ISS {
|
||||
rclaims, rerr := utils.ValidateJWT(p.RefreshToken)
|
||||
if rerr != nil || (rerr != nil && strings.Contains(rerr.Error(), "token is expired")) || rclaims["sub"] != p.AccessToken {
|
||||
if (rerr != nil && strings.Contains(rerr.Error(), "token is expired")) || rclaims["sub"] != p.AccessToken {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -55,8 +55,6 @@ func validatePair(c *fiber.Ctx, p *types.PairTokens) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
userID = pt.UserID
|
||||
}
|
||||
|
||||
userID = claims["sub"].(int32)
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package parsers
|
||||
|
||||
import "unicode"
|
||||
import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func IsInt(s string) bool {
|
||||
for _, c := range s {
|
||||
|
||||
26
parsers/many_ids.go
Normal file
26
parsers/many_ids.go
Normal file
@ -0,0 +1,26 @@
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"system-trace/core/types"
|
||||
"system-trace/core/validators"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetManyIDs(c *fiber.Ctx) (*types.ManyIDs, error) {
|
||||
ids := new(types.ManyIDs)
|
||||
if err := c.BodyParser(ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := validators.Validate(c, ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ids.Array) <= 0 {
|
||||
return nil, errors.New("empty array")
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func ParseOrder(c *fiber.Ctx) (*types.OrderBy, error) {
|
||||
func GetOrderBy(c *fiber.Ctx) (*types.OrderBy, error) {
|
||||
ob := new(types.OrderBy)
|
||||
if err := c.QueryParser(ob); err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func ParsePagination(c *fiber.Ctx) (*types.Pagination, error) {
|
||||
func GetPagination(c *fiber.Ctx) (*types.Pagination, error) {
|
||||
p := new(types.Pagination)
|
||||
if err := c.QueryParser(p); err != nil {
|
||||
return nil, err
|
||||
|
||||
16
parsers/search.go
Normal file
16
parsers/search.go
Normal file
@ -0,0 +1,16 @@
|
||||
package parsers
|
||||
|
||||
import (
|
||||
"system-trace/core/types"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetSearch(c *fiber.Ctx) (*types.Search, error) {
|
||||
s := new(types.Search)
|
||||
if err := c.QueryParser(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
1
plugins/dictionary
Submodule
1
plugins/dictionary
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 40fcb5dc67bae495bccff549f3373dbbac854909
|
||||
34
plugins/plugins.go
Normal file
34
plugins/plugins.go
Normal file
@ -0,0 +1,34 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"system-trace/core/types/constructor"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var plugins *[]constructor.Plugin
|
||||
|
||||
func LoadPlugins() {
|
||||
dictpath := "plugins/dictionary/dict.json"
|
||||
d, err := os.ReadFile(dictpath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(d, &plugins); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: GetPlugins godoc
|
||||
// @Summary Get list of available plugins
|
||||
// @Description Returns array of plugins
|
||||
// @Produce json
|
||||
// @Success 200 {object} []constructor.Plugin
|
||||
// @Router /plugins [get]
|
||||
func GetPlugins(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusOK).JSON(plugins)
|
||||
}
|
||||
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
_ "system-trace/core/docs"
|
||||
"system-trace/core/plugins"
|
||||
"system-trace/core/services"
|
||||
"system-trace/core/services/auth"
|
||||
"system-trace/core/services/groups"
|
||||
@ -17,6 +18,7 @@ func initRouter(app *fiber.App) {
|
||||
{
|
||||
|
||||
v1.Get("/permissions", services.GetPermissions)
|
||||
v1.Get("/plugins", plugins.GetPlugins)
|
||||
|
||||
ag := v1.Group("/auth")
|
||||
{
|
||||
@ -29,6 +31,7 @@ func initRouter(app *fiber.App) {
|
||||
gg.Get("/:id", groups.GetGroupByIDHandler)
|
||||
gg.Post("", groups.CreateGroupHandler)
|
||||
gg.Patch("", groups.UpdateGroupHandler)
|
||||
gg.Delete("", groups.DeleteGroupsHandler)
|
||||
gg.Delete("/:id", groups.DeleteGroupHandler)
|
||||
}
|
||||
|
||||
@ -38,6 +41,10 @@ func initRouter(app *fiber.App) {
|
||||
ug.Get("/:id", users.GetUserByIDHandler)
|
||||
ug.Post("", users.CreateUserHandler)
|
||||
ug.Patch("", users.UpdateUserHandler)
|
||||
ug.Patch("/password/:id", users.ResetUserPasswordHandler)
|
||||
ug.Patch("/block", users.BlockUsersHandler)
|
||||
ug.Patch("/unblock", users.UnblockUsersHandler)
|
||||
ug.Delete("", users.DeleteUsersHandler)
|
||||
ug.Delete("/:id", users.DeleteUserHandler)
|
||||
}
|
||||
}
|
||||
|
||||
13
services/agents/agents.go
Normal file
13
services/agents/agents.go
Normal file
@ -0,0 +1,13 @@
|
||||
package agents
|
||||
|
||||
import "system-trace/core/amqp/types"
|
||||
|
||||
var queues = make(map[string]*types.QueuesPair)
|
||||
|
||||
func getQueuePair(hostname string) *types.QueuesPair {
|
||||
if pair, ok := queues[hostname]; ok {
|
||||
return pair
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
94
services/agents/main_queue.go
Normal file
94
services/agents/main_queue.go
Normal file
@ -0,0 +1,94 @@
|
||||
package agents
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
agent "system-trace/core/amqp/types"
|
||||
|
||||
"github.com/wagslane/go-rabbitmq"
|
||||
)
|
||||
|
||||
var mainQueueName string = "SYSTEM_TRACE_MAIN"
|
||||
|
||||
func CreateMainQueueConsumer(broker *rabbitmq.Conn) {
|
||||
consumer, err := rabbitmq.NewConsumer(
|
||||
broker,
|
||||
mainQueueName,
|
||||
rabbitmq.WithConsumerOptionsRoutingKey(""),
|
||||
rabbitmq.WithConsumerOptionsExchangeName("controller"),
|
||||
rabbitmq.WithConsumerOptionsExchangeDeclare,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = consumer.Run(handleMessageFromMainQueue); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleMessageFromMainQueue(message rabbitmq.Delivery) rabbitmq.Action {
|
||||
am := new(agent.AgentMessage)
|
||||
if err := json.Unmarshal(message.Body, &am); err != nil {
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
action := handleAgentMessage(am)
|
||||
return action
|
||||
}
|
||||
|
||||
func handleAgentMessage(message *agent.AgentMessage) rabbitmq.Action {
|
||||
switch message.Message {
|
||||
case agent.AGENT_HELLO:
|
||||
{
|
||||
if message.Data == nil {
|
||||
fmt.Println("No data in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
if len(message.Data.Hostname) <= 0 {
|
||||
fmt.Println("Hostname field are not presented in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
if len(message.Data.Interfaces) <= 0 {
|
||||
fmt.Println("Interfaces field are not presented in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
if len(message.Data.Version) <= 0 {
|
||||
fmt.Println("Version field are not presented in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
// TODO create or update agent from hello message
|
||||
return rabbitmq.Ack
|
||||
}
|
||||
case agent.AGENT_ERROR:
|
||||
{
|
||||
if message.Data == nil {
|
||||
fmt.Println("No data in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
if len(message.Data.Error) <= 0 {
|
||||
fmt.Println("Error field are not presented in received message hello")
|
||||
// TODO error log
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
// TODO save log
|
||||
return rabbitmq.Ack
|
||||
}
|
||||
// case agent.AGENT_STATUS:
|
||||
// {
|
||||
|
||||
// }
|
||||
// case agent.AGENT_PING:
|
||||
// {
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
return rabbitmq.NackDiscard
|
||||
}
|
||||
@ -50,8 +50,7 @@ func ReqTokensHandler(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
err = GeneratePairAndSetCookie(c, u.ID)
|
||||
if err != nil {
|
||||
if err = GeneratePairAndSetCookie(c, u.ID); err != nil {
|
||||
return c.
|
||||
Status(fiber.StatusBadRequest).
|
||||
JSON(types.JSONError{
|
||||
@ -59,8 +58,7 @@ func ReqTokensHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = users.SetLoginTime(u)
|
||||
if err != nil {
|
||||
if err = users.SetLoginTime(u); err != nil {
|
||||
return c.
|
||||
Status(fiber.StatusBadRequest).
|
||||
JSON(types.JSONError{
|
||||
@ -92,6 +90,7 @@ func setCookie(c *fiber.Ctx, p *types.PairTokens) {
|
||||
atc.Secure = true
|
||||
atc.HTTPOnly = true
|
||||
c.Cookie(atc)
|
||||
|
||||
// Refresh token
|
||||
rtc := new(fiber.Cookie)
|
||||
rtc.Name = "refreshToken"
|
||||
|
||||
@ -15,21 +15,25 @@ import (
|
||||
// @Produce json
|
||||
// @Param count query int true "Count of rows" minimum(10) maximum(100)
|
||||
// @Param offset query int true "Rows to skip" minumum(0)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200 {object} types.JSONPagination
|
||||
// @Router /groups [get]
|
||||
func GetGroupsHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
s := c.Query("search")
|
||||
|
||||
p, err := parsers.ParsePagination(c)
|
||||
s, err := parsers.GetSearch(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
ob, err := parsers.ParseOrder(c)
|
||||
p, err := parsers.GetPagination(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
ob, err := parsers.GetOrderBy(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
@ -55,7 +59,6 @@ func GetGroupsHandler(c *fiber.Ctx) error {
|
||||
// @Tags groups
|
||||
// @Produce json
|
||||
// @Param id path int true "Group ID" minimum(1)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200 {object} database.Group
|
||||
// @Router /groups/:id [get]
|
||||
func GetGroupByIDHandler(c *fiber.Ctx) error {
|
||||
@ -83,7 +86,6 @@ func GetGroupByIDHandler(c *fiber.Ctx) error {
|
||||
// @Tags groups
|
||||
// @Produce json
|
||||
// @Param request body database.Group true "Request body"
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /groups [post]
|
||||
func CreateGroupHandler(c *fiber.Ctx) error {
|
||||
@ -95,8 +97,19 @@ func CreateGroupHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := database.InsertGroup(g)
|
||||
if err != nil {
|
||||
// TODO replace with locals
|
||||
g.IssuerID = 2 //c.Locals("userId").(int32)
|
||||
if err := database.InsertGroup(g); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Insert new permissions
|
||||
for _, v := range g.Permissions {
|
||||
v.GroupID = g.ID
|
||||
}
|
||||
if err := database.InsertGroupPermissions(g.Permissions); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
@ -111,7 +124,6 @@ func CreateGroupHandler(c *fiber.Ctx) error {
|
||||
// @Tags groups
|
||||
// @Produce json
|
||||
// @Param request body database.Group true "Request body"
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /groups [patch]
|
||||
func UpdateGroupHandler(c *fiber.Ctx) error {
|
||||
@ -123,8 +135,24 @@ func UpdateGroupHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := database.UpdateGroup(g, []string{"*"})
|
||||
if err != nil {
|
||||
if err := database.UpdateGroup(g, []string{"name"}); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Remove exist permissions
|
||||
if err := database.DeleteGroupPermissions(g.ID); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
// Insert new permissions
|
||||
for _, v := range g.Permissions {
|
||||
v.GroupID = g.ID
|
||||
}
|
||||
if err := database.InsertGroupPermissions(g.Permissions); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
@ -139,7 +167,6 @@ func UpdateGroupHandler(c *fiber.Ctx) error {
|
||||
// @Tags groups
|
||||
// @Produce json
|
||||
// @Param id path int true "Group ID" minimum(1)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /groups/:id [delete]
|
||||
func DeleteGroupHandler(c *fiber.Ctx) error {
|
||||
@ -151,8 +178,33 @@ func DeleteGroupHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = database.DeleteGroup(id)
|
||||
if err != nil {
|
||||
if err = database.DeleteGroup(id); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
// MARK: DeleteGroups godoc
|
||||
// @Summary Delete many groups
|
||||
// @Description Delete groups by ID
|
||||
// @Tags groups
|
||||
// @Produce json
|
||||
// @Param request body types.ManyIDs true "Request body"
|
||||
// @Success 200
|
||||
// @Router /groups [delete]
|
||||
func DeleteGroupsHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
arr := new(types.ManyIDs)
|
||||
if err := c.BodyParser(arr); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := database.DeleteManyGroups(arr.Array); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
// @Summary Get list of permissions
|
||||
// @Description Returns key-value map with permissions
|
||||
// @Produce json
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200 {object} map[string]int8
|
||||
// @Router /permissions [get]
|
||||
func GetPermissions(c *fiber.Ctx) error {
|
||||
|
||||
25
services/users/service.go
Normal file
25
services/users/service.go
Normal file
@ -0,0 +1,25 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SetLoginTime(u *database.User) error {
|
||||
u.LastLogin = time.Now()
|
||||
return database.UpdateUser(u, []string{"last_login"})
|
||||
}
|
||||
|
||||
func resetPassword(u *database.User) (string, error) {
|
||||
pass, err := utils.GeneratePassword()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := utils.SHA256(pass)
|
||||
u.PasswordHash = hash
|
||||
u.PasswordLength = int8(len(pass))
|
||||
u.IsRequiredToSetPassword = true
|
||||
|
||||
return pass, nil
|
||||
}
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"system-trace/core/database"
|
||||
"system-trace/core/parsers"
|
||||
"system-trace/core/types"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
@ -16,21 +15,25 @@ import (
|
||||
// @Produce json
|
||||
// @Param count query int true "Count of rows" minimum(10) maximum(100)
|
||||
// @Param offset query int true "Rows to skip" minumum(0)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200 {object} types.JSONPagination
|
||||
// @Router /users [get]
|
||||
func GetUsersHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
s := c.Query("search")
|
||||
|
||||
p, err := parsers.ParsePagination(c)
|
||||
s, err := parsers.GetSearch(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
ob, err := parsers.ParseOrder(c)
|
||||
p, err := parsers.GetPagination(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
ob, err := parsers.GetOrderBy(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
@ -56,7 +59,6 @@ func GetUsersHandler(c *fiber.Ctx) error {
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID" minimum(1)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200 {object} database.User
|
||||
// @Router /users/:id [get]
|
||||
func GetUserByIDHandler(c *fiber.Ctx) error {
|
||||
@ -84,8 +86,7 @@ func GetUserByIDHandler(c *fiber.Ctx) error {
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param request body database.User true "Request body"
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Success 200 {object} types.NewCredentials
|
||||
// @Router /users [post]
|
||||
func CreateUserHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
@ -96,14 +97,23 @@ func CreateUserHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := database.InsertUser(u)
|
||||
pass, err := resetPassword(u)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
if err := database.InsertUser(u); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(types.NewCredentials{
|
||||
Email: u.Email,
|
||||
Password: pass,
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: UpdateUser godoc
|
||||
@ -112,7 +122,6 @@ func CreateUserHandler(c *fiber.Ctx) error {
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param request body database.User true "Request body"
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /users [patch]
|
||||
func UpdateUserHandler(c *fiber.Ctx) error {
|
||||
@ -124,8 +133,7 @@ func UpdateUserHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err := database.UpdateUser(u, []string{"*"})
|
||||
if err != nil {
|
||||
if err := database.UpdateUser(u, []string{"email", "real_name", "group_id"}); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
@ -140,7 +148,6 @@ func UpdateUserHandler(c *fiber.Ctx) error {
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID" minimum(1)
|
||||
// @Header 200 {string} Token "accessToken=...;refreshToken=..."
|
||||
// @Success 200
|
||||
// @Router /users/:id [delete]
|
||||
func DeleteUserHandler(c *fiber.Ctx) error {
|
||||
@ -152,8 +159,7 @@ func DeleteUserHandler(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
err = database.DeleteUser(id)
|
||||
if err != nil {
|
||||
if err = database.DeleteUser(id); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
@ -162,7 +168,123 @@ func DeleteUserHandler(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func SetLoginTime(u *database.User) error {
|
||||
u.LastLogin = time.Now()
|
||||
return database.UpdateUser(u, []string{"last_login"})
|
||||
// MARK: DeleteUsers godoc
|
||||
// @Summary Delete many users
|
||||
// @Description Delete users by ID
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param request body types.ManyIDs true "Request body"
|
||||
// @Success 200
|
||||
// @Router /users [delete]
|
||||
func DeleteUsersHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
arr := new(types.ManyIDs)
|
||||
if err := c.BodyParser(arr); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := database.DeleteManyUsers(arr.Array); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
// MARK: BlockUsers godoc
|
||||
// @Summary Block many users
|
||||
// @Description Block users by ID
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param request body types.ManyIDs true "Request body"
|
||||
// @Success 200
|
||||
// @Router /users/block [patch]
|
||||
func BlockUsersHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
arr, err := parsers.GetManyIDs(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := database.BlockManyUsers(arr.Array); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
// MARK: UnblockUsers godoc
|
||||
// @Summary Unblock many users
|
||||
// @Description Unblock users by ID
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param request body types.ManyIDs true "Request body"
|
||||
// @Success 200
|
||||
// @Router /users/unblock [patch]
|
||||
func UnblockUsersHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
arr, err := parsers.GetManyIDs(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := database.UnblockManyUsers(arr.Array); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
// MARK: ResetUserPassword godoc
|
||||
// @Summary Reset user password
|
||||
// @Description Reset user password by user ID
|
||||
// @Tags users
|
||||
// @Produce json
|
||||
// @Param id path int true "User ID" minimum(1)
|
||||
// @Success 200 {object} types.NewCredentials
|
||||
// @Router /users/password/:id [patch]
|
||||
func ResetUserPasswordHandler(c *fiber.Ctx) error {
|
||||
// TODO permission validate
|
||||
id, err := c.ParamsInt("id")
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
user, err := database.FindUserByID(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
pass, err := resetPassword(user)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
if err := database.UpdateUser(user, []string{"password_hash", "password_length", "is_required_to_set_password"}); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(types.JSONError{
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(types.NewCredentials{
|
||||
Email: user.Email,
|
||||
Password: pass,
|
||||
})
|
||||
}
|
||||
|
||||
7
types/constructor/plugin.go
Normal file
7
types/constructor/plugin.go
Normal file
@ -0,0 +1,7 @@
|
||||
package constructor
|
||||
|
||||
type Plugin struct {
|
||||
ID uint32 `json:"ID"`
|
||||
Name string `json:"name"`
|
||||
OnlyUDP bool `json:"onlyUdp"`
|
||||
}
|
||||
65
types/constructor/task.go
Normal file
65
types/constructor/task.go
Normal file
@ -0,0 +1,65 @@
|
||||
package constructor
|
||||
|
||||
const (
|
||||
MODE_THROUGHPUT_BPS uint8 = iota // 0
|
||||
MODE_THROUGHPUT_PPS // 1
|
||||
MODE_TCP_CONNECTIONS // 2
|
||||
MODE_USERS // 3
|
||||
)
|
||||
|
||||
const (
|
||||
TYPE_VFIO uint8 = iota // 0
|
||||
TYPE_POSIX // 1
|
||||
)
|
||||
|
||||
const (
|
||||
MAC_MODE_ROUND_ROBIN uint8 = iota // 0 // every packet receives round-robin balancing mac
|
||||
MAC_MODE_IP_HASH // 1 // ip hash, divide and bind to every IPv4/IPv6. MAC-адресов не может быть больше IP-адресов
|
||||
MAC_MODE_RANDOM // 2 // random MAC every packet
|
||||
MAC_MODE_GENERATE // 3 // generate mac for every IPv4/IPv6 and bind it
|
||||
)
|
||||
|
||||
type TaskData struct {
|
||||
Type uint8 `json:"type" validate:"required,min=0,max=255"`
|
||||
Mode uint8 `json:"mode" validate:"required,min=0,max=255"`
|
||||
Time uint32 `json:"time" validate:"required,min=0,max=4294967295"` // in seconds
|
||||
SourceClient map[string]sourceData `json:"sourceClient" validate:"required"`
|
||||
SourceReceiver map[string]sourceData `json:"sourceReceiver" validate:"required"`
|
||||
Plugins []pluginData `json:"plugins" validate:"required"`
|
||||
Performance performanceData `json:"performance" validate:"required"`
|
||||
Tweaks tweaksData `json:"tweaks" validate:"required"` // [[time_in_seconds, value]]: [[10, 6570]]
|
||||
}
|
||||
|
||||
type performanceData struct { // Depends on selected mode
|
||||
// MODE_THROUGHPUT_BPS + MODE_TCP_CONNECTIONS
|
||||
MaxBPS uint64 `json:"maxBps" validate:"min=0,max=18446744073709551615"`
|
||||
// MODE_THROUGHPUT_PPS + MODE_TCP_CONNECTIONS
|
||||
MaxPPS uint32 `json:"maxPps" validate:"min=0,max=4294967295"`
|
||||
// MODE_TCP_CONNECTIONS
|
||||
MaxTCPConnections uint32 `json:"maxTcpConnections" validate:"min=0,max=4294967295"`
|
||||
// MODE_USERS
|
||||
MaxUsers uint32 `json:"maxUsers" validate:"min=0,max=4294967295"`
|
||||
}
|
||||
|
||||
type tweaksData [][]uint8
|
||||
|
||||
type pluginData struct {
|
||||
Plugin uint32 `json:"plugin" validate:"min=0,max=4294967295"`
|
||||
Weight uint8 `json:"weight" validate:"min=0,max=100"` // 0-100
|
||||
}
|
||||
|
||||
type sourceData struct {
|
||||
IPs []string `json:"IPs" validate:"required"` // SRC-IP mask cannot be duplicated into two or more entities within 1 network interface
|
||||
MACs macData `json:"MACs" validate:"required"`
|
||||
NextHops macData `json:"nextHops" validate:"required"`
|
||||
}
|
||||
|
||||
type macData struct {
|
||||
Mode uint8 `json:"mode" validate:"required,min=0,max=255"`
|
||||
Addresses []macAddressData `json:"addresses"`
|
||||
}
|
||||
|
||||
type macAddressData struct {
|
||||
Address string `json:"address" validate:"required"`
|
||||
Weight uint8 `json:"weight" validate:"min=0,max=100"` // 0-100
|
||||
}
|
||||
5
types/many_ids.go
Normal file
5
types/many_ids.go
Normal file
@ -0,0 +1,5 @@
|
||||
package types
|
||||
|
||||
type ManyIDs struct {
|
||||
Array []int `json:"array" validate:"required"`
|
||||
}
|
||||
6
types/new_credentials.go
Normal file
6
types/new_credentials.go
Normal file
@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type NewCredentials struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package types
|
||||
|
||||
type OrderBy struct {
|
||||
Key string `validate:"required"`
|
||||
Order string `validate:"required,endswith=sc"`
|
||||
Key string `query:"key" validate:"required"`
|
||||
Order string `query:"order" validate:"required,endswith=sc"`
|
||||
}
|
||||
|
||||
6
types/search.go
Normal file
6
types/search.go
Normal file
@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type Search struct {
|
||||
Input string `query:"input"`
|
||||
GroupID string `query:"group_id"`
|
||||
}
|
||||
23
utils/generate_password.go
Normal file
23
utils/generate_password.go
Normal file
@ -0,0 +1,23 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/lucasjones/reggen"
|
||||
)
|
||||
|
||||
func GeneratePassword() (string, error) {
|
||||
patterns := []string{
|
||||
`[A-Z]{6,}[\d]{2,}[a-z]{2,}[@$!%*?&]{2,}[A-Za-z]{2,}`,
|
||||
`[A-Za-z]{2,}[a-z]{6,}[\d]{2,}[@$!%*?&]{4,}`,
|
||||
`[\d]{2,}[A-Z]{6,}[@$!%*?&]{4,}[a-z]{2,}`,
|
||||
`[@$!%*?&]{4,}[a-z]{4,}[A-Z]{4,}[\d]{2,}`,
|
||||
`[A-Z]{2,}[@$!%*?&]{6,}[a-z]{4,}[\d]{2,}`,
|
||||
`[A-Z]{4,}[\d]{6,}[@$!%*?&]{4,}[a-z]{2,}`,
|
||||
}
|
||||
|
||||
i := rand.Intn(len(patterns))
|
||||
pass, err := reggen.Generate(patterns[i], 14)
|
||||
|
||||
return pass, err
|
||||
}
|
||||
20
utils/timeout.go
Normal file
20
utils/timeout.go
Normal file
@ -0,0 +1,20 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
||||
c := make(chan struct{})
|
||||
go func() {
|
||||
defer close(c)
|
||||
wg.Wait()
|
||||
}()
|
||||
select {
|
||||
case <-c:
|
||||
return false // completed normally
|
||||
case <-time.After(timeout):
|
||||
return true // timed out
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user