@@ -3,11 +3,10 @@ package commander
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"io"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type CommandExecutor struct{}
|
||||
|
||||
+20
-4
@@ -160,12 +160,28 @@ func main() {
|
||||
// User management (admin only) - Full CRUD
|
||||
authTokenGroup.GET("/users/:login", handlers.RequireAdmin(), auth.GetUser)
|
||||
authTokenGroup.PUT("/users/:login", handlers.RequireAdmin(), auth.UpdateUser)
|
||||
authTokenGroup.PUT("/users/:login/permissions", handlers.RequireAdmin(), auth.UpdateUserPermissions)
|
||||
authTokenGroup.PUT("/users/:login/password", handlers.RequireAdmin(), auth.ResetUserPassword)
|
||||
authTokenGroup.PUT(
|
||||
"/users/:login/permissions",
|
||||
handlers.RequireAdmin(),
|
||||
auth.UpdateUserPermissions,
|
||||
)
|
||||
authTokenGroup.PUT(
|
||||
"/users/:login/password",
|
||||
handlers.RequireAdmin(),
|
||||
auth.ResetUserPassword,
|
||||
)
|
||||
|
||||
// User activation management (admin only)
|
||||
authTokenGroup.POST("/users/:login/activate", handlers.RequireAdmin(), auth.ActivateUser)
|
||||
authTokenGroup.POST("/users/:login/deactivate", handlers.RequireAdmin(), auth.DeactivateUser)
|
||||
authTokenGroup.POST(
|
||||
"/users/:login/activate",
|
||||
handlers.RequireAdmin(),
|
||||
auth.ActivateUser,
|
||||
)
|
||||
authTokenGroup.POST(
|
||||
"/users/:login/deactivate",
|
||||
handlers.RequireAdmin(),
|
||||
auth.DeactivateUser,
|
||||
)
|
||||
authTokenGroup.GET("/users/inactive", handlers.RequireAdmin(), auth.ListInactiveUsers)
|
||||
}
|
||||
|
||||
|
||||
@@ -295,6 +295,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/token": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Creates a new user with permissions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -354,6 +359,11 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deletes the current authenticated user",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -392,6 +402,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/tokens": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns list of all users with their permissions",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -424,6 +439,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/tokens/:login": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deletes a user by their login",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -471,6 +491,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/:login": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns a user by their login (admin only)",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -525,6 +550,11 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates a user's name and last name (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -593,6 +623,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/:login/activate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Activates a user account by login (admin only)",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -649,6 +684,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/:login/deactivate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deactivates a user account by login (admin only)",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -705,6 +745,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/:login/password": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Resets a user's password to a new value (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -773,6 +818,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/:login/permissions": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates a user's permissions and activation status (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -841,6 +891,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/users/inactive": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns list of all users waiting for activation",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -873,6 +928,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/auth/validate": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Check if the provided Bearer token is valid and return its permissions",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -902,6 +962,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/jobs": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sends a command to the specified agent, waits for execution, and returns the result",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1238,6 +1303,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/scripts/interpreters": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns all script interpreters available in the system",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -1259,6 +1329,11 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Registers a new script interpreter with name, label, and argv",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1293,6 +1368,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/scripts/interpreters/:id": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns a script interpreter by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -1320,6 +1400,11 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates fields of a script interpreter",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1359,6 +1444,11 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Removes a script interpreter by ID",
|
||||
"tags": [
|
||||
"scripts"
|
||||
@@ -1382,6 +1472,11 @@ const docTemplate = `{
|
||||
},
|
||||
"/scripts/run": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Resolves interpreter argv[] and sends the full command to the agent",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
|
||||
@@ -284,6 +284,11 @@
|
||||
},
|
||||
"/auth/token": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Creates a new user with permissions",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -343,6 +348,11 @@
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deletes the current authenticated user",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -381,6 +391,11 @@
|
||||
},
|
||||
"/auth/tokens": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns list of all users with their permissions",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -413,6 +428,11 @@
|
||||
},
|
||||
"/auth/tokens/:login": {
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deletes a user by their login",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -460,6 +480,11 @@
|
||||
},
|
||||
"/auth/users/:login": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns a user by their login (admin only)",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -514,6 +539,11 @@
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates a user's name and last name (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -582,6 +612,11 @@
|
||||
},
|
||||
"/auth/users/:login/activate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Activates a user account by login (admin only)",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -638,6 +673,11 @@
|
||||
},
|
||||
"/auth/users/:login/deactivate": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Deactivates a user account by login (admin only)",
|
||||
"tags": [
|
||||
"auth"
|
||||
@@ -694,6 +734,11 @@
|
||||
},
|
||||
"/auth/users/:login/password": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Resets a user's password to a new value (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -762,6 +807,11 @@
|
||||
},
|
||||
"/auth/users/:login/permissions": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates a user's permissions and activation status (admin only)",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -830,6 +880,11 @@
|
||||
},
|
||||
"/auth/users/inactive": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns list of all users waiting for activation",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -862,6 +917,11 @@
|
||||
},
|
||||
"/auth/validate": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Check if the provided Bearer token is valid and return its permissions",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -891,6 +951,11 @@
|
||||
},
|
||||
"/jobs": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Sends a command to the specified agent, waits for execution, and returns the result",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1227,6 +1292,11 @@
|
||||
},
|
||||
"/scripts/interpreters": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns all script interpreters available in the system",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -1248,6 +1318,11 @@
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Registers a new script interpreter with name, label, and argv",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1282,6 +1357,11 @@
|
||||
},
|
||||
"/scripts/interpreters/:id": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Returns a script interpreter by ID",
|
||||
"produces": [
|
||||
"application/json"
|
||||
@@ -1309,6 +1389,11 @@
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Updates fields of a script interpreter",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
@@ -1348,6 +1433,11 @@
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Removes a script interpreter by ID",
|
||||
"tags": [
|
||||
"scripts"
|
||||
@@ -1371,6 +1461,11 @@
|
||||
},
|
||||
"/scripts/run": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Resolves interpreter argv[] and sends the full command to the agent",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
|
||||
@@ -597,6 +597,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Delete my account
|
||||
tags:
|
||||
- auth
|
||||
@@ -636,6 +638,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Create user
|
||||
tags:
|
||||
- auth
|
||||
@@ -657,6 +661,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: List users
|
||||
tags:
|
||||
- auth
|
||||
@@ -688,6 +694,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Delete user
|
||||
tags:
|
||||
- auth
|
||||
@@ -725,6 +733,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get user by login
|
||||
tags:
|
||||
- auth
|
||||
@@ -769,6 +779,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update user
|
||||
tags:
|
||||
- auth
|
||||
@@ -806,6 +818,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Activate user
|
||||
tags:
|
||||
- auth
|
||||
@@ -843,6 +857,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Deactivate user
|
||||
tags:
|
||||
- auth
|
||||
@@ -888,6 +904,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Reset user password
|
||||
tags:
|
||||
- auth
|
||||
@@ -933,6 +951,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update user permissions
|
||||
tags:
|
||||
- auth
|
||||
@@ -954,6 +974,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: List inactive users
|
||||
tags:
|
||||
- auth
|
||||
@@ -973,6 +995,8 @@ paths:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Validate token
|
||||
tags:
|
||||
- auth
|
||||
@@ -996,6 +1020,8 @@ paths:
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/internal_handlers.AddJobOut'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Create and run a job on an agent
|
||||
tags:
|
||||
- jobs
|
||||
@@ -1202,6 +1228,8 @@ paths:
|
||||
items:
|
||||
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
|
||||
type: array
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: List interpreters
|
||||
tags:
|
||||
- scripts
|
||||
@@ -1223,6 +1251,8 @@ paths:
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Create interpreter
|
||||
tags:
|
||||
- scripts
|
||||
@@ -1238,6 +1268,8 @@ paths:
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Delete interpreter
|
||||
tags:
|
||||
- scripts
|
||||
@@ -1256,6 +1288,8 @@ paths:
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get interpreter
|
||||
tags:
|
||||
- scripts
|
||||
@@ -1282,6 +1316,8 @@ paths:
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/gitea_d3m0k1d_ru_d3m0k1d_HellreigN_backend_internal_repository.ScriptInterpreter'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update interpreter
|
||||
tags:
|
||||
- scripts
|
||||
@@ -1304,6 +1340,8 @@ paths:
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/internal_handlers.RunScriptOut'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Run a script on an agent
|
||||
tags:
|
||||
- scripts
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
|
||||
// Executor handles running Ansible playbooks
|
||||
type Executor struct {
|
||||
workDir string
|
||||
workDir string
|
||||
grpcServerHost string
|
||||
grpcServerPort string
|
||||
backendURL string
|
||||
backendURL string
|
||||
}
|
||||
|
||||
// ExecutorConfig holds configuration for the Executor
|
||||
@@ -23,26 +23,26 @@ type ExecutorConfig struct {
|
||||
WorkDir string
|
||||
GRPCServerHost string
|
||||
GRPCServerPort string
|
||||
BackendURL string
|
||||
BackendURL string
|
||||
}
|
||||
|
||||
// NewExecutor creates a new Ansible executor
|
||||
func NewExecutor(cfg ExecutorConfig) *Executor {
|
||||
return &Executor{
|
||||
workDir: cfg.WorkDir,
|
||||
workDir: cfg.WorkDir,
|
||||
grpcServerHost: cfg.GRPCServerHost,
|
||||
grpcServerPort: cfg.GRPCServerPort,
|
||||
backendURL: cfg.BackendURL,
|
||||
backendURL: cfg.BackendURL,
|
||||
}
|
||||
}
|
||||
|
||||
// DeployResult holds the result of a deployment
|
||||
type DeployResult struct {
|
||||
Host string
|
||||
Success bool
|
||||
Stdout string
|
||||
Stderr string
|
||||
Err error
|
||||
Host string
|
||||
Success bool
|
||||
Stdout string
|
||||
Stderr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// WorkDir returns the work directory path
|
||||
@@ -51,7 +51,11 @@ func (e *Executor) WorkDir() string {
|
||||
}
|
||||
|
||||
// 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"
|
||||
if deployType == "docker" {
|
||||
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
|
||||
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
|
||||
results := make(map[string][]DeployResult)
|
||||
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 !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 {
|
||||
_, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
@@ -120,7 +123,12 @@ func (c *Collector) Stream(stream proto.Collector_StreamServer) error {
|
||||
return 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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())
|
||||
if !ok {
|
||||
return fmt.Errorf("no metadata in context")
|
||||
@@ -164,7 +166,12 @@ func (self *Agent) send() error {
|
||||
// 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{
|
||||
bidi: bidi,
|
||||
in: make(chan *proto.Command),
|
||||
|
||||
@@ -38,7 +38,7 @@ func NewAgentDeployGroup(h *Handlers) *AgentDeployGroup {
|
||||
WorkDir: workDir,
|
||||
GRPCServerHost: "0.0.0.0", // TODO: make configurable
|
||||
GRPCServerPort: grpcPort,
|
||||
BackendURL: backendURL,
|
||||
BackendURL: backendURL,
|
||||
})
|
||||
|
||||
// Write playbooks on init
|
||||
|
||||
@@ -104,7 +104,7 @@ func (arg *AgentRegistrationGroup) Register(c *gin.Context) {
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
CSR string `json:"csr" binding:"required"`
|
||||
CSR string `json:"csr" binding:"required"`
|
||||
Token string `json:"token" binding:"required"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/collector"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type AgentsGroup struct {
|
||||
|
||||
@@ -90,6 +90,7 @@ func (ag *AuthGroup) RegisterUser(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/token [post]
|
||||
func (ag *AuthGroup) CreateToken(c *gin.Context) {
|
||||
var tc repository.TokenCreate
|
||||
@@ -113,6 +114,7 @@ func (ag *AuthGroup) CreateToken(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {object} repository.Tokens
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/validate [get]
|
||||
func (ag *AuthGroup) ValidateToken(c *gin.Context) {
|
||||
tokenVal, exists := c.Get(string(tokenContextKey))
|
||||
@@ -137,6 +139,7 @@ func (ag *AuthGroup) ValidateToken(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {array} repository.Tokens
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/tokens [get]
|
||||
func (ag *AuthGroup) ListTokens(c *gin.Context) {
|
||||
tokens, err := ag.Repo.ListTokens()
|
||||
@@ -155,6 +158,7 @@ func (ag *AuthGroup) ListTokens(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/tokens/:login [delete]
|
||||
func (ag *AuthGroup) DeleteToken(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -182,6 +186,7 @@ func (ag *AuthGroup) DeleteToken(c *gin.Context) {
|
||||
// @Success 200 {object} map[string]string
|
||||
// @Failure 401 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/token [delete]
|
||||
func (ag *AuthGroup) DeleteMyToken(c *gin.Context) {
|
||||
tokenVal, exists := c.Get(string(tokenContextKey))
|
||||
@@ -213,6 +218,7 @@ func (ag *AuthGroup) DeleteMyToken(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login/activate [post]
|
||||
func (ag *AuthGroup) ActivateUser(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -242,6 +248,7 @@ func (ag *AuthGroup) ActivateUser(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login/deactivate [post]
|
||||
func (ag *AuthGroup) DeactivateUser(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -269,6 +276,7 @@ func (ag *AuthGroup) DeactivateUser(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Success 200 {array} repository.Tokens
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/inactive [get]
|
||||
func (ag *AuthGroup) ListInactiveUsers(c *gin.Context) {
|
||||
tokens, err := ag.Repo.ListInactiveTokens()
|
||||
@@ -289,6 +297,7 @@ func (ag *AuthGroup) ListInactiveUsers(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login [get]
|
||||
func (ag *AuthGroup) GetUser(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -321,6 +330,7 @@ func (ag *AuthGroup) GetUser(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login [put]
|
||||
func (ag *AuthGroup) UpdateUser(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -358,6 +368,7 @@ func (ag *AuthGroup) UpdateUser(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login/permissions [put]
|
||||
func (ag *AuthGroup) UpdateUserPermissions(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
@@ -395,6 +406,7 @@ func (ag *AuthGroup) UpdateUserPermissions(c *gin.Context) {
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 404 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security Bearer
|
||||
// @Router /auth/users/:login/password [put]
|
||||
func (ag *AuthGroup) ResetUserPassword(c *gin.Context) {
|
||||
login := c.Param("login")
|
||||
|
||||
@@ -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-Credentials", "true")
|
||||
c.Writer.Header().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")
|
||||
c.Writer.Header().
|
||||
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" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
|
||||
@@ -23,10 +23,10 @@ func NewJobsHandlers(cmder *commander.Commander, svc *service.ScriptService) Job
|
||||
}
|
||||
|
||||
type AddJobIn struct {
|
||||
Command string `json:"command" binding:"required"`
|
||||
Command string `json:"command" binding:"required"`
|
||||
InterpreterID int64 `json:"interpreter_id"`
|
||||
Stdin *string `json:"stdin"`
|
||||
AgentID string `json:"agent_id" binding:"required"`
|
||||
AgentID string `json:"agent_id" binding:"required"`
|
||||
}
|
||||
type AddJobOut struct {
|
||||
ID int64 `json:"id"`
|
||||
@@ -45,6 +45,7 @@ type AddJobOut struct {
|
||||
// @Produce json
|
||||
// @Param body body AddJobIn true "Job request"
|
||||
// @Success 201 {object} AddJobOut
|
||||
// @Security Bearer
|
||||
// @Router /jobs [post]
|
||||
func (self *JobsHandlers) AddJob(c *gin.Context) {
|
||||
err := func() error {
|
||||
@@ -63,7 +64,11 @@ func (self *JobsHandlers) AddJob(c *gin.Context) {
|
||||
command = []string{"sh", "-c", in.Command}
|
||||
} else {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ func NewLogHandlers(logRepo *repository.LogRepository) *LogHandlers {
|
||||
|
||||
type InsertLogRequest struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Level string `json:"level" binding:"required"`
|
||||
Service string `json:"service" binding:"required"`
|
||||
Agent string `json:"agent" binding:"required"`
|
||||
Message string `json:"message" binding:"required"`
|
||||
Level string `json:"level" binding:"required"`
|
||||
Service string `json:"service" binding:"required"`
|
||||
Agent string `json:"agent" binding:"required"`
|
||||
Message string `json:"message" binding:"required"`
|
||||
}
|
||||
|
||||
// @Summary Insert log entry
|
||||
@@ -105,13 +105,13 @@ func (lh *LogHandlers) InsertBatch(c *gin.Context) {
|
||||
}
|
||||
|
||||
type SearchLogsRequest struct {
|
||||
Level string `form:"level"`
|
||||
Service string `form:"service"`
|
||||
Agent string `form:"agent"`
|
||||
Level string `form:"level"`
|
||||
Service string `form:"service"`
|
||||
Agent string `form:"agent"`
|
||||
DateFrom string `form:"date_from"`
|
||||
DateTo string `form:"date_to"`
|
||||
Limit int `form:"limit"`
|
||||
Offset int `form:"offset"`
|
||||
Limit int `form:"limit"`
|
||||
Offset int `form:"offset"`
|
||||
}
|
||||
|
||||
// @Summary Search logs
|
||||
|
||||
@@ -22,9 +22,9 @@ func NewScriptHandlers(svc *service.ScriptService, cmder *commander.Commander) S
|
||||
}
|
||||
|
||||
type RunScriptIn struct {
|
||||
AgentID string `json:"agent_id" binding:"required"`
|
||||
AgentID string `json:"agent_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"`
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ type RunScriptOut struct {
|
||||
// @Produce json
|
||||
// @Param body body RunScriptIn true "Script request"
|
||||
// @Success 201 {object} RunScriptOut
|
||||
// @Security Bearer
|
||||
// @Router /scripts/run [post]
|
||||
func (self *ScriptHandlers) RunScript(c *gin.Context) {
|
||||
err := func() error {
|
||||
@@ -53,7 +54,11 @@ func (self *ScriptHandlers) RunScript(c *gin.Context) {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -98,6 +103,7 @@ func (self *ScriptHandlers) RunScript(c *gin.Context) {
|
||||
// @Tags scripts
|
||||
// @Produce json
|
||||
// @Success 200 {array} repository.ScriptInterpreter
|
||||
// @Security Bearer
|
||||
// @Router /scripts/interpreters [get]
|
||||
func (self *ScriptHandlers) ListInterpreters(c *gin.Context) {
|
||||
interpreters, err := self.svc.List(c.Request.Context())
|
||||
@@ -116,6 +122,7 @@ func (self *ScriptHandlers) ListInterpreters(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param body body repository.ScriptInterpreterCreate true "Interpreter definition"
|
||||
// @Success 201 {object} repository.ScriptInterpreter
|
||||
// @Security Bearer
|
||||
// @Router /scripts/interpreters [post]
|
||||
func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) {
|
||||
var in repository.ScriptInterpreterCreate
|
||||
@@ -139,6 +146,7 @@ func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) {
|
||||
// @Produce json
|
||||
// @Param id path int true "Interpreter ID"
|
||||
// @Success 200 {object} repository.ScriptInterpreter
|
||||
// @Security Bearer
|
||||
// @Router /scripts/interpreters/:id [get]
|
||||
func (self *ScriptHandlers) GetInterpreter(c *gin.Context) {
|
||||
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 body body repository.ScriptInterpreterUpdate true "Interpreter fields"
|
||||
// @Success 200 {object} repository.ScriptInterpreter
|
||||
// @Security Bearer
|
||||
// @Router /scripts/interpreters/:id [put]
|
||||
func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) {
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
@@ -192,6 +201,7 @@ func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) {
|
||||
// @Tags scripts
|
||||
// @Param id path int true "Interpreter ID"
|
||||
// @Success 204
|
||||
// @Security Bearer
|
||||
// @Router /scripts/interpreters/:id [delete]
|
||||
func (self *ScriptHandlers) DeleteInterpreter(c *gin.Context) {
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
|
||||
@@ -23,7 +23,11 @@ func (r *JobRepository) Init(ctx context.Context) error {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
result, err := r.DB.ExecContext(ctx,
|
||||
result, err := r.DB.ExecContext(
|
||||
ctx,
|
||||
`INSERT INTO jobs (agent_id, command, stdin, stdout, stderr, status) VALUES (?, ?, ?, '', '', 0)`,
|
||||
agentID, string(commandJSON), stdinVal,
|
||||
agentID,
|
||||
string(commandJSON),
|
||||
stdinVal,
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -45,10 +52,18 @@ func (r *JobRepository) InitJob(ctx context.Context, agentID string, job models.
|
||||
return result.LastInsertId()
|
||||
}
|
||||
|
||||
func (r *JobRepository) UpdateJobInDB(ctx context.Context, jid int64, msg models.JobForUpdate) (models.Job, error) {
|
||||
result, err := r.DB.ExecContext(ctx,
|
||||
func (r *JobRepository) UpdateJobInDB(
|
||||
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 = ?`,
|
||||
msg.Stdout, msg.Stderr, msg.Status, jid,
|
||||
msg.Stdout,
|
||||
msg.Stderr,
|
||||
msg.Status,
|
||||
jid,
|
||||
)
|
||||
if err != nil {
|
||||
return models.Job{}, err
|
||||
@@ -81,10 +96,10 @@ func (r *JobRepository) GetJobByID(ctx context.Context, jid int64) (models.Job,
|
||||
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)
|
||||
}
|
||||
|
||||
job.JobForInsert.Stdin = stdinVal
|
||||
job.Stdin = stdinVal
|
||||
return job, nil
|
||||
}
|
||||
|
||||
@@ -84,13 +84,13 @@ func (r *LogRepository) InsertBatch(ctx context.Context, logs []storage.LogEntry
|
||||
}
|
||||
|
||||
type LogFilter struct {
|
||||
Level string
|
||||
Service string
|
||||
Agent string
|
||||
Level string
|
||||
Service string
|
||||
Agent string
|
||||
DateFrom time.Time
|
||||
DateTo time.Time
|
||||
Limit int
|
||||
Offset int
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
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)
|
||||
for rows.Next() {
|
||||
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
|
||||
}
|
||||
logs = append(logs, log)
|
||||
|
||||
@@ -2,23 +2,23 @@ package repository
|
||||
|
||||
// Tokens represents a user record with info and permissions.
|
||||
type Tokens struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LastName string `json:"last_name"`
|
||||
Login string `json:"login"`
|
||||
Token string `json:"token"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
IsActive bool `json:"is_active"`
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LastName string `json:"last_name"`
|
||||
Login string `json:"login"`
|
||||
Token string `json:"token"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// TokenCreate is the request body for creating a new user.
|
||||
type TokenCreate struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
LastName string `json:"last_name" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
LastName string `json:"last_name" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
@@ -27,10 +27,10 @@ type TokenCreate struct {
|
||||
|
||||
// UserRegister is the request body for public user registration (all permissions false).
|
||||
type UserRegister struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
LastName string `json:"last_name" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// TokenUpdate is the request body for updating an existing user.
|
||||
@@ -59,7 +59,7 @@ type BatchActionRequest struct {
|
||||
|
||||
// LoginRequest is the request body for login.
|
||||
type LoginRequest struct {
|
||||
Login string `json:"login" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
@@ -117,14 +117,14 @@ const (
|
||||
// AgentDeployConfig represents the configuration for deploying an agent to a server
|
||||
// @Description Configuration for deploying HellreigN agent to a single server
|
||||
type AgentDeployConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
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)"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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)"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
type DeployResponse struct {
|
||||
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
|
||||
// @Description Result of deploying to a single server
|
||||
type DeployResult struct {
|
||||
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"`
|
||||
Token string `json:"token" example:"abc123..." description:"Registration token for agent registration"`
|
||||
Success bool `json:"success" example:"true" description:"Whether deployment succeeded"`
|
||||
Error string `json:"error,omitempty" example:"" description:"Error message if deployment failed"`
|
||||
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"`
|
||||
Token string `json:"token" example:"abc123..." description:"Registration token for agent registration"`
|
||||
Success bool `json:"success" example:"true" description:"Whether deployment succeeded"`
|
||||
Error string `json:"error,omitempty" example:"" description:"Error message if deployment failed"`
|
||||
}
|
||||
|
||||
@@ -50,8 +50,15 @@ func (r *Repository) CreateToken(tc TokenCreate) (string, error) {
|
||||
result, err := r.DB.Exec(
|
||||
`INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
tc.Name, tc.LastName, tc.Login, string(hashed), token,
|
||||
tc.PermissionView, tc.PermissionManage, tc.PermissionAdmin, tc.IsActive,
|
||||
tc.Name,
|
||||
tc.LastName,
|
||||
tc.Login,
|
||||
string(hashed),
|
||||
token,
|
||||
tc.PermissionView,
|
||||
tc.PermissionManage,
|
||||
tc.PermissionAdmin,
|
||||
tc.IsActive,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -79,7 +86,11 @@ func (r *Repository) RegisterUser(ur UserRegister) (string, error) {
|
||||
result, err := r.DB.Exec(
|
||||
`INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin, is_active)
|
||||
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 {
|
||||
return "", err
|
||||
@@ -450,7 +461,11 @@ func (r *Repository) UpdatePermissions(login string, update TokenUpdatePermissio
|
||||
|
||||
result, err := r.DB.Exec(
|
||||
`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 {
|
||||
return err
|
||||
|
||||
@@ -20,9 +20,9 @@ type ScriptInterpreter struct {
|
||||
}
|
||||
|
||||
type ScriptInterpreterCreate struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Label string `json:"label" binding:"required"`
|
||||
Argv []string `json:"argv" binding:"required"`
|
||||
Argv []string `json:"argv" binding:"required"`
|
||||
}
|
||||
|
||||
type ScriptInterpreterUpdate struct {
|
||||
@@ -44,7 +44,10 @@ func (r *ScriptInterpreterRepo) Init(ctx context.Context) error {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -71,7 +74,8 @@ func (r *ScriptInterpreterRepo) GetByID(ctx context.Context, id int64) (*ScriptI
|
||||
var argvJSON 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 = ?`,
|
||||
id,
|
||||
).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() {
|
||||
var si ScriptInterpreter
|
||||
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
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -17,7 +17,11 @@ func NewScriptService(repo *repository.ScriptInterpreterRepo) *ScriptService {
|
||||
|
||||
// ResolveCommand builds the full argv[] by prepending the interpreter's argv
|
||||
// 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -33,11 +37,17 @@ func (self *ScriptService) ResolveCommand(ctx context.Context, interpreterID int
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -45,7 +55,11 @@ func (self *ScriptService) List(ctx context.Context) ([]repository.ScriptInterpr
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@ func OpenClickHouse(cfg ClickHouseConfig) (*sql.DB, error) {
|
||||
}
|
||||
|
||||
// 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
|
||||
delay := initialDelay
|
||||
|
||||
@@ -53,10 +57,20 @@ func OpenClickHouseWithRetry(cfg ClickHouseConfig, maxRetries int, initialDelay
|
||||
return db, nil
|
||||
}
|
||||
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)
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user