Compare commits
4 commits
c1df0f95c3
...
a67a3fdc87
| Author | SHA1 | Date | |
|---|---|---|---|
|
a67a3fdc87 |
|||
|
7e934d16f7 |
|||
|
55ad3c2aed |
|||
|
f2e2f45a19 |
36 changed files with 1289 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/borgmatic-monitor
|
||||
/env.sh
|
||||
/example.json
|
||||
5
Dockerfile
Normal file
5
Dockerfile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
FROM alpine
|
||||
|
||||
COPY ./build/bgm-server /usr/bin/
|
||||
|
||||
ENTRYPOINT ["/usr/bin/bgm-server"]
|
||||
18
Makefile
Normal file
18
Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
swagger:
|
||||
swag init -d "cmd/server/,pkg/web/" -g main.go
|
||||
|
||||
watch-server: swagger
|
||||
MYSQL_DATABASE=borgmaticmon gowatch -p ./cmd/server/
|
||||
|
||||
watch-client:
|
||||
gowatch -p ./cmd/client/ -args="-e,http://localhost:1323/api/v1/borgmatic/infos,-h,apps,-c,env.sh"
|
||||
|
||||
.PHONY:
|
||||
bins:
|
||||
mkdir -p build/
|
||||
CGO_ENABLED=0 go build -o ./build/bgm-server ./cmd/server
|
||||
CGO_ENABLED=0 go build -o ./build/bgm-client ./cmd/client
|
||||
|
||||
.PHONY:
|
||||
docker: bins
|
||||
docker buildx build -t gitnet.fr/deblan/bgm-server --push .
|
||||
BIN
build/bgm-client
Executable file
BIN
build/bgm-client
Executable file
Binary file not shown.
BIN
build/bgm-server
Executable file
BIN
build/bgm-server
Executable file
Binary file not shown.
92
cmd/client/main.go
Normal file
92
cmd/client/main.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := &cli.Command{
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "endpoint",
|
||||
Aliases: []string{"e"},
|
||||
Value: "",
|
||||
Usage: "Endpoint of the monitor",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "hostname",
|
||||
Aliases: []string{"h"},
|
||||
Value: "",
|
||||
Usage: "Hostname",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Value: "",
|
||||
Usage: "Borgmatic config file",
|
||||
},
|
||||
},
|
||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||
var buff bytes.Buffer
|
||||
|
||||
c := exec.Command(
|
||||
"borgmatic",
|
||||
"info",
|
||||
"-c", cmd.String("config"),
|
||||
"--json",
|
||||
)
|
||||
|
||||
c.Stderr = os.Stderr
|
||||
c.Stdout = &buff
|
||||
|
||||
err := c.Run()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
input := api.BorgmaticInfos{
|
||||
Hostname: cmd.String("hostname"),
|
||||
Infos: buff.String(),
|
||||
}
|
||||
|
||||
j, err := json.Marshal(input)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", cmd.String("endpoint"), bytes.NewBuffer(j))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return errors.New("Invalid response code")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := cmd.Run(context.Background(), os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
38
cmd/server/main.go
Normal file
38
cmd/server/main.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
echoSwagger "github.com/swaggo/echo-swagger"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/docs"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database/model"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web"
|
||||
)
|
||||
|
||||
func main() {
|
||||
database.Init()
|
||||
|
||||
db := database.GetDb()
|
||||
|
||||
db.AutoMigrate(model.Host{})
|
||||
db.AutoMigrate(model.Info{})
|
||||
|
||||
e := echo.New()
|
||||
e.Use(middleware.RequestLogger())
|
||||
|
||||
e.GET("/", web.Hosts)
|
||||
e.GET("/host/:id", web.Host)
|
||||
e.POST("/api/v1/borgmatic/infos", web.ApiBorgmaticInfos)
|
||||
|
||||
docs.SwaggerInfo.Title = "Borgmatic monitor"
|
||||
docs.SwaggerInfo.Description = "API"
|
||||
docs.SwaggerInfo.Version = "1.0"
|
||||
docs.SwaggerInfo.BasePath = "/"
|
||||
docs.SwaggerInfo.Schemes = []string{"http", "https"}
|
||||
e.GET("/swagger/*", echoSwagger.WrapHandler)
|
||||
|
||||
if err := e.Start(":1323"); err != nil {
|
||||
e.Logger.Error("failed to start server", "error", err)
|
||||
}
|
||||
}
|
||||
80
docs/docs.go
Normal file
80
docs/docs.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/api/v1/borgmatic/infos": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Create a borgmatic infos",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Object",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.BorgmaticInfos"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.BorgmaticInfos": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"type": "string"
|
||||
},
|
||||
"infos": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "",
|
||||
Host: "",
|
||||
BasePath: "",
|
||||
Schemes: []string{},
|
||||
Title: "",
|
||||
Description: "",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
||||
51
docs/swagger.json
Normal file
51
docs/swagger.json
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"contact": {}
|
||||
},
|
||||
"paths": {
|
||||
"/api/v1/borgmatic/infos": {
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"summary": "Create a borgmatic infos",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Object",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/api.BorgmaticInfos"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"api.BorgmaticInfos": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"type": "string"
|
||||
},
|
||||
"infos": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
docs/swagger.yaml
Normal file
31
docs/swagger.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
definitions:
|
||||
api.BorgmaticInfos:
|
||||
properties:
|
||||
hostname:
|
||||
type: string
|
||||
infos:
|
||||
type: string
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
paths:
|
||||
/api/v1/borgmatic/infos:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
parameters:
|
||||
- description: Object
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/api.BorgmaticInfos'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
"400":
|
||||
description: Bad Request
|
||||
summary: Create a borgmatic infos
|
||||
swagger: "2.0"
|
||||
51
go.mod
51
go.mod
|
|
@ -1,3 +1,54 @@
|
|||
module gitnet.fr/deblan/borgmatic-monitor
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.4 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.4 // indirect
|
||||
github.com/go-openapi/spec v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/labstack/echo/v5 v5.0.4 // indirect
|
||||
github.com/mailru/easyjson v0.9.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/sv-tools/openapi v0.4.0 // indirect
|
||||
github.com/swaggo/echo-swagger v1.5.0 // indirect
|
||||
github.com/swaggo/files/v2 v2.0.2 // indirect
|
||||
github.com/swaggo/swag v1.16.6 // indirect
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 // indirect
|
||||
github.com/urfave/cli/v2 v2.27.5 // indirect
|
||||
github.com/urfave/cli/v3 v3.6.2 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/text v0.34.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gorm.io/driver/mysql v1.6.0 // indirect
|
||||
gorm.io/gorm v1.31.1 // indirect
|
||||
maragu.dev/gomponents v1.2.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
|
|
|||
150
go.sum
Normal file
150
go.sum
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
|
||||
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
|
||||
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
|
||||
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v5 v5.0.4 h1:ll3I/O8BifjMztj9dD1vx/peZQv8cR2CTUdQK6QxGGc=
|
||||
github.com/labstack/echo/v5 v5.0.4/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/sv-tools/openapi v0.4.0 h1:UhD9DVnGox1hfTePNclpUzUFgos57FvzT2jmcAuTOJ4=
|
||||
github.com/sv-tools/openapi v0.4.0/go.mod h1:kD/dG+KP0+Fom1r6nvcj/ORtLus8d8enXT6dyRZDirE=
|
||||
github.com/swaggo/echo-swagger v1.5.0 h1:nkHxOaBy0SkbJMtMeXZC64KHSa0mJdZFQhVqwEcMres=
|
||||
github.com/swaggo/echo-swagger v1.5.0/go.mod h1:TzO363X1ZG/MSbjrG2IX6m65Yd3/zpqh5KM6lPctAhk=
|
||||
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
|
||||
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
|
||||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5 h1:fK7d6ET9rrEsdB8IyuwXREWMcyQN3N7gawGFbbrjgHk=
|
||||
github.com/swaggo/swag/v2 v2.0.0-rc5/go.mod h1:kCL8Fu4Zl8d5tB2Bgj96b8wRowwrwk175bZHXfuGVFI=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
|
||||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
|
||||
github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
|
||||
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
|
||||
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
maragu.dev/gomponents v1.2.0 h1:H7/N5htz1GCnhu0HB1GasluWeU2rJZOYztVEyN61iTc=
|
||||
maragu.dev/gomponents v1.2.0/go.mod h1:oEDahza2gZoXDoDHhw8jBNgH+3UR5ni7Ur648HORydM=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
15
pkg/borgmatic/archive.go
Normal file
15
pkg/borgmatic/archive.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package borgmatic
|
||||
|
||||
type Archive struct {
|
||||
CommandLine []string `json:"command_line"`
|
||||
Comment string `json:"comment"`
|
||||
Duration float64 `json:"duration"`
|
||||
Start *Time `json:"start"`
|
||||
End *Time `json:"end"`
|
||||
Hostname string `json:"hostname"`
|
||||
Id string `json:"id"`
|
||||
Limits ArchiveLimits `json:"limits"`
|
||||
Stats ArchiveStats `json:"stats"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
5
pkg/borgmatic/archive_limits.go
Normal file
5
pkg/borgmatic/archive_limits.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package borgmatic
|
||||
|
||||
type ArchiveLimits struct {
|
||||
MaxArchiveSize float64 `json:"max_archive_size"`
|
||||
}
|
||||
8
pkg/borgmatic/archive_stats.go
Normal file
8
pkg/borgmatic/archive_stats.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package borgmatic
|
||||
|
||||
type ArchiveStats struct {
|
||||
CompressedSize int64 `json:"compressed_size"`
|
||||
DeduplicatedSize int64 `json:"deduplicated_size"`
|
||||
Nfiles int64 `json:"nfiles"`
|
||||
OriginalSize int64 `json:"original_size"`
|
||||
}
|
||||
6
pkg/borgmatic/cache.go
Normal file
6
pkg/borgmatic/cache.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package borgmatic
|
||||
|
||||
type Cache struct {
|
||||
Path string `json:"path"`
|
||||
Stats CacheStats `json:"stats"`
|
||||
}
|
||||
10
pkg/borgmatic/cache_stats.go
Normal file
10
pkg/borgmatic/cache_stats.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package borgmatic
|
||||
|
||||
type CacheStats struct {
|
||||
TotalChunks float64 `json:"total_chunks"`
|
||||
TotalCsize float64 `json:"total_csize"`
|
||||
TotalSize float64 `json:"total_size"`
|
||||
TotalUniqueChunks float64 `json:"total_unique_chunks"`
|
||||
UniqueCsize float64 `json:"unique_csize"`
|
||||
UniqueSize float64 `json:"unique_size"`
|
||||
}
|
||||
5
pkg/borgmatic/encryption.go
Normal file
5
pkg/borgmatic/encryption.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package borgmatic
|
||||
|
||||
type Encryption struct {
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
8
pkg/borgmatic/info.go
Normal file
8
pkg/borgmatic/info.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package borgmatic
|
||||
|
||||
type Info struct {
|
||||
Archives []Archive `json:"archives"`
|
||||
Cache Cache `json:"cache"`
|
||||
Encryption Encryption `json:"encryption"`
|
||||
Repository Repository `json:"repository"`
|
||||
}
|
||||
3
pkg/borgmatic/infos.go
Normal file
3
pkg/borgmatic/infos.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package borgmatic
|
||||
|
||||
type Infos []Info
|
||||
8
pkg/borgmatic/repository.go
Normal file
8
pkg/borgmatic/repository.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package borgmatic
|
||||
|
||||
type Repository struct {
|
||||
Id string `json:"id"`
|
||||
LastModified *Time `json:"last_modified"`
|
||||
Location string `json:"location"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
24
pkg/borgmatic/time.go
Normal file
24
pkg/borgmatic/time.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package borgmatic
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Time struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func (t *Time) UnmarshalJSON(b []byte) (err error) {
|
||||
s := strings.Trim(string(b), "\"")
|
||||
|
||||
if s == "null" {
|
||||
t.Time = time.Time{}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
t.Time, err = time.Parse("2006-01-02T15:04:05.000000", s)
|
||||
|
||||
return
|
||||
}
|
||||
68
pkg/database/database.go
Normal file
68
pkg/database/database.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
db *gorm.DB
|
||||
config mysql.Config
|
||||
)
|
||||
|
||||
func Init() {
|
||||
config = mysql.Config{
|
||||
DSN: fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local",
|
||||
fallback(os.Getenv("MYSQL_USER"), "root"),
|
||||
fallback(os.Getenv("MYSQL_PASSWORD"), "root"),
|
||||
fallback(os.Getenv("MYSQL_HOST"), "127.0.0.1"),
|
||||
fallback(os.Getenv("MYSQL_PORT"), "3306"),
|
||||
fallback(os.Getenv("MYSQL_DATABASE"), "app"),
|
||||
),
|
||||
DefaultStringSize: 256,
|
||||
DisableDatetimePrecision: true,
|
||||
DontSupportRenameIndex: true,
|
||||
DontSupportRenameColumn: true,
|
||||
SkipInitializeWithVersion: false,
|
||||
}
|
||||
}
|
||||
|
||||
func GetDb() *gorm.DB {
|
||||
var err error
|
||||
|
||||
if db == nil {
|
||||
db, err = gorm.Open(mysql.New(config))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
gdb, err := db.DB()
|
||||
|
||||
if err != nil {
|
||||
db = nil
|
||||
|
||||
return GetDb()
|
||||
}
|
||||
|
||||
if err = gdb.Ping(); err != nil {
|
||||
db = nil
|
||||
|
||||
return GetDb()
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func fallback(value string, fb string) string {
|
||||
if value == "" {
|
||||
return fb
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
80
pkg/database/host.go
Normal file
80
pkg/database/host.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func Hosts() []model.Host {
|
||||
var hosts []model.Host
|
||||
|
||||
GetDb().
|
||||
Model(model.Host{}).
|
||||
Preload("Infos", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("infos.id DESC")
|
||||
}).
|
||||
Order("name ASC").
|
||||
Find(&hosts)
|
||||
|
||||
return hosts
|
||||
}
|
||||
|
||||
func Host(id int) *model.Host {
|
||||
var host model.Host
|
||||
|
||||
GetDb().
|
||||
Model(model.Host{}).
|
||||
Order("name ASC").
|
||||
Where("id = ?", id).
|
||||
First(&host)
|
||||
|
||||
if host.ID > 0 {
|
||||
return &host
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HostByName(label string) *model.Host {
|
||||
var host model.Host
|
||||
|
||||
GetDb().
|
||||
Model(model.Host{}).
|
||||
Order("name ASC").
|
||||
Where("name = ?", label).
|
||||
First(&host)
|
||||
|
||||
if host.ID > 0 {
|
||||
return &host
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func HostInfos(host *model.Host, page, itemPerPage int) *[]model.Info {
|
||||
var infos []model.Info
|
||||
|
||||
GetDb().
|
||||
Model(model.Info{}).
|
||||
Order("infos.id DESC").
|
||||
Offset((page-1)*itemPerPage).
|
||||
Limit(itemPerPage).
|
||||
Where("host_id = ?", host.ID).
|
||||
Find(&infos)
|
||||
|
||||
return &infos
|
||||
}
|
||||
|
||||
func HostInfosMaxPages(host *model.Host, itemPerPage int) int {
|
||||
var count int64
|
||||
|
||||
GetDb().
|
||||
Model(model.Info{}).
|
||||
Order("infos.id DESC").
|
||||
Where("host_id = ?", host.ID).
|
||||
Count(&count)
|
||||
|
||||
return int(math.Ceil(float64(count) / float64(itemPerPage)))
|
||||
}
|
||||
35
pkg/database/model/host.go
Normal file
35
pkg/database/model/host.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/borgmatic"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
gorm.Model
|
||||
|
||||
Name string `gorm:"not null"`
|
||||
Token string `gorm:"not null"`
|
||||
Infos []Info `gorm:"one2many:info;not null"`
|
||||
}
|
||||
|
||||
func (h *Host) LastArchive() *borgmatic.Archive {
|
||||
var last *borgmatic.Archive
|
||||
var lastDate *time.Time
|
||||
|
||||
for _, item := range h.Infos {
|
||||
if infos := *item.Infos(); infos != nil {
|
||||
for _, info := range infos {
|
||||
for _, a := range info.Archives {
|
||||
if lastDate == nil || a.End.After(*lastDate) {
|
||||
last = &a
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return last
|
||||
}
|
||||
28
pkg/database/model/info.go
Normal file
28
pkg/database/model/info.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/borgmatic"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
gorm.Model
|
||||
|
||||
HostID uint
|
||||
Host Host `gorm:"many2one:info"`
|
||||
Data string `gorm:"type:text;not null"`
|
||||
}
|
||||
|
||||
func (i *Info) Infos() *borgmatic.Infos {
|
||||
var infos borgmatic.Infos
|
||||
|
||||
err := json.Unmarshal([]byte(i.Data), &infos)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &infos
|
||||
}
|
||||
6
pkg/web/api/borgmatic_infos.go
Normal file
6
pkg/web/api/borgmatic_infos.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package api
|
||||
|
||||
type BorgmaticInfos struct {
|
||||
Hostname string `json:"hostname"`
|
||||
Infos string `json:"infos"`
|
||||
}
|
||||
49
pkg/web/api_borgmatic_infos.go
Normal file
49
pkg/web/api_borgmatic_infos.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/borgmatic"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database/model"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web/api"
|
||||
)
|
||||
|
||||
// @Summary Create a borgmatic infos
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200
|
||||
// @Success 400
|
||||
// @Router /api/v1/borgmatic/infos [post]
|
||||
// @Param request body api.BorgmaticInfos true "Object"
|
||||
func ApiBorgmaticInfos(c *echo.Context) error {
|
||||
value := new(api.BorgmaticInfos)
|
||||
|
||||
if err := c.Bind(value); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
host := database.HostByName(value.Hostname)
|
||||
|
||||
if host == nil {
|
||||
return c.JSON(http.StatusBadRequest, "Host not found")
|
||||
}
|
||||
|
||||
var infos borgmatic.Infos
|
||||
|
||||
err := json.Unmarshal([]byte(value.Infos), &infos)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
info := new(model.Info)
|
||||
info.HostID = host.ID
|
||||
info.Data = value.Infos
|
||||
|
||||
database.GetDb().Save(info)
|
||||
|
||||
return c.JSON(200, "ok")
|
||||
}
|
||||
83
pkg/web/host.go
Normal file
83
pkg/web/host.go
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/spf13/cast"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web/partial"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Host(c *echo.Context) error {
|
||||
id := cast.ToInt(c.Param("id"))
|
||||
|
||||
if id == 0 {
|
||||
return c.HTML(http.StatusNotFound, "Not found.")
|
||||
}
|
||||
|
||||
host := database.Host(id)
|
||||
|
||||
if host == nil {
|
||||
return c.HTML(http.StatusNotFound, "Not found.")
|
||||
}
|
||||
|
||||
type params struct {
|
||||
Page float64 `query:"page"`
|
||||
}
|
||||
|
||||
var p params
|
||||
|
||||
maxPerPages := 2
|
||||
maxPages := database.HostInfosMaxPages(host, maxPerPages)
|
||||
|
||||
if err := c.Bind(&p); err != nil {
|
||||
p.Page = 1
|
||||
}
|
||||
|
||||
page := int(math.Min(math.Max(p.Page, 1), float64(maxPages)))
|
||||
infos := database.HostInfos(host, page, maxPerPages)
|
||||
|
||||
var nodes []Node
|
||||
|
||||
if len(*infos) == 0 {
|
||||
nodes = append(
|
||||
nodes,
|
||||
Div(
|
||||
Class("text-muted"),
|
||||
Text("Nothing yet!"),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
for _, item := range *infos {
|
||||
if infos := *item.Infos(); infos != nil {
|
||||
for i := len(infos) - 1; i >= 0; i-- {
|
||||
nodes = append(nodes, partial.BorgmaticInfo(infos[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, Page(
|
||||
fmt.Sprintf("host_%d", host.ID),
|
||||
host.Name,
|
||||
Div(
|
||||
Class("container-fluid"),
|
||||
Div(
|
||||
Class("d-flex justify-content-end"),
|
||||
partial.Paginate(fmt.Sprintf("/host/%d?page=%%page%%", host.ID), page, maxPages),
|
||||
),
|
||||
Div(
|
||||
Class("row"),
|
||||
Map(nodes, func(node Node) Node {
|
||||
return node
|
||||
}),
|
||||
),
|
||||
),
|
||||
))
|
||||
}
|
||||
31
pkg/web/hosts.go
Normal file
31
pkg/web/hosts.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web/partial"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Hosts(c *echo.Context) error {
|
||||
var nodes []Node
|
||||
|
||||
for _, host := range database.Hosts() {
|
||||
nodes = append(nodes, partial.Host(host))
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, Page(
|
||||
"home",
|
||||
"Hosts",
|
||||
Div(
|
||||
Class("container-fluid"),
|
||||
Div(
|
||||
Class("row"),
|
||||
Group(nodes),
|
||||
),
|
||||
),
|
||||
))
|
||||
}
|
||||
50
pkg/web/page.go
Normal file
50
pkg/web/page.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/web/partial"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/components"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Page(currentNode string, title string, children ...Node) string {
|
||||
page := HTML5(HTML5Props{
|
||||
Title: title,
|
||||
Language: "en",
|
||||
Head: []Node{
|
||||
Link(
|
||||
Href("https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"),
|
||||
Rel("stylesheet"),
|
||||
Integrity("sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"),
|
||||
CrossOrigin("anonymous"),
|
||||
),
|
||||
Link(
|
||||
Href("https://cdn.jsdelivr.net/npm/remixicon@4.9.0/fonts/remixicon.css"),
|
||||
Rel("stylesheet"),
|
||||
),
|
||||
},
|
||||
Body: []Node{
|
||||
Main(
|
||||
Class("d-flex flex-nowrap"),
|
||||
partial.NavMenu(currentNode),
|
||||
Div(
|
||||
Class("flex-fill p-3"),
|
||||
Style("max-height: 100vh; overflow: auto"),
|
||||
Group(children),
|
||||
),
|
||||
),
|
||||
Script(
|
||||
Src("https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"),
|
||||
Integrity("sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"),
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
page.Render(&buf)
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
47
pkg/web/partial/archive.go
Normal file
47
pkg/web/partial/archive.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package partial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/borgmatic"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Archive(archive borgmatic.Archive) Node {
|
||||
return Div(
|
||||
Class("row"),
|
||||
Div(
|
||||
Class("col-12 col-md-4"),
|
||||
I(Class("ri-store-3-fill me-1")),
|
||||
Text(archive.Name),
|
||||
),
|
||||
Div(
|
||||
Class("text-end col-12 col-md-4"),
|
||||
Text(fmt.Sprintf("%2.fs", archive.Duration)),
|
||||
I(Class("ri-time-fill mx-2")),
|
||||
Text(fmt.Sprintf("%d", archive.Stats.Nfiles)),
|
||||
I(Class("ri-file-fill ms-2")),
|
||||
),
|
||||
Div(
|
||||
Class("text-end col-12 col-md-4"),
|
||||
Div(
|
||||
Class("row"),
|
||||
Div(
|
||||
Class("col-4 text-secondary"),
|
||||
Text(humanize.Bytes(uint64(archive.Stats.OriginalSize))),
|
||||
),
|
||||
Div(
|
||||
Class("col-4 text-primary"),
|
||||
Text(humanize.Bytes(uint64(archive.Stats.DeduplicatedSize))),
|
||||
),
|
||||
Div(
|
||||
Class("col-4 text-success"),
|
||||
Text(humanize.Bytes(uint64(archive.Stats.CompressedSize))),
|
||||
I(Class("ri-file-zip-fill ms-2")),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
39
pkg/web/partial/borgmatic_info.go
Normal file
39
pkg/web/partial/borgmatic_info.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package partial
|
||||
|
||||
import (
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/borgmatic"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func BorgmaticInfo(info borgmatic.Info) Node {
|
||||
return Div(
|
||||
Class("col-12"),
|
||||
Div(
|
||||
Class("card mb-3"),
|
||||
Div(
|
||||
Class("card-header"),
|
||||
Text(info.Repository.LastModified.Format("2006-01-02 15:04:05")),
|
||||
),
|
||||
Div(
|
||||
Class("card-body"),
|
||||
Div(
|
||||
I(Class("ri-price-tag-3-line me-1")),
|
||||
Text(info.Repository.Label),
|
||||
),
|
||||
Div(
|
||||
I(Class("ri-map-pin-line me-1")),
|
||||
Text(info.Repository.Location),
|
||||
),
|
||||
Div(
|
||||
Class("mb-3"),
|
||||
I(Class("ri-lock-line me-1")),
|
||||
Text(info.Encryption.Mode),
|
||||
),
|
||||
Div(
|
||||
Map(info.Archives, Archive),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
64
pkg/web/partial/host.go
Normal file
64
pkg/web/partial/host.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package partial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database/model"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Host(host model.Host) Node {
|
||||
lastArchive := host.LastArchive()
|
||||
|
||||
var body Node
|
||||
|
||||
if lastArchive != nil {
|
||||
pushes := len(host.Infos)
|
||||
|
||||
body = Div(
|
||||
Div(
|
||||
Class("fw-bold"),
|
||||
I(Class("ri-archive-stack-line me-1")),
|
||||
If(pushes <= 1, Text(fmt.Sprintf("%d push", pushes))),
|
||||
If(pushes > 1, Text(fmt.Sprintf("%d pushes", pushes))),
|
||||
),
|
||||
Div(
|
||||
I(Class("ri-calendar-line me-1")),
|
||||
Text(fmt.Sprintf(
|
||||
"Last update on %s",
|
||||
lastArchive.End.Format("2006-01-02 15:04:05"),
|
||||
)),
|
||||
),
|
||||
Div(
|
||||
Class("mt-3"),
|
||||
A(
|
||||
Class("btn btn-primary"),
|
||||
Href(fmt.Sprintf("/host/%d", host.ID)),
|
||||
Text("Show"),
|
||||
),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
body = Div(
|
||||
Class("text-danger"),
|
||||
I(Class("ri-error-warning-line me-1")),
|
||||
Text("Nothing yet!"),
|
||||
)
|
||||
}
|
||||
|
||||
return Div(
|
||||
Class("col-6"),
|
||||
Div(
|
||||
Class("card mb-3"),
|
||||
Div(
|
||||
Class("card-header"),
|
||||
Text(host.Name),
|
||||
),
|
||||
Div(
|
||||
Class("card-body"),
|
||||
body,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
56
pkg/web/partial/page.go
Normal file
56
pkg/web/partial/page.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package partial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gitnet.fr/deblan/borgmatic-monitor/pkg/database"
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func NavMenu(currentNode string) Node {
|
||||
items := [][3]string{
|
||||
[3]string{"home", "/", "All"},
|
||||
}
|
||||
|
||||
for _, host := range database.Hosts() {
|
||||
items = append(
|
||||
items,
|
||||
[3]string{
|
||||
fmt.Sprintf("host_%d", host.ID),
|
||||
fmt.Sprintf("/host/%d", host.ID),
|
||||
host.Name,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return Div(
|
||||
Class("d-flex flex-column flex-shrink-0 p-3 text-bg-dark"),
|
||||
Style("width: 280px; height: 100vh"),
|
||||
Ul(
|
||||
Class("nav nav-pills flex-column mb-auto"),
|
||||
Map(items, func(item [3]string) Node {
|
||||
return NavMenuItem(item, currentNode == item[0])
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func NavMenuItem(item [3]string, isActive bool) Node {
|
||||
class := "nav-link"
|
||||
|
||||
if isActive {
|
||||
class += " active"
|
||||
} else {
|
||||
class += " text-white"
|
||||
}
|
||||
|
||||
return Li(
|
||||
Class("nav-item"),
|
||||
A(
|
||||
Class(class),
|
||||
Href(item[1]),
|
||||
Text(item[2]),
|
||||
),
|
||||
)
|
||||
}
|
||||
42
pkg/web/partial/pager.go
Normal file
42
pkg/web/partial/pager.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package partial
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func Paginate(path string, currentPage int, pages int) Node {
|
||||
return Nav(
|
||||
Attr("arial-label", "Pagination"),
|
||||
Ul(
|
||||
Class("pagination"),
|
||||
If(
|
||||
currentPage > 1,
|
||||
Li(
|
||||
Class("page-item"),
|
||||
A(
|
||||
Class("page-link"),
|
||||
Attr("arial-label", "Previous"),
|
||||
Href(strings.Replace(path, "%page%", fmt.Sprintf("%d", currentPage-1), -1)),
|
||||
Span(Attr("arial-hidden", "true"), Raw("«")),
|
||||
),
|
||||
),
|
||||
),
|
||||
If(
|
||||
currentPage < pages,
|
||||
Li(
|
||||
Class("page-item"),
|
||||
A(
|
||||
Class("page-link"),
|
||||
Attr("arial-label", "Next"),
|
||||
Href(strings.Replace(path, "%page%", fmt.Sprintf("%d", currentPage+1), -1)),
|
||||
Span(Attr("arial-hidden", "true"), Raw("»")),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue