Compare commits
No commits in common. "master" and "v2" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
config.json
|
||||
backup
|
||||
backup.json
|
||||
|
8
addr.go
8
addr.go
@ -84,7 +84,7 @@ func (a Addr) ValidSnapshots() ([]*ZfsSnapshot, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a Addr) SetManaged(managed bool) error {
|
||||
func (a Addr) SetManaged(val bool) error {
|
||||
log.WithFields(log.Fields{"addr": a}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"addr": a}).Debugf("done")
|
||||
|
||||
@ -99,9 +99,9 @@ func (a Addr) SetManaged(managed bool) error {
|
||||
} else {
|
||||
fs.mx.Lock()
|
||||
defer fs.mx.Unlock()
|
||||
if fs.managed != managed {
|
||||
if fs.managed != val {
|
||||
var cmd string
|
||||
if managed {
|
||||
if val {
|
||||
cmd = fmt.Sprintf("zfs set %s=+ %s", zfsManagedPropertyName, a.Path())
|
||||
} else {
|
||||
cmd = fmt.Sprintf("zfs set %s=- %s", zfsManagedPropertyName, a.Path())
|
||||
@ -111,7 +111,7 @@ func (a Addr) SetManaged(managed bool) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fs.managed = managed
|
||||
fs.managed = val
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
42
admin.go
42
admin.go
@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
@ -21,7 +20,6 @@ type AdminConfig struct {
|
||||
Users []*User `json:"users"`
|
||||
Secrets *SecretsConfig `json:"secrets"`
|
||||
Addr string `json:"addr"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type SecretsConfig struct {
|
||||
@ -44,7 +42,6 @@ func NewAdmin() *AdminConfig {
|
||||
Addr: "0.0.0.0:8080",
|
||||
Secrets: NewSecrets(),
|
||||
Users: make([]*User, 0),
|
||||
URL: "https://backup.example.com/",
|
||||
}
|
||||
|
||||
return a
|
||||
@ -77,6 +74,9 @@ func (a *AdminConfig) NewAdminUser() {
|
||||
a.Users = append(a.Users, u)
|
||||
|
||||
log.WithFields(log.Fields{}).Warnf("Admin user password : %s", p)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (a *AdminConfig) Run() {
|
||||
@ -103,6 +103,12 @@ func (a *AdminConfig) Run() {
|
||||
r.GET("/", HttpAnyIndex)
|
||||
r.POST("/", HttpAnyIndex)
|
||||
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "pong",
|
||||
})
|
||||
})
|
||||
|
||||
r.GET("/run", func(c *gin.Context) {
|
||||
cfg.Run()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
@ -110,29 +116,15 @@ func (a *AdminConfig) Run() {
|
||||
})
|
||||
})
|
||||
|
||||
r.GET("/save", func(c *gin.Context) {
|
||||
if err := cfg.Save(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": "error",
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "done",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
fsys, _ := fs.Sub(assets, "assets/static")
|
||||
r.StaticFS("/assets", http.FS(fsys))
|
||||
|
||||
protected := r.Group("p", HttpAuth())
|
||||
protected.GET("test", HttpAnyHome)
|
||||
protected.GET("home", HttpAnyHome)
|
||||
|
||||
unprotected := r.Group("u", HttpNoAuth())
|
||||
unprotected.GET("signin", HttpGetSignIn)
|
||||
unprotected.POST("submit", HttpPostSubmit)
|
||||
unprotected.GET("recover", HttpGetRecover)
|
||||
unprotected.GET("signin", HttpAnySignIn)
|
||||
unprotected.POST("signin", HttpAnySignIn)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: a.Addr,
|
||||
@ -168,15 +160,3 @@ func (a *AdminConfig) Run() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func FindUserID(user string) (uint64, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
for _, v := range cfg.Admin.Users {
|
||||
if v.Username == user {
|
||||
return v.ID, nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("no user")
|
||||
}
|
||||
|
13
app.go
13
app.go
@ -478,7 +478,7 @@ func (a *App) Transfer() error {
|
||||
defer log.WithFields(log.Fields{"app": a.name}).Debugf("done")
|
||||
|
||||
for _, src := range a.sources {
|
||||
dests := make([]Addr, 0)
|
||||
backedUp := false
|
||||
for _, dest := range a.destinations {
|
||||
dest2 := dest.Append("/" + src.Box() + "/" + src.Path())
|
||||
if dest2.Online() {
|
||||
@ -486,17 +486,18 @@ func (a *App) Transfer() error {
|
||||
log.WithFields(log.Fields{"app": a.name, "call": "Mkdir", "attr": dest, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
if err := TransferZfs(src, dest2); err != nil {
|
||||
log.WithFields(log.Fields{"app": a.name, "call": "TransferZfs", "src": src, "dest": dest, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
if err := dest2.SetManaged(true); err != nil {
|
||||
log.WithFields(log.Fields{"app": a.name, "call": "SetManaged", "src": src, "dest": dest, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
dests = append(dests, dest2)
|
||||
backedUp = true
|
||||
}
|
||||
}
|
||||
if n, err := TransferZfs(src, dests); err != nil {
|
||||
log.WithFields(log.Fields{"app": a.name, "call": "TransferZfs", "src": src, "dests": dests, "error": err}).Errorf("")
|
||||
return err
|
||||
} else if n > 0 {
|
||||
if backedUp {
|
||||
if err := src.SetBackedUp(true); err != nil {
|
||||
log.WithFields(log.Fields{"app": a.name, "call": "SetBackedUp", "src": src, "error": err}).Errorf("")
|
||||
return err
|
||||
|
@ -1,17 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<title>Home :: zBackup</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="text-center">
|
||||
<div class="container">
|
||||
<h1 class="h3 mb-3 font-weight-normal">Logged in.</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,24 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<title>Recover :: zBackup</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="/assets/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="text-center">
|
||||
<div class="container">
|
||||
<form class="form-signin" method="POST" action="/u/submit">
|
||||
<h1 class="h3 mb-3 font-weight-normal">Recover password</h1>
|
||||
<div class="form-group">
|
||||
<input type="username" class="form-control" id="username" placeholder="Username" name="username" required autofocus>
|
||||
</div>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit" >Recover</button>
|
||||
<p class="mt-5 mb-3 text-muted">© 2023-2024</p>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -18,10 +18,10 @@
|
||||
<input type="username" class="form-control" id="username" placeholder="Username" name="username" required autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" id="password" placeholder="Password" name="password" required>
|
||||
<input type="password" class="form-control" id="pwd" placeholder="Password" name="password" required>
|
||||
</div>
|
||||
<button class="btn btn-lg btn-primary btn-block" type="submit" >Sign in</button>
|
||||
<p class="mt-5 mb-3 text-muted">© 2023-2024</p>
|
||||
<p class="mt-5 mb-3 text-muted">© 2023</p>
|
||||
</form>
|
||||
<a href="/u/recover">Lost password</a>
|
||||
</div>
|
||||
|
110
box.go
110
box.go
@ -7,21 +7,31 @@ import (
|
||||
|
||||
"github.com/silenceper/pool"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type Box struct {
|
||||
name string
|
||||
addr string
|
||||
user string
|
||||
key string
|
||||
zfs *BoxZfs
|
||||
sshPool pool.Pool
|
||||
created bool
|
||||
online bool
|
||||
mx sync.Mutex
|
||||
name string
|
||||
addr string
|
||||
user string
|
||||
key string
|
||||
zfs *BoxZfs
|
||||
sshPool pool.Pool
|
||||
created bool
|
||||
online bool
|
||||
allowDirectConnect bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func (c *Config) NewBox(name, addr, user, key string) (b *Box, err error) {
|
||||
type BoxSshPool struct {
|
||||
signer ssh.Signer
|
||||
config *ssh.ClientConfig
|
||||
client *ssh.Client
|
||||
logged bool
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
func (c *Config) NewBox(name, addr, user, key string, direct bool) (b *Box, err error) {
|
||||
log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("done")
|
||||
|
||||
@ -46,9 +56,10 @@ func (c *Config) NewBox(name, addr, user, key string) (b *Box, err error) {
|
||||
zfs: &BoxZfs{
|
||||
online: false,
|
||||
},
|
||||
sshPool: p,
|
||||
online: false,
|
||||
created: true,
|
||||
sshPool: p,
|
||||
online: false,
|
||||
created: true,
|
||||
allowDirectConnect: true, //FIXME use direct
|
||||
}
|
||||
|
||||
b.zfs.box = b
|
||||
@ -128,34 +139,22 @@ func (b *Box) Exec(cmd string) (r string, err error) {
|
||||
return s.Exec(cmd)
|
||||
}
|
||||
|
||||
func TransferZfs(from Addr, to []Addr) (int, error) {
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"from": from, "to": to}).Debugf("done")
|
||||
|
||||
count := 0
|
||||
|
||||
for _, dest := range to {
|
||||
if err := TransferDirectZfs(from, dest); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "TransferDirectZfs", "attr": dest, "error": err}).Errorf("")
|
||||
return count, err
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
|
||||
}
|
||||
|
||||
func TransferDirectZfs(from, to Addr) error {
|
||||
func TransferZfs(from, to Addr) error {
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"from": from, "to": to}).Debugf("done")
|
||||
|
||||
var (
|
||||
err error
|
||||
fromSnapshots, toSnapshots []*ZfsSnapshot
|
||||
directTransfer bool
|
||||
)
|
||||
|
||||
if cfg.box[from.Box()].allowDirectConnect && cfg.box[to.Box()].allowDirectConnect {
|
||||
directTransfer = true
|
||||
} else {
|
||||
directTransfer = false
|
||||
}
|
||||
|
||||
if fromSnapshots, err = from.ValidSnapshots(); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "ValidSnapshots", "attr": from, "error": err}).Errorf("")
|
||||
return err
|
||||
@ -172,30 +171,29 @@ func TransferDirectZfs(from, to Addr) error {
|
||||
|
||||
if len(toSnapshots) == 0 {
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("initiating destination")
|
||||
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send " + fromSnapshots[0].String() + " | zfs recv -F " + to.Path()); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
|
||||
return err
|
||||
if directTransfer {
|
||||
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send " + fromSnapshots[0].String() + " | zfs recv -F " + to.Path()); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
newToSnapshot := &ZfsSnapshot{name: fromSnapshots[0].name, fs: cfg.box[to.Box()].zfs.filesystems[to.Path()]}
|
||||
toSnapshots = append(toSnapshots, newToSnapshot)
|
||||
cfg.box[to.Box()].zfs.filesystems[to.Path()].AddSnapshot(newToSnapshot)
|
||||
} else {
|
||||
//handle indirect transfer
|
||||
}
|
||||
newToSnapshot := &ZfsSnapshot{name: fromSnapshots[0].name, fs: cfg.box[to.Box()].zfs.filesystems[to.Path()]}
|
||||
toSnapshots = append(toSnapshots, newToSnapshot)
|
||||
cfg.box[to.Box()].zfs.filesystems[to.Path()].AddSnapshot(newToSnapshot)
|
||||
|
||||
}
|
||||
|
||||
fromFromSnapshotId := len(fromSnapshots) - 1
|
||||
fromToSnapshotId := -1
|
||||
for fromFromSnapshotId >= 0 {
|
||||
fromToSnapshotId = len(toSnapshots) - 1
|
||||
for fromToSnapshotId >= 0 {
|
||||
if fromSnapshots[fromFromSnapshotId].name == toSnapshots[fromToSnapshotId].name {
|
||||
break
|
||||
}
|
||||
fromToSnapshotId = fromToSnapshotId - 1
|
||||
}
|
||||
if fromToSnapshotId >= 0 {
|
||||
fromFromSnapshotId := -1
|
||||
lastToSnapshot := toSnapshots[len(toSnapshots)-1]
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("searching last snapshot %s", lastToSnapshot.String())
|
||||
for id, v := range fromSnapshots {
|
||||
if v.name == lastToSnapshot.name {
|
||||
fromFromSnapshotId = id
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("found %s", v.String())
|
||||
break
|
||||
}
|
||||
fromFromSnapshotId = fromFromSnapshotId - 1
|
||||
}
|
||||
|
||||
if fromFromSnapshotId == -1 {
|
||||
@ -206,9 +204,13 @@ func TransferDirectZfs(from, to Addr) error {
|
||||
|
||||
if fromFromSnapshotId < len(fromSnapshots)-1 {
|
||||
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("transfering from %s to %s", fromSnapshots[fromFromSnapshotId].name, fromSnapshots[len(fromSnapshots)-1].name)
|
||||
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send -I " + fromSnapshots[fromFromSnapshotId].String() + " " + fromSnapshots[len(fromSnapshots)-1].String() + " | zfs recv -F " + to.Path()); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
|
||||
return err
|
||||
if directTransfer {
|
||||
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send -I " + fromSnapshots[fromFromSnapshotId].String() + " " + fromSnapshots[len(fromSnapshots)-1].String() + " | zfs recv -F " + to.Path()); err != nil {
|
||||
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// handle indirect transfer
|
||||
}
|
||||
|
||||
for _, v := range fromSnapshots[fromFromSnapshotId+1:] {
|
||||
|
70
config.go
70
config.go
@ -13,19 +13,19 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tailscale/hujson"
|
||||
"github.com/tidwall/pretty"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ScheduleDuration map[string]string `json:"schedule,omitempty"`
|
||||
Box map[string]*BoxConfig `json:"box,omitempty"`
|
||||
Email *EmailConfig `json:"email,omitempty"`
|
||||
Apps []*AppConfig `json:"apps,omitempty"`
|
||||
Timezone string `json:"timezone,omitempty"`
|
||||
Admin *AdminConfig `json:"admin,omitempty"`
|
||||
box map[string]*Box `json:"-"`
|
||||
apps map[string]*App `json:"-"`
|
||||
timezone *time.Location `json:"-"`
|
||||
ScheduleDuration map[string]string `json:"schedule"`
|
||||
Box map[string]BoxConfig `json:"box"`
|
||||
Email EmailConfig `json:"email"`
|
||||
Apps []AppConfig `json:"apps"`
|
||||
Timezone string `json:"timezone"`
|
||||
Admin *AdminConfig `json:"admin"`
|
||||
Debug bool `json:"debug"`
|
||||
box map[string]*Box `json:"-"`
|
||||
apps map[string]*App `json:"-"`
|
||||
timezone *time.Location `json:"-"`
|
||||
}
|
||||
|
||||
var (
|
||||
@ -37,9 +37,10 @@ var (
|
||||
var sampleCfg []byte
|
||||
|
||||
type BoxConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
User string `json:"user"`
|
||||
Key string `json:"key"`
|
||||
Addr string `json:"addr"`
|
||||
User string `json:"user"`
|
||||
Key string `json:"key"`
|
||||
AllowDirectConnect bool `json:"allow_direct_connect"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
@ -47,9 +48,9 @@ type AppConfig struct {
|
||||
Schedule []string `json:"schedule"`
|
||||
Sources []string `json:"src"`
|
||||
Destinations []string `json:"dest"`
|
||||
Before map[string]string `json:"before,omitempty"`
|
||||
After map[string]string `json:"after,omitempty"`
|
||||
Active bool `json:"active,omitempty"`
|
||||
Before map[string]string `json:"before"`
|
||||
After map[string]string `json:"after"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// Load config from file
|
||||
@ -95,7 +96,7 @@ func LoadConfigByte(conf []byte) (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.Email != nil {
|
||||
if c.Email.Active {
|
||||
if len(c.Email.SmtpHost) == 0 {
|
||||
err := fmt.Errorf("no smtp")
|
||||
log.WithFields(log.Fields{"error": err}).Errorf("")
|
||||
@ -135,7 +136,7 @@ func LoadConfigByte(conf []byte) (*Config, error) {
|
||||
|
||||
c.box = make(map[string]*Box)
|
||||
for k, v := range c.Box {
|
||||
if b, err := c.NewBox(k, v.Addr, v.User, v.Key); err != nil {
|
||||
if b, err := c.NewBox(k, v.Addr, v.User, v.Key, v.AllowDirectConnect); err != nil {
|
||||
log.WithFields(log.Fields{"call": "NewBox", "attr": k, "error": err}).Errorf("")
|
||||
return nil, err
|
||||
} else {
|
||||
@ -180,37 +181,6 @@ func LoadConfigByte(conf []byte) (*Config, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Save config
|
||||
func (c *Config) Save() error {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
cfgMx.Lock()
|
||||
defer cfgMx.Unlock()
|
||||
|
||||
b, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"error": err, "call": "json.Marshal"}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
r := pretty.PrettyOptions(b, &pretty.Options{Indent: " "})
|
||||
|
||||
f, err := os.Create(*cfgFile)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"error": err, "call": "os.Open"}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := f.Write(r); err != nil {
|
||||
log.WithFields(log.Fields{"error": err, "call": "File.Write"}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Run config
|
||||
func (c *Config) Run() {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
@ -312,4 +282,6 @@ func (c *Config) Run() {
|
||||
log.WithFields(log.Fields{"call": "email.Send", "error": err}).Errorf("")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
3
email.go
3
email.go
@ -6,6 +6,7 @@ import (
|
||||
"net/smtp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -14,9 +15,11 @@ import (
|
||||
type Email struct {
|
||||
startTime time.Time
|
||||
items []string
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
Active bool `json:"active"`
|
||||
SmtpHost string `json:"smtp"`
|
||||
FromEmail string `json:"email_from"`
|
||||
ToEmail []string `json:"email_to"`
|
||||
|
43
go.mod
43
go.mod
@ -1,40 +1,13 @@
|
||||
module git.siteop.biz/shoopea/backup
|
||||
|
||||
go 1.21
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sethvargo/go-password v0.2.0
|
||||
github.com/silenceper/pool v1.0.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
|
||||
github.com/tidwall/pretty v1.2.1
|
||||
golang.org/x/crypto v0.29.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sethvargo/go-password v0.2.0 // indirect
|
||||
github.com/silenceper/pool v1.0.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||
golang.org/x/crypto v0.9.0
|
||||
)
|
||||
|
73
go.sum
73
go.sum
@ -5,7 +5,6 @@ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
@ -13,7 +12,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
@ -25,7 +23,6 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@ -33,6 +30,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
@ -45,7 +43,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
@ -53,6 +50,7 @@ github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc
|
||||
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
|
||||
github.com/silenceper/pool v1.0.0 h1:JTCaA+U6hJAA0P8nCx+JfsRCHMwLTfatsm5QXelffmU=
|
||||
github.com/silenceper/pool v1.0.0/go.mod h1:3DN13bqAbq86Lmzf6iUXWEPIWFPOSYVfaoceFvilKKI=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
@ -67,38 +65,77 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM=
|
||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
99
http.go
99
http.go
@ -4,13 +4,9 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func HttpAuth() gin.HandlerFunc {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
// Search signed-in userID
|
||||
userID := 0
|
||||
@ -18,44 +14,18 @@ func HttpAuth() gin.HandlerFunc {
|
||||
// Return 404 and abort handlers chain.
|
||||
c.String(http.StatusNotFound, "404 page not found")
|
||||
c.AbortWithStatus(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HttpNoAuth() gin.HandlerFunc {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
return func(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func HttpPostSubmit(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
t, err := GetCSRFToken(c)
|
||||
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "bad token")
|
||||
return
|
||||
}
|
||||
|
||||
if !t.Valid() {
|
||||
c.String(http.StatusBadRequest, "expired token")
|
||||
return
|
||||
}
|
||||
|
||||
switch t.GetPath() {
|
||||
case "signin":
|
||||
log.WithFields(log.Fields{"call": "Context.Request.ParseForm", "err": err}).Debugf("submit signin")
|
||||
HttpSubmitSignIn(c)
|
||||
HttpAnyIndex(c)
|
||||
default:
|
||||
log.WithFields(log.Fields{"call": "Context.Request.ParseForm", "err": err}).Debugf("submit %s", t.GetPath())
|
||||
c.String(http.StatusBadRequest, "")
|
||||
}
|
||||
|
||||
func HttpAnySignIn(c *gin.Context) {
|
||||
if GetWebSessionUserID(c) > 0 {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/p/home")
|
||||
} else {
|
||||
@ -66,83 +36,32 @@ func HttpPostSubmit(c *gin.Context) {
|
||||
"Error": warning,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func HttpGetRecover(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
SetCSRFToken(c)
|
||||
c.HTML(http.StatusOK, "page-recover.html", gin.H{})
|
||||
}
|
||||
|
||||
func HttpGetSignIn(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
SetCSRFToken(c)
|
||||
c.HTML(http.StatusOK, "page-signin.html", gin.H{})
|
||||
return
|
||||
}
|
||||
|
||||
func HttpAnyIndex(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if GetWebSessionUserID(c) > 0 {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/p/home")
|
||||
} else {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/u/signin")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HttpAnyHome(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if GetWebSessionUserID(c) == 0 {
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/u/signin")
|
||||
} else {
|
||||
SetCSRFToken(c)
|
||||
c.HTML(http.StatusOK, "page-home.html", gin.H{})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetWebSessionUserID(c *gin.Context) uint64 {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
func GetWebSessionUserID(c *gin.Context) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func HttpSubmitSignIn(c *gin.Context) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
c.SetCookie("warning", "Unable to parse form", 0, "/", cfg.Admin.URL, false, true)
|
||||
log.WithFields(log.Fields{"call": "Context.Request.ParseForm", "err": err}).Debugf("")
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Request.FormValue("username")
|
||||
password := c.Request.FormValue("password")
|
||||
|
||||
userID, err := FindUserID(username)
|
||||
if err != nil {
|
||||
c.SetCookie("warning", "Invalid user or password", 0, "/", cfg.Admin.URL, false, true)
|
||||
log.WithFields(log.Fields{"call": "FindUserID", "attr": username, "err": err}).Debugf("")
|
||||
return
|
||||
}
|
||||
|
||||
if !VerifyUserPassword(userID, password) {
|
||||
c.SetCookie("warning", "Invalid user or password", 0, "/", cfg.Admin.URL, false, true)
|
||||
log.WithFields(log.Fields{"call": "VerifyUserPassword", "attr": "***"}).Debugf("auth not ok")
|
||||
return
|
||||
}
|
||||
|
||||
t := NewSessionToken(userID)
|
||||
|
||||
c.SetCookie("session", t.Encode(), 9999999999, "/", cfg.Admin.URL, false, true)
|
||||
c.SetCookie("warning", "", -1, "/", cfg.Admin.URL, false, true)
|
||||
func SetCSRFToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
298
http_tokens.go
298
http_tokens.go
@ -1,298 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var sessionMap map[string]uint64
|
||||
|
||||
type SessionToken struct {
|
||||
Identifier [32]byte
|
||||
Verifier [32]byte
|
||||
}
|
||||
|
||||
type CSRFToken struct {
|
||||
Seed [4]byte
|
||||
Path [16]byte
|
||||
Time int64
|
||||
Checksum uint32
|
||||
}
|
||||
|
||||
func NewSessionToken(userID uint64) *SessionToken {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
identifier := make([]byte, 32)
|
||||
_, err := rand.Read(identifier)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "rand.Read", "attr": "identifier", "err": err}).Debugf("")
|
||||
return &SessionToken{}
|
||||
}
|
||||
|
||||
verifier := make([]byte, 32)
|
||||
_, err = rand.Read(verifier)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "rand.Read", "attr": "verifier", "err": err}).Debugf("")
|
||||
return &SessionToken{}
|
||||
}
|
||||
|
||||
hash := hmac.New(sha256.New, verifier)
|
||||
hash.Write(verifier)
|
||||
hashVerifier := hash.Sum(nil)
|
||||
|
||||
t := SessionToken{}
|
||||
|
||||
copy(t.Verifier[:], verifier[:32])
|
||||
copy(t.Identifier[:], hashVerifier[:32])
|
||||
|
||||
return &t
|
||||
|
||||
}
|
||||
|
||||
func (t *SessionToken) Bytes() []byte {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, t)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (t *SessionToken) Load(b []byte) error {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if len(b) != binary.Size(t) {
|
||||
return fmt.Errorf("wrong size")
|
||||
}
|
||||
|
||||
r := bytes.NewReader(b)
|
||||
|
||||
err := binary.Read(r, binary.LittleEndian, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SessionToken) GetSessionID() uint64 {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if sessionID, ok := sessionMap[string(t.Identifier[:])]; !ok {
|
||||
return 0
|
||||
} else {
|
||||
return sessionID
|
||||
}
|
||||
}
|
||||
|
||||
func GetSessionTokenParam(c *gin.Context) (*SessionToken, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "Context.Request.ParseForm", "err": err}).Errorf("")
|
||||
return nil, err
|
||||
}
|
||||
session := c.Param("key")
|
||||
|
||||
return DecodeSessionToken(session)
|
||||
}
|
||||
|
||||
func GetSessionTokenCookie(c *gin.Context) (*SessionToken, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if session, err := c.Cookie("session"); err != nil {
|
||||
log.WithFields(log.Fields{"call": "Context.Cookie", "attr": "session", "err": err}).Errorf("")
|
||||
return nil, err
|
||||
} else {
|
||||
return DecodeSessionToken(session)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *SessionToken) Encode() string {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
return base64.StdEncoding.EncodeToString(t.Bytes())
|
||||
}
|
||||
|
||||
func DecodeSessionToken(s string) (*SessionToken, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "base64.StdEncoding.DecodeString", "attr": "session", "err": err}).Errorf("")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := SessionToken{}
|
||||
err = t.Load(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (t *CSRFToken) GetPath() string {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
for _, b := range t.Path {
|
||||
if b > 0 {
|
||||
buf.WriteByte(b)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (t *CSRFToken) Bytes() []byte {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, binary.LittleEndian, t)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (t *CSRFToken) Load(b []byte) error {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if len(b) != binary.Size(t) {
|
||||
return fmt.Errorf("wrong size")
|
||||
}
|
||||
r := bytes.NewReader(b)
|
||||
err := binary.Read(r, binary.LittleEndian, t)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "binary.Read", "attr": "b", "err": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
chk := t.Checksum
|
||||
t.Checksum = 0
|
||||
if crc32.ChecksumIEEE(t.Bytes()) != chk {
|
||||
return fmt.Errorf("wrong checksum")
|
||||
}
|
||||
t.Checksum = chk
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewCSRFToken(c *gin.Context) *CSRFToken {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
t := CSRFToken{}
|
||||
|
||||
copy(t.Path[:], c.Request.URL.Path[0:])
|
||||
|
||||
t.Time = time.Now().UTC().Unix()
|
||||
|
||||
_, err := rand.Read(t.Seed[:])
|
||||
log.WithFields(log.Fields{"call": "rand.Read", "attr": "seed", "err": err}).Errorf("")
|
||||
|
||||
t.Checksum = crc32.ChecksumIEEE(t.Bytes())
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func GetCSRFToken(c *gin.Context) (*CSRFToken, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if context, err := c.Cookie("context"); err != nil {
|
||||
return nil, fmt.Errorf("no context param")
|
||||
} else {
|
||||
return DecodeCSRFToken(context)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *CSRFToken) Encode() string {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(cfg.Admin.Secrets.ContextKey))
|
||||
key := hash.Sum(nil)
|
||||
|
||||
block, _ := aes.NewCipher(key)
|
||||
ciphertext := make([]byte, 32) // size of CSRFToken
|
||||
if binary.Size(t) != 32 {
|
||||
log.WithFields(log.Fields{"err": fmt.Errorf("size is wrong")}).Fatalf("")
|
||||
}
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext[:], t.Bytes())
|
||||
|
||||
return base64.StdEncoding.EncodeToString(ciphertext)
|
||||
}
|
||||
|
||||
func DecodeCSRFToken(s string) (*CSRFToken, error) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b) != binary.Size(CSRFToken{}) {
|
||||
return nil, fmt.Errorf("wrong size")
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(cfg.Admin.Secrets.ContextKey))
|
||||
key := hash.Sum(nil)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(b, b)
|
||||
|
||||
t := CSRFToken{}
|
||||
err = t.Load(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (t *CSRFToken) Valid() bool {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
if time.Now().UTC().Sub(time.Unix(t.Time, 0)) > time.Duration(cfg.Admin.Secrets.ContextExpiration)*time.Second {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SetCSRFToken(c *gin.Context) {
|
||||
c.SetCookie("context", NewCSRFToken(c).Encode(), cfg.Admin.Secrets.ContextExpiration, "/", cfg.Admin.URL, false, true)
|
||||
return
|
||||
}
|
6
ssh.go
6
ssh.go
@ -137,8 +137,8 @@ func (s *Ssh) ExecPipe(cmd string) error {
|
||||
}
|
||||
s.session = session
|
||||
|
||||
if err = s.session.Setenv("TZ", cfg.Timezone); err != nil {
|
||||
log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Setenv", "attr": cfg.Timezone, "error": err}).Errorf("")
|
||||
if s.session.Setenv("TZ", cfg.Timezone); err != nil {
|
||||
log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Setenv", "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ func (s *Ssh) ExecPipe(cmd string) error {
|
||||
}
|
||||
|
||||
if err = s.session.Start(cmd); err != nil {
|
||||
log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Start", "attr": cmd, "error": err}).Errorf("")
|
||||
log.WithFields(log.Fields{"name": s.name, "cmd": cmd, "call": "session.Start", "error": err}).Errorf("")
|
||||
s.session.Close()
|
||||
return err
|
||||
}
|
||||
|
30
user.go
30
user.go
@ -12,20 +12,16 @@ import (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uint64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Salt string `json:"salt"`
|
||||
Password string `json:"passwd"`
|
||||
Email string `json:"email"`
|
||||
Passwd string `json:"passwd"`
|
||||
}
|
||||
|
||||
func NewUser(name, passwd string) (*User, error) {
|
||||
log.WithFields(log.Fields{"name": name}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"name": name}).Debugf("done")
|
||||
|
||||
userID := uint64(1)
|
||||
for _, v := range cfg.Admin.Users {
|
||||
userID = max(v.ID+1, userID)
|
||||
if v.Username == name {
|
||||
err := errors.New("user already exists")
|
||||
log.WithFields(log.Fields{"name": name, "error": err}).Errorf("")
|
||||
@ -34,7 +30,6 @@ func NewUser(name, passwd string) (*User, error) {
|
||||
}
|
||||
|
||||
u := &User{
|
||||
ID: userID,
|
||||
Username: name,
|
||||
}
|
||||
|
||||
@ -49,7 +44,7 @@ func NewUser(name, passwd string) (*User, error) {
|
||||
log.WithFields(log.Fields{"name": name, "call": "HashPassword", "error": err}).Errorf("")
|
||||
return nil, err
|
||||
} else {
|
||||
u.Password = hex.EncodeToString(pass)
|
||||
u.Passwd = hex.EncodeToString(pass)
|
||||
}
|
||||
|
||||
return u, nil
|
||||
@ -79,24 +74,3 @@ func (u *User) HashPassword(passwd string) ([]byte, error) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func VerifyUserPassword(userID uint64, clearPass string) bool {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
var u *User
|
||||
|
||||
for _, v := range cfg.Admin.Users {
|
||||
if v.ID == userID {
|
||||
u = v
|
||||
}
|
||||
}
|
||||
|
||||
hashPass, err := u.HashPassword(clearPass)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{"call": "user.HashPassword", "attr": "***", "error": err}).Errorf("")
|
||||
return false
|
||||
}
|
||||
|
||||
return hex.EncodeToString(hashPass) == u.Password
|
||||
}
|
||||
|
10
version.go
10
version.go
@ -1,7 +1,7 @@
|
||||
// Code generated by version.sh (@generated) DO NOT EDIT.
|
||||
package main
|
||||
var githash = "a31ff56"
|
||||
var branch = "master"
|
||||
var buildstamp = "2024-11-17_22:42:14"
|
||||
var commits = "107"
|
||||
var version = "a31ff56-b107 - 2024-11-17_22:42:14"
|
||||
var githash = "1a1713e"
|
||||
var branch = "v2"
|
||||
var buildstamp = "2023-08-21_12:35:47"
|
||||
var commits = "83"
|
||||
var version = "1a1713e-b83 - 2023-08-21_12:35:47"
|
||||
|
3
zfs.go
3
zfs.go
@ -311,9 +311,6 @@ func (fs *ZfsFs) AddSnapshot(s *ZfsSnapshot) error {
|
||||
}
|
||||
|
||||
func (fs *ZfsFs) ValidSnapshots() []*ZfsSnapshot {
|
||||
log.WithFields(log.Fields{"box": fs.zfs.box.name, "fs": fs.path}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"box": fs.zfs.box.name, "fs": fs.path}).Debugf("done")
|
||||
|
||||
tab := make([]*ZfsSnapshot, 0)
|
||||
|
||||
for _, s := range fs.snapshots {
|
||||
|
Loading…
x
Reference in New Issue
Block a user