List Probers CLI command #2

Listing probers is now only available from server CLI.

The `ProbersQueryParams` struct also changed. I don't think that I will
use more than 20 probers in this project, so paging is not required.

The search param also simplified in one field struct `Search" which
search both in `name` and `api_key` column.
This commit is contained in:
ditatompel 2024-05-18 00:56:13 +07:00
parent 49b786ed80
commit 59f1dd9421
No known key found for this signature in database
GPG key ID: 31D3D06D77950979
4 changed files with 81 additions and 141 deletions

65
cmd/probers.go Normal file
View file

@ -0,0 +1,65 @@
package cmd
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"time"
"xmr-remote-nodes/internal/database"
"xmr-remote-nodes/internal/repo"
"github.com/spf13/cobra"
)
var probersCmd = &cobra.Command{
Use: "probers [search]",
Short: "[server] Get registered probers",
Long: `Get list of registered prober machines.
Use [search] args to filter results by name or api key.
"sort-by" flag can be "id" or "last_submit_ts"
"sort-dir" flag can be "asc" or "desc"`,
Example: `# To sort probers by last submit time in ascending order that contains "sin1":
xmr-nodes probers -s last_submit_ts -d asc sin1`,
Run: func(cmd *cobra.Command, args []string) {
if err := database.ConnectDB(); err != nil {
panic(err)
}
sortBy, _ := cmd.Flags().GetString("sort-by")
sortDir, _ := cmd.Flags().GetString("sort-dir")
probersRepo := repo.NewProberRepo(database.GetDB())
probers, err := probersRepo.Probers(repo.ProbersQueryParams{
Search: strings.Join(args, " "),
SortBy: sortBy,
SortDirection: sortDir,
})
if err != nil {
fmt.Println(err)
return
}
if len(probers) == 0 {
fmt.Println("No probers found")
return
}
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
fmt.Fprintf(w, "ID\t| Name\t| Last Submit\t| API Key\n")
for _, prober := range probers {
fmt.Fprintf(w, "%d\t| %s\t| %s\t| %s\n",
prober.Id,
prober.Name,
time.Unix(prober.LastSubmitTs, 0).Format(time.RFC3339),
prober.ApiKey,
)
}
w.Flush()
},
}
func init() {
rootCmd.AddCommand(probersCmd)
probersCmd.Flags().StringP("sort-by", "s", "last_submit_ts", "Sort by column name, can be id or last_submit_ts")
probersCmd.Flags().StringP("sort-dir", "d", "desc", "Sort direction, can be asc or desc")
}

View file

@ -60,101 +60,6 @@ func Logout(c *fiber.Ctx) error {
})
}
func Prober(c *fiber.Ctx) error {
proberRepo := repo.NewProberRepo(database.GetDB())
if c.Method() == "POST" {
payload := repo.Prober{}
if err := c.BodyParser(&payload); err != nil {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
if payload.Name == "" {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"status": "error",
"message": "Please fill prober name",
"data": nil,
})
}
err := proberRepo.AddProber(payload.Name)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
} else if c.Method() == "DELETE" {
id, _ := strconv.Atoi(c.Params("id"))
err := proberRepo.Delete(id)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
return c.JSON(fiber.Map{
"status": "ok",
"message": "Success",
"data": nil,
})
} else if c.Method() == "PATCH" {
payload := repo.Prober{}
if err := c.BodyParser(&payload); err != nil {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
if payload.Name == "" {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"status": "error",
"message": "Please fill prober name",
"data": nil,
})
}
id, _ := strconv.Atoi(c.Params("id"))
err := proberRepo.Update(id, payload.Name)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
}
query := repo.ProbersQueryParams{
RowsPerPage: c.QueryInt("limit", 10),
Page: c.QueryInt("page", 1),
SortBy: c.Query("sort_by", "id"),
SortDirection: c.Query("sort_direction", "desc"),
Name: c.Query("name"),
ApiKey: c.Query("api_key"),
}
prober, err := proberRepo.Probers(query)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"status": "error",
"message": err.Error(),
"data": nil,
})
}
return c.JSON(fiber.Map{
"status": "ok",
"message": "Success",
"data": prober,
})
}
func MoneroNode(c *fiber.Ctx) error {
nodeId, err := c.ParamsInt("id", 0)
if err != nil {

View file

@ -12,10 +12,6 @@ func AppRoute(app *fiber.App) {
func V1Api(app *fiber.App) {
v1 := app.Group("/api/v1")
v1.Get("/prober", CookieProtected, Prober)
v1.Post("/prober", CookieProtected, Prober)
v1.Patch("/prober/:id", CookieProtected, Prober)
v1.Delete("/prober/:id", CookieProtected, Prober)
v1.Get("/crons", CookieProtected, Crons)
v1.Get("/nodes", MoneroNodes)
v1.Post("/nodes", AddNode)

View file

@ -12,7 +12,7 @@ import (
type ProberRepository interface {
AddProber(name string) error
Update(id int, name string) error
Probers(q ProbersQueryParams) (Probers, error)
Probers(q ProbersQueryParams) ([]Prober, error)
CheckApi(key string) (Prober, error)
Delete(id int) error
}
@ -28,24 +28,6 @@ type Prober struct {
LastSubmitTs int64 `json:"last_submit_ts" db:"last_submit_ts"`
}
type ProbersQueryParams struct {
Name string
ApiKey string
RowsPerPage int
Page int
SortBy string
SortDirection string
}
type Probers struct {
TotalRows int `json:"total_rows"`
RowsPerPage int `json:"rows_per_page"`
CurrentPage int `json:"current_page"`
NextPage int `json:"next_page"`
Items []*Prober `json:"items"`
}
func NewProberRepo(db *database.DB) ProberRepository {
return &ProberRepo{db}
}
@ -71,35 +53,31 @@ func (repo *ProberRepo) Delete(id int) error {
return err
}
func (repo *ProberRepo) Probers(q ProbersQueryParams) (Probers, error) {
type ProbersQueryParams struct {
Search string
SortBy string
SortDirection string
}
func (repo *ProberRepo) Probers(q ProbersQueryParams) ([]Prober, error) {
queryParams := []interface{}{}
whereQueries := []string{}
where := ""
if q.Name != "" {
whereQueries = append(whereQueries, "name LIKE ?")
queryParams = append(queryParams, "%"+q.Name+"%")
}
if q.ApiKey != "" {
whereQueries = append(whereQueries, "api_key LIKE ?")
queryParams = append(queryParams, "%"+q.ApiKey+"%")
if q.Search != "" {
whereQueries = append(whereQueries, "(name LIKE ? OR api_key LIKE ?)")
queryParams = append(queryParams, "%"+q.Search+"%")
queryParams = append(queryParams, "%"+q.Search+"%")
}
if len(whereQueries) > 0 {
where = "WHERE " + strings.Join(whereQueries, " AND ")
}
probers := Probers{}
queryTotalRows := fmt.Sprintf("SELECT COUNT(id) AS total_rows FROM tbl_prober %s", where)
err := repo.db.QueryRow(queryTotalRows, queryParams...).Scan(&probers.TotalRows)
if err != nil {
return probers, err
}
queryParams = append(queryParams, q.RowsPerPage, (q.Page-1)*q.RowsPerPage)
probers := []Prober{}
allowedSort := []string{"id", "last_submit_ts"}
sortBy := "id"
sortBy := "last_submit_ts"
if slices.Contains(allowedSort, q.SortBy) {
sortBy = q.SortBy
}
@ -108,7 +86,7 @@ func (repo *ProberRepo) Probers(q ProbersQueryParams) (Probers, error) {
sortDirection = "ASC"
}
query := fmt.Sprintf("SELECT id, name, api_key, last_submit_ts FROM tbl_prober %s ORDER BY %s %s LIMIT ? OFFSET ?", where, sortBy, sortDirection)
query := fmt.Sprintf("SELECT id, name, api_key, last_submit_ts FROM tbl_prober %s ORDER BY %s %s", where, sortBy, sortDirection)
row, err := repo.db.Query(query, queryParams...)
if err != nil {
@ -116,17 +94,13 @@ func (repo *ProberRepo) Probers(q ProbersQueryParams) (Probers, error) {
}
defer row.Close()
probers.RowsPerPage = q.RowsPerPage
probers.CurrentPage = q.Page
probers.NextPage = q.Page + 1
for row.Next() {
prober := Prober{}
err = row.Scan(&prober.Id, &prober.Name, &prober.ApiKey, &prober.LastSubmitTs)
if err != nil {
return probers, err
}
probers.Items = append(probers.Items, &prober)
probers = append(probers, prober)
}
return probers, nil
}