fix: linter and docs
ci-agent / build (push) Failing after 2m50s

This commit is contained in:
d3m0k1d
2026-04-04 19:43:43 +03:00
parent fe7e41e4af
commit 0f8b148279
23 changed files with 480 additions and 105 deletions
+2 -3
View File
@@ -3,11 +3,10 @@ package commander
import ( import (
"bytes" "bytes"
"errors" "errors"
"io"
"os/exec"
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"io"
"os/exec"
) )
type CommandExecutor struct{} type CommandExecutor struct{}
+20 -4
View File
@@ -160,12 +160,28 @@ func main() {
// User management (admin only) - Full CRUD // User management (admin only) - Full CRUD
authTokenGroup.GET("/users/:login", handlers.RequireAdmin(), auth.GetUser) authTokenGroup.GET("/users/:login", handlers.RequireAdmin(), auth.GetUser)
authTokenGroup.PUT("/users/:login", handlers.RequireAdmin(), auth.UpdateUser) authTokenGroup.PUT("/users/:login", handlers.RequireAdmin(), auth.UpdateUser)
authTokenGroup.PUT("/users/:login/permissions", handlers.RequireAdmin(), auth.UpdateUserPermissions) authTokenGroup.PUT(
authTokenGroup.PUT("/users/:login/password", handlers.RequireAdmin(), auth.ResetUserPassword) "/users/:login/permissions",
handlers.RequireAdmin(),
auth.UpdateUserPermissions,
)
authTokenGroup.PUT(
"/users/:login/password",
handlers.RequireAdmin(),
auth.ResetUserPassword,
)
// User activation management (admin only) // User activation management (admin only)
authTokenGroup.POST("/users/:login/activate", handlers.RequireAdmin(), auth.ActivateUser) authTokenGroup.POST(
authTokenGroup.POST("/users/:login/deactivate", handlers.RequireAdmin(), auth.DeactivateUser) "/users/:login/activate",
handlers.RequireAdmin(),
auth.ActivateUser,
)
authTokenGroup.POST(
"/users/:login/deactivate",
handlers.RequireAdmin(),
auth.DeactivateUser,
)
authTokenGroup.GET("/users/inactive", handlers.RequireAdmin(), auth.ListInactiveUsers) authTokenGroup.GET("/users/inactive", handlers.RequireAdmin(), auth.ListInactiveUsers)
} }
+95
View File
@@ -295,6 +295,11 @@ const docTemplate = `{
}, },
"/auth/token": { "/auth/token": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Creates a new user with permissions", "description": "Creates a new user with permissions",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -354,6 +359,11 @@ const docTemplate = `{
} }
}, },
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Deletes the current authenticated user", "description": "Deletes the current authenticated user",
"tags": [ "tags": [
"auth" "auth"
@@ -392,6 +402,11 @@ const docTemplate = `{
}, },
"/auth/tokens": { "/auth/tokens": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns list of all users with their permissions", "description": "Returns list of all users with their permissions",
"produces": [ "produces": [
"application/json" "application/json"
@@ -424,6 +439,11 @@ const docTemplate = `{
}, },
"/auth/tokens/:login": { "/auth/tokens/:login": {
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Deletes a user by their login", "description": "Deletes a user by their login",
"tags": [ "tags": [
"auth" "auth"
@@ -471,6 +491,11 @@ const docTemplate = `{
}, },
"/auth/users/:login": { "/auth/users/:login": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns a user by their login (admin only)", "description": "Returns a user by their login (admin only)",
"produces": [ "produces": [
"application/json" "application/json"
@@ -525,6 +550,11 @@ const docTemplate = `{
} }
}, },
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates a user's name and last name (admin only)", "description": "Updates a user's name and last name (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -593,6 +623,11 @@ const docTemplate = `{
}, },
"/auth/users/:login/activate": { "/auth/users/:login/activate": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Activates a user account by login (admin only)", "description": "Activates a user account by login (admin only)",
"tags": [ "tags": [
"auth" "auth"
@@ -649,6 +684,11 @@ const docTemplate = `{
}, },
"/auth/users/:login/deactivate": { "/auth/users/:login/deactivate": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Deactivates a user account by login (admin only)", "description": "Deactivates a user account by login (admin only)",
"tags": [ "tags": [
"auth" "auth"
@@ -705,6 +745,11 @@ const docTemplate = `{
}, },
"/auth/users/:login/password": { "/auth/users/:login/password": {
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Resets a user's password to a new value (admin only)", "description": "Resets a user's password to a new value (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -773,6 +818,11 @@ const docTemplate = `{
}, },
"/auth/users/:login/permissions": { "/auth/users/:login/permissions": {
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates a user's permissions and activation status (admin only)", "description": "Updates a user's permissions and activation status (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -841,6 +891,11 @@ const docTemplate = `{
}, },
"/auth/users/inactive": { "/auth/users/inactive": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns list of all users waiting for activation", "description": "Returns list of all users waiting for activation",
"produces": [ "produces": [
"application/json" "application/json"
@@ -873,6 +928,11 @@ const docTemplate = `{
}, },
"/auth/validate": { "/auth/validate": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Check if the provided Bearer token is valid and return its permissions", "description": "Check if the provided Bearer token is valid and return its permissions",
"produces": [ "produces": [
"application/json" "application/json"
@@ -902,6 +962,11 @@ const docTemplate = `{
}, },
"/jobs": { "/jobs": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Sends a command to the specified agent, waits for execution, and returns the result", "description": "Sends a command to the specified agent, waits for execution, and returns the result",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1238,6 +1303,11 @@ const docTemplate = `{
}, },
"/scripts/interpreters": { "/scripts/interpreters": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns all script interpreters available in the system", "description": "Returns all script interpreters available in the system",
"produces": [ "produces": [
"application/json" "application/json"
@@ -1259,6 +1329,11 @@ const docTemplate = `{
} }
}, },
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Registers a new script interpreter with name, label, and argv", "description": "Registers a new script interpreter with name, label, and argv",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1293,6 +1368,11 @@ const docTemplate = `{
}, },
"/scripts/interpreters/:id": { "/scripts/interpreters/:id": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns a script interpreter by ID", "description": "Returns a script interpreter by ID",
"produces": [ "produces": [
"application/json" "application/json"
@@ -1320,6 +1400,11 @@ const docTemplate = `{
} }
}, },
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates fields of a script interpreter", "description": "Updates fields of a script interpreter",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1359,6 +1444,11 @@ const docTemplate = `{
} }
}, },
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Removes a script interpreter by ID", "description": "Removes a script interpreter by ID",
"tags": [ "tags": [
"scripts" "scripts"
@@ -1382,6 +1472,11 @@ const docTemplate = `{
}, },
"/scripts/run": { "/scripts/run": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Resolves interpreter argv[] and sends the full command to the agent", "description": "Resolves interpreter argv[] and sends the full command to the agent",
"consumes": [ "consumes": [
"application/json" "application/json"
+95
View File
@@ -284,6 +284,11 @@
}, },
"/auth/token": { "/auth/token": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Creates a new user with permissions", "description": "Creates a new user with permissions",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -343,6 +348,11 @@
} }
}, },
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Deletes the current authenticated user", "description": "Deletes the current authenticated user",
"tags": [ "tags": [
"auth" "auth"
@@ -381,6 +391,11 @@
}, },
"/auth/tokens": { "/auth/tokens": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns list of all users with their permissions", "description": "Returns list of all users with their permissions",
"produces": [ "produces": [
"application/json" "application/json"
@@ -413,6 +428,11 @@
}, },
"/auth/tokens/:login": { "/auth/tokens/:login": {
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Deletes a user by their login", "description": "Deletes a user by their login",
"tags": [ "tags": [
"auth" "auth"
@@ -460,6 +480,11 @@
}, },
"/auth/users/:login": { "/auth/users/:login": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns a user by their login (admin only)", "description": "Returns a user by their login (admin only)",
"produces": [ "produces": [
"application/json" "application/json"
@@ -514,6 +539,11 @@
} }
}, },
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates a user's name and last name (admin only)", "description": "Updates a user's name and last name (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -582,6 +612,11 @@
}, },
"/auth/users/:login/activate": { "/auth/users/:login/activate": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Activates a user account by login (admin only)", "description": "Activates a user account by login (admin only)",
"tags": [ "tags": [
"auth" "auth"
@@ -638,6 +673,11 @@
}, },
"/auth/users/:login/deactivate": { "/auth/users/:login/deactivate": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Deactivates a user account by login (admin only)", "description": "Deactivates a user account by login (admin only)",
"tags": [ "tags": [
"auth" "auth"
@@ -694,6 +734,11 @@
}, },
"/auth/users/:login/password": { "/auth/users/:login/password": {
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Resets a user's password to a new value (admin only)", "description": "Resets a user's password to a new value (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -762,6 +807,11 @@
}, },
"/auth/users/:login/permissions": { "/auth/users/:login/permissions": {
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates a user's permissions and activation status (admin only)", "description": "Updates a user's permissions and activation status (admin only)",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -830,6 +880,11 @@
}, },
"/auth/users/inactive": { "/auth/users/inactive": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns list of all users waiting for activation", "description": "Returns list of all users waiting for activation",
"produces": [ "produces": [
"application/json" "application/json"
@@ -862,6 +917,11 @@
}, },
"/auth/validate": { "/auth/validate": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Check if the provided Bearer token is valid and return its permissions", "description": "Check if the provided Bearer token is valid and return its permissions",
"produces": [ "produces": [
"application/json" "application/json"
@@ -891,6 +951,11 @@
}, },
"/jobs": { "/jobs": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Sends a command to the specified agent, waits for execution, and returns the result", "description": "Sends a command to the specified agent, waits for execution, and returns the result",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1227,6 +1292,11 @@
}, },
"/scripts/interpreters": { "/scripts/interpreters": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns all script interpreters available in the system", "description": "Returns all script interpreters available in the system",
"produces": [ "produces": [
"application/json" "application/json"
@@ -1248,6 +1318,11 @@
} }
}, },
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Registers a new script interpreter with name, label, and argv", "description": "Registers a new script interpreter with name, label, and argv",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1282,6 +1357,11 @@
}, },
"/scripts/interpreters/:id": { "/scripts/interpreters/:id": {
"get": { "get": {
"security": [
{
"Bearer": []
}
],
"description": "Returns a script interpreter by ID", "description": "Returns a script interpreter by ID",
"produces": [ "produces": [
"application/json" "application/json"
@@ -1309,6 +1389,11 @@
} }
}, },
"put": { "put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates fields of a script interpreter", "description": "Updates fields of a script interpreter",
"consumes": [ "consumes": [
"application/json" "application/json"
@@ -1348,6 +1433,11 @@
} }
}, },
"delete": { "delete": {
"security": [
{
"Bearer": []
}
],
"description": "Removes a script interpreter by ID", "description": "Removes a script interpreter by ID",
"tags": [ "tags": [
"scripts" "scripts"
@@ -1371,6 +1461,11 @@
}, },
"/scripts/run": { "/scripts/run": {
"post": { "post": {
"security": [
{
"Bearer": []
}
],
"description": "Resolves interpreter argv[] and sends the full command to the agent", "description": "Resolves interpreter argv[] and sends the full command to the agent",
"consumes": [ "consumes": [
"application/json" "application/json"
+38
View File
@@ -597,6 +597,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Delete my account summary: Delete my account
tags: tags:
- auth - auth
@@ -636,6 +638,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Create user summary: Create user
tags: tags:
- auth - auth
@@ -657,6 +661,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: List users summary: List users
tags: tags:
- auth - auth
@@ -688,6 +694,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Delete user summary: Delete user
tags: tags:
- auth - auth
@@ -725,6 +733,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Get user by login summary: Get user by login
tags: tags:
- auth - auth
@@ -769,6 +779,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Update user summary: Update user
tags: tags:
- auth - auth
@@ -806,6 +818,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Activate user summary: Activate user
tags: tags:
- auth - auth
@@ -843,6 +857,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Deactivate user summary: Deactivate user
tags: tags:
- auth - auth
@@ -888,6 +904,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Reset user password summary: Reset user password
tags: tags:
- auth - auth
@@ -933,6 +951,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Update user permissions summary: Update user permissions
tags: tags:
- auth - auth
@@ -954,6 +974,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: List inactive users summary: List inactive users
tags: tags:
- auth - auth
@@ -973,6 +995,8 @@ paths:
additionalProperties: additionalProperties:
type: string type: string
type: object type: object
security:
- Bearer: []
summary: Validate token summary: Validate token
tags: tags:
- auth - auth
@@ -996,6 +1020,8 @@ paths:
description: Created description: Created
schema: schema:
$ref: '#/definitions/internal_handlers.AddJobOut' $ref: '#/definitions/internal_handlers.AddJobOut'
security:
- Bearer: []
summary: Create and run a job on an agent summary: Create and run a job on an agent
tags: tags:
- jobs - jobs
@@ -1202,6 +1228,8 @@ paths:
items: items:
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter' $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
type: array type: array
security:
- Bearer: []
summary: List interpreters summary: List interpreters
tags: tags:
- scripts - scripts
@@ -1223,6 +1251,8 @@ paths:
description: Created description: Created
schema: schema:
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter' $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
security:
- Bearer: []
summary: Create interpreter summary: Create interpreter
tags: tags:
- scripts - scripts
@@ -1238,6 +1268,8 @@ paths:
responses: responses:
"204": "204":
description: No Content description: No Content
security:
- Bearer: []
summary: Delete interpreter summary: Delete interpreter
tags: tags:
- scripts - scripts
@@ -1256,6 +1288,8 @@ paths:
description: OK description: OK
schema: schema:
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter' $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
security:
- Bearer: []
summary: Get interpreter summary: Get interpreter
tags: tags:
- scripts - scripts
@@ -1282,6 +1316,8 @@ paths:
description: OK description: OK
schema: schema:
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter' $ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
security:
- Bearer: []
summary: Update interpreter summary: Update interpreter
tags: tags:
- scripts - scripts
@@ -1304,6 +1340,8 @@ paths:
description: Created description: Created
schema: schema:
$ref: '#/definitions/internal_handlers.RunScriptOut' $ref: '#/definitions/internal_handlers.RunScriptOut'
security:
- Bearer: []
summary: Run a script on an agent summary: Run a script on an agent
tags: tags:
- scripts - scripts
+20 -12
View File
@@ -12,10 +12,10 @@ import (
// Executor handles running Ansible playbooks // Executor handles running Ansible playbooks
type Executor struct { type Executor struct {
workDir string workDir string
grpcServerHost string grpcServerHost string
grpcServerPort string grpcServerPort string
backendURL string backendURL string
} }
// ExecutorConfig holds configuration for the Executor // ExecutorConfig holds configuration for the Executor
@@ -23,26 +23,26 @@ type ExecutorConfig struct {
WorkDir string WorkDir string
GRPCServerHost string GRPCServerHost string
GRPCServerPort string GRPCServerPort string
BackendURL string BackendURL string
} }
// NewExecutor creates a new Ansible executor // NewExecutor creates a new Ansible executor
func NewExecutor(cfg ExecutorConfig) *Executor { func NewExecutor(cfg ExecutorConfig) *Executor {
return &Executor{ return &Executor{
workDir: cfg.WorkDir, workDir: cfg.WorkDir,
grpcServerHost: cfg.GRPCServerHost, grpcServerHost: cfg.GRPCServerHost,
grpcServerPort: cfg.GRPCServerPort, grpcServerPort: cfg.GRPCServerPort,
backendURL: cfg.BackendURL, backendURL: cfg.BackendURL,
} }
} }
// DeployResult holds the result of a deployment // DeployResult holds the result of a deployment
type DeployResult struct { type DeployResult struct {
Host string Host string
Success bool Success bool
Stdout string Stdout string
Stderr string Stderr string
Err error Err error
} }
// WorkDir returns the work directory path // WorkDir returns the work directory path
@@ -51,7 +51,11 @@ func (e *Executor) WorkDir() string {
} }
// Deploy runs Ansible playbook for the given inventory // Deploy runs Ansible playbook for the given inventory
func (e *Executor) Deploy(ctx context.Context, inventoryPath string, deployType string) ([]DeployResult, error) { func (e *Executor) Deploy(
ctx context.Context,
inventoryPath string,
deployType string,
) ([]DeployResult, error) {
playbookName := "binary_deploy.yml" playbookName := "binary_deploy.yml"
if deployType == "docker" { if deployType == "docker" {
playbookName = "docker_deploy.yml" playbookName = "docker_deploy.yml"
@@ -84,7 +88,11 @@ func (e *Executor) Deploy(ctx context.Context, inventoryPath string, deployType
} }
// DeployParallel runs Ansible playbook for multiple inventories in parallel // DeployParallel runs Ansible playbook for multiple inventories in parallel
func (e *Executor) DeployParallel(ctx context.Context, inventoryPaths []string, deployType string) (map[string][]DeployResult, error) { func (e *Executor) DeployParallel(
ctx context.Context,
inventoryPaths []string,
deployType string,
) (map[string][]DeployResult, error) {
var wg sync.WaitGroup var wg sync.WaitGroup
results := make(map[string][]DeployResult) results := make(map[string][]DeployResult)
errCh := make(chan error, len(inventoryPaths)) errCh := make(chan error, len(inventoryPaths))
@@ -82,7 +82,10 @@ func (c *Collector) Stream(stream proto.Collector_StreamServer) error {
// If no ClickHouse, just consume the stream without storing // If no ClickHouse, just consume the stream without storing
if !c.logRepo.IsConnected() { if !c.logRepo.IsConnected() {
log.Printf("Warning: ClickHouse not connected yet, consuming logs without storing for agent %s", agentName) log.Printf(
"Warning: ClickHouse not connected yet, consuming logs without storing for agent %s",
agentName,
)
for { for {
_, err := stream.Recv() _, err := stream.Recv()
if err == io.EOF { if err == io.EOF {
@@ -120,7 +123,12 @@ func (c *Collector) Stream(stream proto.Collector_StreamServer) error {
return nil return nil
} }
if err := c.logRepo.InsertBatch(stream.Context(), batch); err != nil { if err := c.logRepo.InsertBatch(stream.Context(), batch); err != nil {
log.Printf("Failed to insert batch for agent %s, service %s: %v", agentName, service, err) log.Printf(
"Failed to insert batch for agent %s, service %s: %v",
agentName,
service,
err,
)
return err return err
} }
log.Printf("Flushed %d logs for agent %s, service %s", len(batch), agentName, service) log.Printf("Flushed %d logs for agent %s, service %s", len(batch), agentName, service)
@@ -95,7 +95,9 @@ func (self *Agent) WaitJob(jid int64) (*models.Job, error) {
return &result.fc, result.err return &result.fc, result.err
} }
func (self *Commander) Stream(bidi grpc.BidiStreamingServer[proto.FinishedCommand, proto.Command]) error { func (self *Commander) Stream(
bidi grpc.BidiStreamingServer[proto.FinishedCommand, proto.Command],
) error {
md, ok := metadata.FromIncomingContext(bidi.Context()) md, ok := metadata.FromIncomingContext(bidi.Context())
if !ok { if !ok {
return fmt.Errorf("no metadata in context") return fmt.Errorf("no metadata in context")
@@ -164,7 +166,12 @@ func (self *Agent) send() error {
// self.jobs[] // self.jobs[]
} }
func newAgent(bidi grpc.BidiStreamingServer[proto.FinishedCommand, proto.Command], jobber Jobber, aid string, label string) Agent { func newAgent(
bidi grpc.BidiStreamingServer[proto.FinishedCommand, proto.Command],
jobber Jobber,
aid string,
label string,
) Agent {
return Agent{ return Agent{
bidi: bidi, bidi: bidi,
in: make(chan *proto.Command), in: make(chan *proto.Command),
+1 -1
View File
@@ -38,7 +38,7 @@ func NewAgentDeployGroup(h *Handlers) *AgentDeployGroup {
WorkDir: workDir, WorkDir: workDir,
GRPCServerHost: "0.0.0.0", // TODO: make configurable GRPCServerHost: "0.0.0.0", // TODO: make configurable
GRPCServerPort: grpcPort, GRPCServerPort: grpcPort,
BackendURL: backendURL, BackendURL: backendURL,
}) })
// Write playbooks on init // Write playbooks on init
+1 -1
View File
@@ -104,7 +104,7 @@ func (arg *AgentRegistrationGroup) Register(c *gin.Context) {
} }
type RegisterRequest struct { type RegisterRequest struct {
CSR string `json:"csr" binding:"required"` CSR string `json:"csr" binding:"required"`
Token string `json:"token" binding:"required"` Token string `json:"token" binding:"required"`
} }
+2 -1
View File
@@ -1,9 +1,10 @@
package handlers package handlers
import ( import (
"net/http"
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/collector" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/collector"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
) )
type AgentsGroup struct { type AgentsGroup struct {
+12
View File
@@ -90,6 +90,7 @@ func (ag *AuthGroup) RegisterUser(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string // @Failure 401 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/token [post] // @Router /auth/token [post]
func (ag *AuthGroup) CreateToken(c *gin.Context) { func (ag *AuthGroup) CreateToken(c *gin.Context) {
var tc repository.TokenCreate var tc repository.TokenCreate
@@ -113,6 +114,7 @@ func (ag *AuthGroup) CreateToken(c *gin.Context) {
// @Produce json // @Produce json
// @Success 200 {object} repository.Tokens // @Success 200 {object} repository.Tokens
// @Failure 401 {object} map[string]string // @Failure 401 {object} map[string]string
// @Security Bearer
// @Router /auth/validate [get] // @Router /auth/validate [get]
func (ag *AuthGroup) ValidateToken(c *gin.Context) { func (ag *AuthGroup) ValidateToken(c *gin.Context) {
tokenVal, exists := c.Get(string(tokenContextKey)) tokenVal, exists := c.Get(string(tokenContextKey))
@@ -137,6 +139,7 @@ func (ag *AuthGroup) ValidateToken(c *gin.Context) {
// @Produce json // @Produce json
// @Success 200 {array} repository.Tokens // @Success 200 {array} repository.Tokens
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/tokens [get] // @Router /auth/tokens [get]
func (ag *AuthGroup) ListTokens(c *gin.Context) { func (ag *AuthGroup) ListTokens(c *gin.Context) {
tokens, err := ag.Repo.ListTokens() tokens, err := ag.Repo.ListTokens()
@@ -155,6 +158,7 @@ func (ag *AuthGroup) ListTokens(c *gin.Context) {
// @Success 200 {object} map[string]string // @Success 200 {object} map[string]string
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/tokens/:login [delete] // @Router /auth/tokens/:login [delete]
func (ag *AuthGroup) DeleteToken(c *gin.Context) { func (ag *AuthGroup) DeleteToken(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -182,6 +186,7 @@ func (ag *AuthGroup) DeleteToken(c *gin.Context) {
// @Success 200 {object} map[string]string // @Success 200 {object} map[string]string
// @Failure 401 {object} map[string]string // @Failure 401 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/token [delete] // @Router /auth/token [delete]
func (ag *AuthGroup) DeleteMyToken(c *gin.Context) { func (ag *AuthGroup) DeleteMyToken(c *gin.Context) {
tokenVal, exists := c.Get(string(tokenContextKey)) tokenVal, exists := c.Get(string(tokenContextKey))
@@ -213,6 +218,7 @@ func (ag *AuthGroup) DeleteMyToken(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login/activate [post] // @Router /auth/users/:login/activate [post]
func (ag *AuthGroup) ActivateUser(c *gin.Context) { func (ag *AuthGroup) ActivateUser(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -242,6 +248,7 @@ func (ag *AuthGroup) ActivateUser(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login/deactivate [post] // @Router /auth/users/:login/deactivate [post]
func (ag *AuthGroup) DeactivateUser(c *gin.Context) { func (ag *AuthGroup) DeactivateUser(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -269,6 +276,7 @@ func (ag *AuthGroup) DeactivateUser(c *gin.Context) {
// @Produce json // @Produce json
// @Success 200 {array} repository.Tokens // @Success 200 {array} repository.Tokens
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/inactive [get] // @Router /auth/users/inactive [get]
func (ag *AuthGroup) ListInactiveUsers(c *gin.Context) { func (ag *AuthGroup) ListInactiveUsers(c *gin.Context) {
tokens, err := ag.Repo.ListInactiveTokens() tokens, err := ag.Repo.ListInactiveTokens()
@@ -289,6 +297,7 @@ func (ag *AuthGroup) ListInactiveUsers(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login [get] // @Router /auth/users/:login [get]
func (ag *AuthGroup) GetUser(c *gin.Context) { func (ag *AuthGroup) GetUser(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -321,6 +330,7 @@ func (ag *AuthGroup) GetUser(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login [put] // @Router /auth/users/:login [put]
func (ag *AuthGroup) UpdateUser(c *gin.Context) { func (ag *AuthGroup) UpdateUser(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -358,6 +368,7 @@ func (ag *AuthGroup) UpdateUser(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login/permissions [put] // @Router /auth/users/:login/permissions [put]
func (ag *AuthGroup) UpdateUserPermissions(c *gin.Context) { func (ag *AuthGroup) UpdateUserPermissions(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
@@ -395,6 +406,7 @@ func (ag *AuthGroup) UpdateUserPermissions(c *gin.Context) {
// @Failure 400 {object} map[string]string // @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string // @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string // @Failure 500 {object} map[string]string
// @Security Bearer
// @Router /auth/users/:login/password [put] // @Router /auth/users/:login/password [put]
func (ag *AuthGroup) ResetUserPassword(c *gin.Context) { func (ag *AuthGroup) ResetUserPassword(c *gin.Context) {
login := c.Param("login") login := c.Param("login")
+4 -2
View File
@@ -20,8 +20,10 @@ func CorsMiddleware(origincfg string) gin.HandlerFunc {
} }
c.Writer.Header().Set("Access-Control-Allow-Origin", origin) c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
// c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, Authorization") c.Writer.Header().
c.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH, DELETE, PUT") Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, Authorization")
c.Writer.Header().
Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH, DELETE, PUT")
if c.Request.Method == "OPTIONS" { if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent) c.AbortWithStatus(http.StatusNoContent)
+8 -3
View File
@@ -23,10 +23,10 @@ func NewJobsHandlers(cmder *commander.Commander, svc *service.ScriptService) Job
} }
type AddJobIn struct { type AddJobIn struct {
Command string `json:"command" binding:"required"` Command string `json:"command" binding:"required"`
InterpreterID int64 `json:"interpreter_id"` InterpreterID int64 `json:"interpreter_id"`
Stdin *string `json:"stdin"` Stdin *string `json:"stdin"`
AgentID string `json:"agent_id" binding:"required"` AgentID string `json:"agent_id" binding:"required"`
} }
type AddJobOut struct { type AddJobOut struct {
ID int64 `json:"id"` ID int64 `json:"id"`
@@ -45,6 +45,7 @@ type AddJobOut struct {
// @Produce json // @Produce json
// @Param body body AddJobIn true "Job request" // @Param body body AddJobIn true "Job request"
// @Success 201 {object} AddJobOut // @Success 201 {object} AddJobOut
// @Security Bearer
// @Router /jobs [post] // @Router /jobs [post]
func (self *JobsHandlers) AddJob(c *gin.Context) { func (self *JobsHandlers) AddJob(c *gin.Context) {
err := func() error { err := func() error {
@@ -63,7 +64,11 @@ func (self *JobsHandlers) AddJob(c *gin.Context) {
command = []string{"sh", "-c", in.Command} command = []string{"sh", "-c", in.Command}
} else { } else {
var err error var err error
command, err = self.svc.ResolveCommand(c.Request.Context(), in.InterpreterID, in.Command) command, err = self.svc.ResolveCommand(
c.Request.Context(),
in.InterpreterID,
in.Command,
)
if err != nil { if err != nil {
return err return err
} }
+9 -9
View File
@@ -20,10 +20,10 @@ func NewLogHandlers(logRepo *repository.LogRepository) *LogHandlers {
type InsertLogRequest struct { type InsertLogRequest struct {
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Level string `json:"level" binding:"required"` Level string `json:"level" binding:"required"`
Service string `json:"service" binding:"required"` Service string `json:"service" binding:"required"`
Agent string `json:"agent" binding:"required"` Agent string `json:"agent" binding:"required"`
Message string `json:"message" binding:"required"` Message string `json:"message" binding:"required"`
} }
// @Summary Insert log entry // @Summary Insert log entry
@@ -105,13 +105,13 @@ func (lh *LogHandlers) InsertBatch(c *gin.Context) {
} }
type SearchLogsRequest struct { type SearchLogsRequest struct {
Level string `form:"level"` Level string `form:"level"`
Service string `form:"service"` Service string `form:"service"`
Agent string `form:"agent"` Agent string `form:"agent"`
DateFrom string `form:"date_from"` DateFrom string `form:"date_from"`
DateTo string `form:"date_to"` DateTo string `form:"date_to"`
Limit int `form:"limit"` Limit int `form:"limit"`
Offset int `form:"offset"` Offset int `form:"offset"`
} }
// @Summary Search logs // @Summary Search logs
+13 -3
View File
@@ -22,9 +22,9 @@ func NewScriptHandlers(svc *service.ScriptService, cmder *commander.Commander) S
} }
type RunScriptIn struct { type RunScriptIn struct {
AgentID string `json:"agent_id" binding:"required"` AgentID string `json:"agent_id" binding:"required"`
InterpreterID int64 `json:"interpreter_id" binding:"required"` InterpreterID int64 `json:"interpreter_id" binding:"required"`
ScriptText string `json:"script_text" binding:"required"` ScriptText string `json:"script_text" binding:"required"`
Stdin *string `json:"stdin"` Stdin *string `json:"stdin"`
} }
@@ -45,6 +45,7 @@ type RunScriptOut struct {
// @Produce json // @Produce json
// @Param body body RunScriptIn true "Script request" // @Param body body RunScriptIn true "Script request"
// @Success 201 {object} RunScriptOut // @Success 201 {object} RunScriptOut
// @Security Bearer
// @Router /scripts/run [post] // @Router /scripts/run [post]
func (self *ScriptHandlers) RunScript(c *gin.Context) { func (self *ScriptHandlers) RunScript(c *gin.Context) {
err := func() error { err := func() error {
@@ -53,7 +54,11 @@ func (self *ScriptHandlers) RunScript(c *gin.Context) {
return err return err
} }
command, err := self.svc.ResolveCommand(c.Request.Context(), in.InterpreterID, in.ScriptText) command, err := self.svc.ResolveCommand(
c.Request.Context(),
in.InterpreterID,
in.ScriptText,
)
if err != nil { if err != nil {
return err return err
} }
@@ -98,6 +103,7 @@ func (self *ScriptHandlers) RunScript(c *gin.Context) {
// @Tags scripts // @Tags scripts
// @Produce json // @Produce json
// @Success 200 {array} repository.ScriptInterpreter // @Success 200 {array} repository.ScriptInterpreter
// @Security Bearer
// @Router /scripts/interpreters [get] // @Router /scripts/interpreters [get]
func (self *ScriptHandlers) ListInterpreters(c *gin.Context) { func (self *ScriptHandlers) ListInterpreters(c *gin.Context) {
interpreters, err := self.svc.List(c.Request.Context()) interpreters, err := self.svc.List(c.Request.Context())
@@ -116,6 +122,7 @@ func (self *ScriptHandlers) ListInterpreters(c *gin.Context) {
// @Produce json // @Produce json
// @Param body body repository.ScriptInterpreterCreate true "Interpreter definition" // @Param body body repository.ScriptInterpreterCreate true "Interpreter definition"
// @Success 201 {object} repository.ScriptInterpreter // @Success 201 {object} repository.ScriptInterpreter
// @Security Bearer
// @Router /scripts/interpreters [post] // @Router /scripts/interpreters [post]
func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) { func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) {
var in repository.ScriptInterpreterCreate var in repository.ScriptInterpreterCreate
@@ -139,6 +146,7 @@ func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) {
// @Produce json // @Produce json
// @Param id path int true "Interpreter ID" // @Param id path int true "Interpreter ID"
// @Success 200 {object} repository.ScriptInterpreter // @Success 200 {object} repository.ScriptInterpreter
// @Security Bearer
// @Router /scripts/interpreters/:id [get] // @Router /scripts/interpreters/:id [get]
func (self *ScriptHandlers) GetInterpreter(c *gin.Context) { func (self *ScriptHandlers) GetInterpreter(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64) id, err := strconv.ParseInt(c.Param("id"), 10, 64)
@@ -164,6 +172,7 @@ func (self *ScriptHandlers) GetInterpreter(c *gin.Context) {
// @Param id path int true "Interpreter ID" // @Param id path int true "Interpreter ID"
// @Param body body repository.ScriptInterpreterUpdate true "Interpreter fields" // @Param body body repository.ScriptInterpreterUpdate true "Interpreter fields"
// @Success 200 {object} repository.ScriptInterpreter // @Success 200 {object} repository.ScriptInterpreter
// @Security Bearer
// @Router /scripts/interpreters/:id [put] // @Router /scripts/interpreters/:id [put]
func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) { func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64) id, err := strconv.ParseInt(c.Param("id"), 10, 64)
@@ -192,6 +201,7 @@ func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) {
// @Tags scripts // @Tags scripts
// @Param id path int true "Interpreter ID" // @Param id path int true "Interpreter ID"
// @Success 204 // @Success 204
// @Security Bearer
// @Router /scripts/interpreters/:id [delete] // @Router /scripts/interpreters/:id [delete]
func (self *ScriptHandlers) DeleteInterpreter(c *gin.Context) { func (self *ScriptHandlers) DeleteInterpreter(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64) id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+23 -8
View File
@@ -23,7 +23,11 @@ func (r *JobRepository) Init(ctx context.Context) error {
return err return err
} }
func (r *JobRepository) InitJob(ctx context.Context, agentID string, job models.JobForInsert) (int64, error) { func (r *JobRepository) InitJob(
ctx context.Context,
agentID string,
job models.JobForInsert,
) (int64, error) {
commandJSON, err := json.Marshal(job.Command) commandJSON, err := json.Marshal(job.Command)
if err != nil { if err != nil {
return 0, fmt.Errorf("marshal command: %w", err) return 0, fmt.Errorf("marshal command: %w", err)
@@ -34,9 +38,12 @@ func (r *JobRepository) InitJob(ctx context.Context, agentID string, job models.
stdinVal = job.Stdin stdinVal = job.Stdin
} }
result, err := r.DB.ExecContext(ctx, result, err := r.DB.ExecContext(
ctx,
`INSERT INTO jobs (agent_id, command, stdin, stdout, stderr, status) VALUES (?, ?, ?, '', '', 0)`, `INSERT INTO jobs (agent_id, command, stdin, stdout, stderr, status) VALUES (?, ?, ?, '', '', 0)`,
agentID, string(commandJSON), stdinVal, agentID,
string(commandJSON),
stdinVal,
) )
if err != nil { if err != nil {
return 0, err return 0, err
@@ -45,10 +52,18 @@ func (r *JobRepository) InitJob(ctx context.Context, agentID string, job models.
return result.LastInsertId() return result.LastInsertId()
} }
func (r *JobRepository) UpdateJobInDB(ctx context.Context, jid int64, msg models.JobForUpdate) (models.Job, error) { func (r *JobRepository) UpdateJobInDB(
result, err := r.DB.ExecContext(ctx, ctx context.Context,
jid int64,
msg models.JobForUpdate,
) (models.Job, error) {
result, err := r.DB.ExecContext(
ctx,
`UPDATE jobs SET stdout = ?, stderr = ?, status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, `UPDATE jobs SET stdout = ?, stderr = ?, status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
msg.Stdout, msg.Stderr, msg.Status, jid, msg.Stdout,
msg.Stderr,
msg.Status,
jid,
) )
if err != nil { if err != nil {
return models.Job{}, err return models.Job{}, err
@@ -81,10 +96,10 @@ func (r *JobRepository) GetJobByID(ctx context.Context, jid int64) (models.Job,
return models.Job{}, err return models.Job{}, err
} }
if err := json.Unmarshal([]byte(commandJSON), &job.JobForInsert.Command); err != nil { if err := json.Unmarshal([]byte(commandJSON), &job.Command); err != nil {
return models.Job{}, fmt.Errorf("unmarshal command: %w", err) return models.Job{}, fmt.Errorf("unmarshal command: %w", err)
} }
job.JobForInsert.Stdin = stdinVal job.Stdin = stdinVal
return job, nil return job, nil
} }
+12 -6
View File
@@ -84,13 +84,13 @@ func (r *LogRepository) InsertBatch(ctx context.Context, logs []storage.LogEntry
} }
type LogFilter struct { type LogFilter struct {
Level string Level string
Service string Service string
Agent string Agent string
DateFrom time.Time DateFrom time.Time
DateTo time.Time DateTo time.Time
Limit int Limit int
Offset int Offset int
} }
func (r *LogRepository) Search(ctx context.Context, filter LogFilter) ([]storage.LogEntry, error) { func (r *LogRepository) Search(ctx context.Context, filter LogFilter) ([]storage.LogEntry, error) {
@@ -157,7 +157,13 @@ func (r *LogRepository) Search(ctx context.Context, filter LogFilter) ([]storage
logs := make([]storage.LogEntry, 0) logs := make([]storage.LogEntry, 0)
for rows.Next() { for rows.Next() {
var log storage.LogEntry var log storage.LogEntry
if err := rows.Scan(&log.Timestamp, &log.Level, &log.Service, &log.Agent, &log.Message); err != nil { if err := rows.Scan(
&log.Timestamp,
&log.Level,
&log.Service,
&log.Agent,
&log.Message,
); err != nil {
return nil, err return nil, err
} }
logs = append(logs, log) logs = append(logs, log)
+31 -31
View File
@@ -2,23 +2,23 @@ package repository
// Tokens represents a user record with info and permissions. // Tokens represents a user record with info and permissions.
type Tokens struct { type Tokens struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
LastName string `json:"last_name"` LastName string `json:"last_name"`
Login string `json:"login"` Login string `json:"login"`
Token string `json:"token"` Token string `json:"token"`
PermissionView bool `json:"permission_view"` PermissionView bool `json:"permission_view"`
PermissionManage bool `json:"permission_manage_agent"` PermissionManage bool `json:"permission_manage_agent"`
PermissionAdmin bool `json:"permission_admin"` PermissionAdmin bool `json:"permission_admin"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
// TokenCreate is the request body for creating a new user. // TokenCreate is the request body for creating a new user.
type TokenCreate struct { type TokenCreate struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
LastName string `json:"last_name" binding:"required"` LastName string `json:"last_name" binding:"required"`
Login string `json:"login" binding:"required"` Login string `json:"login" binding:"required"`
Password string `json:"password" binding:"required"` Password string `json:"password" binding:"required"`
PermissionView bool `json:"permission_view"` PermissionView bool `json:"permission_view"`
PermissionManage bool `json:"permission_manage_agent"` PermissionManage bool `json:"permission_manage_agent"`
PermissionAdmin bool `json:"permission_admin"` PermissionAdmin bool `json:"permission_admin"`
@@ -27,10 +27,10 @@ type TokenCreate struct {
// UserRegister is the request body for public user registration (all permissions false). // UserRegister is the request body for public user registration (all permissions false).
type UserRegister struct { type UserRegister struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
LastName string `json:"last_name" binding:"required"` LastName string `json:"last_name" binding:"required"`
Login string `json:"login" binding:"required"` Login string `json:"login" binding:"required"`
Password string `json:"password" binding:"required"` Password string `json:"password" binding:"required"`
} }
// TokenUpdate is the request body for updating an existing user. // TokenUpdate is the request body for updating an existing user.
@@ -59,7 +59,7 @@ type BatchActionRequest struct {
// LoginRequest is the request body for login. // LoginRequest is the request body for login.
type LoginRequest struct { type LoginRequest struct {
Login string `json:"login" binding:"required"` Login string `json:"login" binding:"required"`
Password string `json:"password" binding:"required"` Password string `json:"password" binding:"required"`
} }
@@ -117,14 +117,14 @@ const (
// AgentDeployConfig represents the configuration for deploying an agent to a server // AgentDeployConfig represents the configuration for deploying an agent to a server
// @Description Configuration for deploying HellreigN agent to a single server // @Description Configuration for deploying HellreigN agent to a single server
type AgentDeployConfig struct { type AgentDeployConfig struct {
User string `json:"user" binding:"required" example:"admin" description:"SSH username"` User string `json:"user" binding:"required" example:"admin" description:"SSH username"`
IP string `json:"ip" binding:"required" example:"192.168.1.100" description:"Server IP address"` IP string `json:"ip" binding:"required" example:"192.168.1.100" description:"Server IP address"`
Port int `json:"port" example:"22" description:"SSH port (default: 22)"` Port int `json:"port" example:"22" description:"SSH port (default: 22)"`
AuthMethod AuthMethod `json:"authMethod" binding:"required" example:"key" description:"SSH auth method: key or password"` AuthMethod AuthMethod `json:"authMethod" binding:"required" example:"key" description:"SSH auth method: key or password"`
SSHKey string `json:"sshKey,omitempty" example:"-----BEGIN OPENSSH PRIVATE KEY-----" description:"SSH private key (required if authMethod=key)"` SSHKey string `json:"sshKey,omitempty" example:"-----BEGIN OPENSSH PRIVATE KEY-----" description:"SSH private key (required if authMethod=key)"`
Password string `json:"password,omitempty" example:"secret" description:"SSH password (required if authMethod=password)"` Password string `json:"password,omitempty" example:"secret" description:"SSH password (required if authMethod=password)"`
DeployType DeployType `json:"deployType" binding:"required" example:"docker" description:"Deployment type: docker or binary"` DeployType DeployType `json:"deployType" binding:"required" example:"docker" description:"Deployment type: docker or binary"`
AgentLabel string `json:"agentLabel" binding:"required" example:"production-server-1" description:"Unique label for the agent"` AgentLabel string `json:"agentLabel" binding:"required" example:"production-server-1" description:"Unique label for the agent"`
} }
// DeployAgentsRequest represents the request body for deploying agents to multiple servers // DeployAgentsRequest represents the request body for deploying agents to multiple servers
@@ -137,15 +137,15 @@ type DeployAgentsRequest struct {
// @Description Response containing deployment results and registration tokens // @Description Response containing deployment results and registration tokens
type DeployResponse struct { type DeployResponse struct {
Message string `json:"message" example:"Deployment completed"` Message string `json:"message" example:"Deployment completed"`
Results []DeployResult `json:"results" description:"Deployment results for each server"` Results []DeployResult `json:"results" description:"Deployment results for each server"`
} }
// DeployResult represents the result of deploying to a single server // DeployResult represents the result of deploying to a single server
// @Description Result of deploying to a single server // @Description Result of deploying to a single server
type DeployResult struct { type DeployResult struct {
IP string `json:"ip" example:"192.168.1.100" description:"Server IP address"` IP string `json:"ip" example:"192.168.1.100" description:"Server IP address"`
AgentLabel string `json:"agent_label" example:"production-server-1" description:"Agent label"` AgentLabel string `json:"agent_label" example:"production-server-1" description:"Agent label"`
Token string `json:"token" example:"abc123..." description:"Registration token for agent registration"` Token string `json:"token" example:"abc123..." description:"Registration token for agent registration"`
Success bool `json:"success" example:"true" description:"Whether deployment succeeded"` Success bool `json:"success" example:"true" description:"Whether deployment succeeded"`
Error string `json:"error,omitempty" example:"" description:"Error message if deployment failed"` Error string `json:"error,omitempty" example:"" description:"Error message if deployment failed"`
} }
+19 -4
View File
@@ -50,8 +50,15 @@ func (r *Repository) CreateToken(tc TokenCreate) (string, error) {
result, err := r.DB.Exec( result, err := r.DB.Exec(
`INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active) `INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
tc.Name, tc.LastName, tc.Login, string(hashed), token, tc.Name,
tc.PermissionView, tc.PermissionManage, tc.PermissionAdmin, tc.IsActive, tc.LastName,
tc.Login,
string(hashed),
token,
tc.PermissionView,
tc.PermissionManage,
tc.PermissionAdmin,
tc.IsActive,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -79,7 +86,11 @@ func (r *Repository) RegisterUser(ur UserRegister) (string, error) {
result, err := r.DB.Exec( result, err := r.DB.Exec(
`INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active) `INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active)
VALUES (?, ?, ?, ?, ?, 0, 0, 0, 0)`, VALUES (?, ?, ?, ?, ?, 0, 0, 0, 0)`,
ur.Name, ur.LastName, ur.Login, string(hashed), token, ur.Name,
ur.LastName,
ur.Login,
string(hashed),
token,
) )
if err != nil { if err != nil {
return "", err return "", err
@@ -450,7 +461,11 @@ func (r *Repository) UpdatePermissions(login string, update TokenUpdatePermissio
result, err := r.DB.Exec( result, err := r.DB.Exec(
`UPDATE tokens SET permission_view = ?, permission_manage_agent = ?, permission_admin = ?, is_active = ? WHERE login = ?`, `UPDATE tokens SET permission_view = ?, permission_manage_agent = ?, permission_admin = ?, is_active = ? WHERE login = ?`,
newView, newManage, newAdmin, newActive, login, newView,
newManage,
newAdmin,
newActive,
login,
) )
if err != nil { if err != nil {
return err return err
@@ -20,9 +20,9 @@ type ScriptInterpreter struct {
} }
type ScriptInterpreterCreate struct { type ScriptInterpreterCreate struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
Label string `json:"label" binding:"required"` Label string `json:"label" binding:"required"`
Argv []string `json:"argv" binding:"required"` Argv []string `json:"argv" binding:"required"`
} }
type ScriptInterpreterUpdate struct { type ScriptInterpreterUpdate struct {
@@ -44,7 +44,10 @@ func (r *ScriptInterpreterRepo) Init(ctx context.Context) error {
return err return err
} }
func (r *ScriptInterpreterRepo) Create(ctx context.Context, in ScriptInterpreterCreate) (*ScriptInterpreter, error) { func (r *ScriptInterpreterRepo) Create(
ctx context.Context,
in ScriptInterpreterCreate,
) (*ScriptInterpreter, error) {
argvJSON, err := json.Marshal(in.Argv) argvJSON, err := json.Marshal(in.Argv)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -71,7 +74,8 @@ func (r *ScriptInterpreterRepo) GetByID(ctx context.Context, id int64) (*ScriptI
var argvJSON string var argvJSON string
var createdAt, updatedAt string var createdAt, updatedAt string
err := r.DB.QueryRowContext(ctx, err := r.DB.QueryRowContext(
ctx,
`SELECT id, name, label, argv, created_at, updated_at FROM script_interpreters WHERE id = ?`, `SELECT id, name, label, argv, created_at, updated_at FROM script_interpreters WHERE id = ?`,
id, id,
).Scan(&si.ID, &si.Name, &si.Label, &argvJSON, &createdAt, &updatedAt) ).Scan(&si.ID, &si.Name, &si.Label, &argvJSON, &createdAt, &updatedAt)
@@ -103,7 +107,14 @@ func (r *ScriptInterpreterRepo) List(ctx context.Context) ([]ScriptInterpreter,
for rows.Next() { for rows.Next() {
var si ScriptInterpreter var si ScriptInterpreter
var argvJSON, createdAt, updatedAt string var argvJSON, createdAt, updatedAt string
if err := rows.Scan(&si.ID, &si.Name, &si.Label, &argvJSON, &createdAt, &updatedAt); err != nil { if err := rows.Scan(
&si.ID,
&si.Name,
&si.Label,
&argvJSON,
&createdAt,
&updatedAt,
); err != nil {
return nil, err return nil, err
} }
if err := json.Unmarshal([]byte(argvJSON), &si.Argv); err != nil { if err := json.Unmarshal([]byte(argvJSON), &si.Argv); err != nil {
@@ -116,7 +127,11 @@ func (r *ScriptInterpreterRepo) List(ctx context.Context) ([]ScriptInterpreter,
return interpreters, rows.Err() return interpreters, rows.Err()
} }
func (r *ScriptInterpreterRepo) Update(ctx context.Context, id int64, in ScriptInterpreterUpdate) (*ScriptInterpreter, error) { func (r *ScriptInterpreterRepo) Update(
ctx context.Context,
id int64,
in ScriptInterpreterUpdate,
) (*ScriptInterpreter, error) {
si, err := r.GetByID(ctx, id) si, err := r.GetByID(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
+18 -4
View File
@@ -17,7 +17,11 @@ func NewScriptService(repo *repository.ScriptInterpreterRepo) *ScriptService {
// ResolveCommand builds the full argv[] by prepending the interpreter's argv // ResolveCommand builds the full argv[] by prepending the interpreter's argv
// to the script text (as the last argument). // to the script text (as the last argument).
func (self *ScriptService) ResolveCommand(ctx context.Context, interpreterID int64, scriptText string) ([]string, error) { func (self *ScriptService) ResolveCommand(
ctx context.Context,
interpreterID int64,
scriptText string,
) ([]string, error) {
interpreter, err := self.repo.GetByID(ctx, interpreterID) interpreter, err := self.repo.GetByID(ctx, interpreterID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -33,11 +37,17 @@ func (self *ScriptService) ResolveCommand(ctx context.Context, interpreterID int
return argv, nil return argv, nil
} }
func (self *ScriptService) Create(ctx context.Context, in repository.ScriptInterpreterCreate) (*repository.ScriptInterpreter, error) { func (self *ScriptService) Create(
ctx context.Context,
in repository.ScriptInterpreterCreate,
) (*repository.ScriptInterpreter, error) {
return self.repo.Create(ctx, in) return self.repo.Create(ctx, in)
} }
func (self *ScriptService) GetByID(ctx context.Context, id int64) (*repository.ScriptInterpreter, error) { func (self *ScriptService) GetByID(
ctx context.Context,
id int64,
) (*repository.ScriptInterpreter, error) {
return self.repo.GetByID(ctx, id) return self.repo.GetByID(ctx, id)
} }
@@ -45,7 +55,11 @@ func (self *ScriptService) List(ctx context.Context) ([]repository.ScriptInterpr
return self.repo.List(ctx) return self.repo.List(ctx)
} }
func (self *ScriptService) Update(ctx context.Context, id int64, in repository.ScriptInterpreterUpdate) (*repository.ScriptInterpreter, error) { func (self *ScriptService) Update(
ctx context.Context,
id int64,
in repository.ScriptInterpreterUpdate,
) (*repository.ScriptInterpreter, error) {
return self.repo.Update(ctx, id, in) return self.repo.Update(ctx, id, in)
} }
+17 -3
View File
@@ -43,7 +43,11 @@ func OpenClickHouse(cfg ClickHouseConfig) (*sql.DB, error) {
} }
// OpenClickHouseWithRetry attempts to connect to ClickHouse with retries and backoff. // OpenClickHouseWithRetry attempts to connect to ClickHouse with retries and backoff.
func OpenClickHouseWithRetry(cfg ClickHouseConfig, maxRetries int, initialDelay time.Duration) (*sql.DB, error) { func OpenClickHouseWithRetry(
cfg ClickHouseConfig,
maxRetries int,
initialDelay time.Duration,
) (*sql.DB, error) {
var lastErr error var lastErr error
delay := initialDelay delay := initialDelay
@@ -53,10 +57,20 @@ func OpenClickHouseWithRetry(cfg ClickHouseConfig, maxRetries int, initialDelay
return db, nil return db, nil
} }
lastErr = err lastErr = err
log.Printf("ClickHouse connection attempt %d/%d failed: %v, retrying in %v...", i+1, maxRetries, err, delay) log.Printf(
"ClickHouse connection attempt %d/%d failed: %v, retrying in %v...",
i+1,
maxRetries,
err,
delay,
)
time.Sleep(delay) time.Sleep(delay)
delay *= 2 delay *= 2
} }
return nil, fmt.Errorf("clickhouse connection failed after %d attempts: %w", maxRetries, lastErr) return nil, fmt.Errorf(
"clickhouse connection failed after %d attempts: %w",
maxRetries,
lastErr,
)
} }