feat(backend): add script interpreters
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
|||||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/commander"
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/commander"
|
||||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/handlers"
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/handlers"
|
||||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/service"
|
||||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
||||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -86,6 +87,14 @@ func main() {
|
|||||||
|
|
||||||
cmdr := commander.New(jobRepo)
|
cmdr := commander.New(jobRepo)
|
||||||
|
|
||||||
|
// Initialize script interpreter repository and service
|
||||||
|
scriptRepo := repository.NewScriptInterpreterRepo(db)
|
||||||
|
if err := scriptRepo.Init(context.Background()); err != nil {
|
||||||
|
log.Printf("Warning: failed to initialize script interpreters table: %v", err)
|
||||||
|
}
|
||||||
|
scriptSvc := service.NewScriptService(scriptRepo)
|
||||||
|
scriptHandlers := handlers.NewScriptHandlers(scriptSvc, cmdr)
|
||||||
|
|
||||||
agents := handlers.NewAgentsGroup(h, coll)
|
agents := handlers.NewAgentsGroup(h, coll)
|
||||||
auth := handlers.AuthGroup{Handlers: h}
|
auth := handlers.AuthGroup{Handlers: h}
|
||||||
agentReg := handlers.NewAgentRegistrationGroup(h)
|
agentReg := handlers.NewAgentRegistrationGroup(h)
|
||||||
@@ -193,6 +202,18 @@ func main() {
|
|||||||
logsGroup.GET("/agents", logHandlers.GetAgents)
|
logsGroup.GET("/agents", logHandlers.GetAgents)
|
||||||
logsGroup.GET("/levels", logHandlers.GetLevels)
|
logsGroup.GET("/levels", logHandlers.GetLevels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scripts (requires admin permission)
|
||||||
|
scriptsGroup := v1.Group("/scripts")
|
||||||
|
scriptsGroup.Use(auth.AuthMiddleware(), handlers.RequireAdmin())
|
||||||
|
{
|
||||||
|
scriptsGroup.POST("/run", scriptHandlers.RunScript)
|
||||||
|
scriptsGroup.GET("/interpreters", scriptHandlers.ListInterpreters)
|
||||||
|
scriptsGroup.POST("/interpreters", scriptHandlers.CreateInterpreter)
|
||||||
|
scriptsGroup.GET("/interpreters/:id", scriptHandlers.GetInterpreter)
|
||||||
|
scriptsGroup.PUT("/interpreters/:id", scriptHandlers.UpdateInterpreter)
|
||||||
|
scriptsGroup.DELETE("/interpreters/:id", scriptHandlers.DeleteInterpreter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start gRPC server with mTLS in background
|
// Start gRPC server with mTLS in background
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/grpcsrv/commander"
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/models"
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScriptHandlers struct {
|
||||||
|
svc *service.ScriptService
|
||||||
|
cmder *commander.Commander
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScriptHandlers(svc *service.ScriptService, cmder *commander.Commander) ScriptHandlers {
|
||||||
|
return ScriptHandlers{svc: svc, cmder: cmder}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunScript executes a script on a target agent.
|
||||||
|
// @Summary Run a script on an agent
|
||||||
|
// @Description Resolves interpreter argv[] and sends the full command to the agent
|
||||||
|
// @Tags scripts
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body RunScriptIn true "Script request"
|
||||||
|
// @Success 201 {object} RunScriptOut
|
||||||
|
// @Router /scripts/run [post]
|
||||||
|
func (self *ScriptHandlers) RunScript(c *gin.Context) {
|
||||||
|
err := func() error {
|
||||||
|
type RunScriptIn struct {
|
||||||
|
AgentID string `json:"agent_id" binding:"required"`
|
||||||
|
InterpreterID int64 `json:"interpreter_id" binding:"required"`
|
||||||
|
ScriptText string `json:"script_text" binding:"required"`
|
||||||
|
Stdin *string `json:"stdin"`
|
||||||
|
}
|
||||||
|
var in RunScriptIn
|
||||||
|
if err := c.Bind(&in); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err := self.svc.ResolveCommand(c.Request.Context(), in.InterpreterID, in.ScriptText)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
agent, ok := self.cmder.GetAgent(in.AgentID)
|
||||||
|
if !ok {
|
||||||
|
c.Status(http.StatusNotFound)
|
||||||
|
return fmt.Errorf("agent not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
jid, err := agent.AddJob(models.JobForInsert{
|
||||||
|
Command: command,
|
||||||
|
Stdin: in.Stdin,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
job, err := agent.WaitJob(jid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunScriptOut struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Command []string `json:"command"`
|
||||||
|
Stdin *string `json:"stdin"`
|
||||||
|
Stdout string `json:"stdout"`
|
||||||
|
Stderr string `json:"stderr"`
|
||||||
|
Status int32 `json:"status"`
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusCreated, RunScriptOut{
|
||||||
|
ID: job.ID,
|
||||||
|
Command: job.Command,
|
||||||
|
Stdin: job.Stdin,
|
||||||
|
Stdout: job.Stdout,
|
||||||
|
Stderr: job.Stderr,
|
||||||
|
Status: job.Status,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInterpreters returns all registered script interpreters.
|
||||||
|
// @Summary List interpreters
|
||||||
|
// @Description Returns all script interpreters available in the system
|
||||||
|
// @Tags scripts
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} repository.ScriptInterpreter
|
||||||
|
// @Router /scripts/interpreters [get]
|
||||||
|
func (self *ScriptHandlers) ListInterpreters(c *gin.Context) {
|
||||||
|
interpreters, err := self.svc.List(c.Request.Context())
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, interpreters)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInterpreter registers a new script interpreter.
|
||||||
|
// @Summary Create interpreter
|
||||||
|
// @Description Registers a new script interpreter with name, label, and argv
|
||||||
|
// @Tags scripts
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param body body repository.ScriptInterpreterCreate true "Interpreter definition"
|
||||||
|
// @Success 201 {object} repository.ScriptInterpreter
|
||||||
|
// @Router /scripts/interpreters [post]
|
||||||
|
func (self *ScriptHandlers) CreateInterpreter(c *gin.Context) {
|
||||||
|
var in repository.ScriptInterpreterCreate
|
||||||
|
if err := c.BindJSON(&in); err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
si, err := self.svc.Create(c.Request.Context(), in)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusCreated, si)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInterpreter returns a single interpreter by ID.
|
||||||
|
// @Summary Get interpreter
|
||||||
|
// @Description Returns a script interpreter by ID
|
||||||
|
// @Tags scripts
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "Interpreter ID"
|
||||||
|
// @Success 200 {object} repository.ScriptInterpreter
|
||||||
|
// @Router /scripts/interpreters/:id [get]
|
||||||
|
func (self *ScriptHandlers) GetInterpreter(c *gin.Context) {
|
||||||
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
si, err := self.svc.GetByID(c.Request.Context(), id)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, si)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateInterpreter updates an interpreter.
|
||||||
|
// @Summary Update interpreter
|
||||||
|
// @Description Updates fields of a script interpreter
|
||||||
|
// @Tags scripts
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path int true "Interpreter ID"
|
||||||
|
// @Param body body repository.ScriptInterpreterUpdate true "Interpreter fields"
|
||||||
|
// @Success 200 {object} repository.ScriptInterpreter
|
||||||
|
// @Router /scripts/interpreters/:id [put]
|
||||||
|
func (self *ScriptHandlers) UpdateInterpreter(c *gin.Context) {
|
||||||
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var in repository.ScriptInterpreterUpdate
|
||||||
|
if err := c.BindJSON(&in); err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
si, err := self.svc.Update(c.Request.Context(), id, in)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, si)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteInterpreter removes an interpreter.
|
||||||
|
// @Summary Delete interpreter
|
||||||
|
// @Description Removes a script interpreter by ID
|
||||||
|
// @Tags scripts
|
||||||
|
// @Param id path int true "Interpreter ID"
|
||||||
|
// @Success 204
|
||||||
|
// @Router /scripts/interpreters/:id [delete]
|
||||||
|
func (self *ScriptHandlers) DeleteInterpreter(c *gin.Context) {
|
||||||
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := self.svc.Delete(c.Request.Context(), id); err != nil {
|
||||||
|
c.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScriptInterpreter struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Argv []string `json:"argv"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptInterpreterCreate struct {
|
||||||
|
Name string `json:"name" binding:"required"`
|
||||||
|
Label string `json:"label" binding:"required"`
|
||||||
|
Argv []string `json:"argv" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptInterpreterUpdate struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
Label *string `json:"label"`
|
||||||
|
Argv []string `json:"argv"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScriptInterpreterRepo struct {
|
||||||
|
DB *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScriptInterpreterRepo(db *sql.DB) *ScriptInterpreterRepo {
|
||||||
|
return &ScriptInterpreterRepo{DB: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ScriptInterpreterRepo) Init(ctx context.Context) error {
|
||||||
|
_, err := r.DB.ExecContext(ctx, storage.CreateScriptInterpretersTable)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ScriptInterpreterRepo) Create(ctx context.Context, in ScriptInterpreterCreate) (*ScriptInterpreter, error) {
|
||||||
|
argvJSON, err := json.Marshal(in.Argv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := r.DB.ExecContext(ctx,
|
||||||
|
`INSERT INTO script_interpreters (name, label, argv) VALUES (?, ?, ?)`,
|
||||||
|
in.Name, in.Label, string(argvJSON),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := result.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.GetByID(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ScriptInterpreterRepo) GetByID(ctx context.Context, id int64) (*ScriptInterpreter, error) {
|
||||||
|
var si ScriptInterpreter
|
||||||
|
var argvJSON string
|
||||||
|
var createdAt, updatedAt string
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(argvJSON), &si.Argv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
si.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
||||||
|
si.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
||||||
|
return &si, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ScriptInterpreterRepo) List(ctx context.Context) ([]ScriptInterpreter, error) {
|
||||||
|
rows, err := r.DB.QueryContext(ctx,
|
||||||
|
`SELECT id, name, label, argv, created_at, updated_at FROM script_interpreters`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var interpreters []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 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(argvJSON), &si.Argv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
si.CreatedAt, _ = time.Parse(time.RFC3339, createdAt)
|
||||||
|
si.UpdatedAt, _ = time.Parse(time.RFC3339, updatedAt)
|
||||||
|
interpreters = append(interpreters, si)
|
||||||
|
}
|
||||||
|
return interpreters, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
set := ""
|
||||||
|
args := make([]interface{}, 0)
|
||||||
|
idx := 1
|
||||||
|
|
||||||
|
if in.Name != nil {
|
||||||
|
set += "name = ?"
|
||||||
|
args = append(args, *in.Name)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
if in.Label != nil {
|
||||||
|
if idx > 1 {
|
||||||
|
set += ", "
|
||||||
|
}
|
||||||
|
set += "label = ?"
|
||||||
|
args = append(args, *in.Label)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
if in.Argv != nil {
|
||||||
|
if idx > 1 {
|
||||||
|
set += ", "
|
||||||
|
}
|
||||||
|
argvJSON, err := json.Marshal(in.Argv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
set += "argv = ?"
|
||||||
|
args = append(args, string(argvJSON))
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx == 1 {
|
||||||
|
return si, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
set += ", updated_at = CURRENT_TIMESTAMP"
|
||||||
|
args = append(args, id)
|
||||||
|
|
||||||
|
_, err = r.DB.ExecContext(ctx,
|
||||||
|
`UPDATE script_interpreters SET `+set+` WHERE id = ?`,
|
||||||
|
args...,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.GetByID(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ScriptInterpreterRepo) Delete(ctx context.Context, id int64) error {
|
||||||
|
result, err := r.DB.ExecContext(ctx,
|
||||||
|
`DELETE FROM script_interpreters WHERE id = ?`,
|
||||||
|
id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
affected, err := result.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if affected == 0 {
|
||||||
|
return ErrNotFound
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScriptService struct {
|
||||||
|
repo *repository.ScriptInterpreterRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScriptService(repo *repository.ScriptInterpreterRepo) *ScriptService {
|
||||||
|
return &ScriptService{repo: repo}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
interpreter, err := self.repo.GetByID(ctx, interpreterID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(interpreter.Argv) == 0 {
|
||||||
|
return nil, fmt.Errorf("interpreter %q has empty argv", interpreter.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
argv := make([]string, len(interpreter.Argv)+1)
|
||||||
|
copy(argv, interpreter.Argv)
|
||||||
|
argv[len(argv)-1] = scriptText
|
||||||
|
return argv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return self.repo.GetByID(ctx, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ScriptService) List(ctx context.Context) ([]repository.ScriptInterpreter, error) {
|
||||||
|
return self.repo.List(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ScriptService) Update(ctx context.Context, id int64, in repository.ScriptInterpreterUpdate) (*repository.ScriptInterpreter, error) {
|
||||||
|
return self.repo.Update(ctx, id, in)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ScriptService) Delete(ctx context.Context, id int64) error {
|
||||||
|
return self.repo.Delete(ctx, id)
|
||||||
|
}
|
||||||
@@ -46,6 +46,17 @@ CREATE TABLE IF NOT EXISTS jobs (
|
|||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const CreateScriptInterpretersTable = `
|
||||||
|
CREATE TABLE IF NOT EXISTS script_interpreters (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
label TEXT NOT NULL,
|
||||||
|
argv TEXT NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
const CreateLogsTable = `
|
const CreateLogsTable = `
|
||||||
CREATE TABLE IF NOT EXISTS logs (
|
CREATE TABLE IF NOT EXISTS logs (
|
||||||
timestamp DateTime64(3) DEFAULT now(),
|
timestamp DateTime64(3) DEFAULT now(),
|
||||||
|
|||||||
Reference in New Issue
Block a user