store download & add key
This commit is contained in:
parent
1672b901d4
commit
1e3ac9a0fb
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
*.sw[po]
|
*.sw[po]
|
||||||
modd-local.conf
|
modd-local.conf
|
||||||
/tmp
|
/tmp
|
||||||
/go.work
|
|
||||||
/dist
|
/dist
|
||||||
|
/go.work
|
||||||
|
/go.work.sum
|
||||||
|
@ -12,13 +12,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func casCleaner() {
|
func casCleaner() {
|
||||||
for {
|
for range time.Tick(*cacheCleanDelay) {
|
||||||
|
if !wPublicState.Get().Store.Open {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
err := cleanCAS()
|
err := cleanCAS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("warn: couldn't clean cache: ", err)
|
log.Print("warn: couldn't clean cache: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(*cacheCleanDelay)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,4 +6,7 @@ import (
|
|||||||
"m.cluseau.fr/go/httperr"
|
"m.cluseau.fr/go/httperr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotFound = httperr.NewStd(404, http.StatusNotFound, "not found")
|
var (
|
||||||
|
ErrNotFound = httperr.NewStd(404, http.StatusNotFound, "not found")
|
||||||
|
ErrInvalidToken = httperr.NewStd(403, http.StatusForbidden, "invalid token")
|
||||||
|
)
|
||||||
|
@ -53,7 +53,7 @@ func main() {
|
|||||||
if autoUnlock != "" {
|
if autoUnlock != "" {
|
||||||
log.Printf("auto-unlocking the store")
|
log.Printf("auto-unlocking the store")
|
||||||
err := unlockSecretStore([]byte(autoUnlock))
|
err := unlockSecretStore([]byte(autoUnlock))
|
||||||
if err != nil {
|
if err.Any() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base32"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -20,7 +18,8 @@ import (
|
|||||||
|
|
||||||
var secStore *secretstore.Store
|
var secStore *secretstore.Store
|
||||||
|
|
||||||
func secStorePath(name string) string { return filepath.Join(*dataDir, "secrets", name) }
|
func secStoreRoot() string { return filepath.Join(*dataDir, "secrets") }
|
||||||
|
func secStorePath(name string) string { return filepath.Join(secStoreRoot(), name) }
|
||||||
func secKeysStorePath() string { return secStorePath(".keys") }
|
func secKeysStorePath() string { return secStorePath(".keys") }
|
||||||
|
|
||||||
func openSecretStore() {
|
func openSecretStore() {
|
||||||
@ -64,7 +63,7 @@ var (
|
|||||||
ErrInvalidPassphrase = httperr.NewStd(http.StatusBadRequest, 2, "invalid passphrase")
|
ErrInvalidPassphrase = httperr.NewStd(http.StatusBadRequest, 2, "invalid passphrase")
|
||||||
)
|
)
|
||||||
|
|
||||||
func unlockSecretStore(passphrase []byte) *httperr.Error {
|
func unlockSecretStore(passphrase []byte) (err httperr.Error) {
|
||||||
unlockMutex.Lock()
|
unlockMutex.Lock()
|
||||||
defer unlockMutex.Unlock()
|
defer unlockMutex.Unlock()
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ func unlockSecretStore(passphrase []byte) *httperr.Error {
|
|||||||
if secStore.IsNew() {
|
if secStore.IsNew() {
|
||||||
err := secStore.Init(passphrase)
|
err := secStore.Init(passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return httperr.New(http.StatusInternalServerError, err)
|
return httperr.Internal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = secStore.SaveTo(secKeysStorePath())
|
err = secStore.SaveTo(secKeysStorePath())
|
||||||
@ -83,7 +82,7 @@ func unlockSecretStore(passphrase []byte) *httperr.Error {
|
|||||||
log.Print("secret store save error: ", err)
|
log.Print("secret store save error: ", err)
|
||||||
secStore.Close()
|
secStore.Close()
|
||||||
|
|
||||||
return httperr.New(http.StatusInternalServerError, err)
|
return httperr.Internal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -98,25 +97,21 @@ func unlockSecretStore(passphrase []byte) *httperr.Error {
|
|||||||
log.Print("failed to read admin token: ", err)
|
log.Print("failed to read admin token: ", err)
|
||||||
secStore.Close()
|
secStore.Close()
|
||||||
|
|
||||||
return httperr.New(http.StatusInternalServerError, err)
|
return httperr.Internal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
randBytes := make([]byte, 32)
|
token, err = newToken(32)
|
||||||
_, err := rand.Read(randBytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("rand read error: ", err)
|
|
||||||
secStore.Close()
|
secStore.Close()
|
||||||
|
return httperr.Internal(err)
|
||||||
return httperr.New(http.StatusInternalServerError, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randBytes)
|
|
||||||
err = writeSecret("admin-token", token)
|
err = writeSecret("admin-token", token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("write error: ", err)
|
log.Print("write error: ", err)
|
||||||
secStore.Close()
|
secStore.Close()
|
||||||
|
|
||||||
return httperr.New(http.StatusInternalServerError, err)
|
return httperr.Internal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("wrote new admin token")
|
log.Print("wrote new admin token")
|
||||||
@ -124,6 +119,18 @@ func unlockSecretStore(passphrase []byte) *httperr.Error {
|
|||||||
|
|
||||||
*adminToken = token
|
*adminToken = token
|
||||||
|
|
||||||
|
{
|
||||||
|
token, err := newToken(16)
|
||||||
|
if err != nil {
|
||||||
|
secStore.Close()
|
||||||
|
return httperr.Internal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wState.Change(func(v *State) {
|
||||||
|
v.Store.DownloadToken = token
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
wPublicState.Change(func(v *PublicState) {
|
wPublicState.Change(func(v *PublicState) {
|
||||||
v.Store.New = false
|
v.Store.New = false
|
||||||
v.Store.Open = true
|
v.Store.Open = true
|
||||||
@ -132,7 +139,7 @@ func unlockSecretStore(passphrase []byte) *httperr.Error {
|
|||||||
go updateState()
|
go updateState()
|
||||||
go migrateSecrets()
|
go migrateSecrets()
|
||||||
|
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func readSecret(name string, value any) (err error) {
|
func readSecret(name string, value any) (err error) {
|
||||||
|
@ -20,6 +20,10 @@ var wPublicState = watchable.New[PublicState]()
|
|||||||
type State struct {
|
type State struct {
|
||||||
HasConfig bool
|
HasConfig bool
|
||||||
|
|
||||||
|
Store struct {
|
||||||
|
DownloadToken string
|
||||||
|
}
|
||||||
|
|
||||||
Clusters []ClusterState
|
Clusters []ClusterState
|
||||||
Hosts []HostState
|
Hosts []HostState
|
||||||
Config *localconfig.Config
|
Config *localconfig.Config
|
||||||
|
24
cmd/dkl-local-server/token.go
Normal file
24
cmd/dkl-local-server/token.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base32"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"m.cluseau.fr/go/httperr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newToken(sizeInBytes int) (token string, err error) {
|
||||||
|
randBytes := make([]byte, sizeInBytes)
|
||||||
|
|
||||||
|
_, err = rand.Read(randBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("rand read error: ", err)
|
||||||
|
err = httperr.New(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randBytes)
|
||||||
|
return
|
||||||
|
}
|
@ -1,7 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
)
|
)
|
||||||
@ -14,10 +19,80 @@ func wsUnlockStore(req *restful.Request, resp *restful.Response) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unlockSecretStore([]byte(passphrase)); err != nil {
|
if err := unlockSecretStore([]byte(passphrase)); err.Any() {
|
||||||
err.WriteJSON(resp.ResponseWriter)
|
err.WriteJSON(resp.ResponseWriter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.WriteEntity(*adminToken)
|
resp.WriteEntity(*adminToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wsStoreDownload(req *restful.Request, resp *restful.Response) {
|
||||||
|
token := req.QueryParameter("token")
|
||||||
|
if token != wState.Get().Store.DownloadToken {
|
||||||
|
wsError(resp, ErrInvalidToken)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
arch := tar.NewWriter(buf)
|
||||||
|
|
||||||
|
root := os.DirFS(secStoreRoot())
|
||||||
|
|
||||||
|
err := fs.WalkDir(root, ".", func(path string, d fs.DirEntry, readErr error) (err error) {
|
||||||
|
if readErr != nil {
|
||||||
|
err = readErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == "." {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := d.Info()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr, err := tar.FileInfoHeader(fi, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr.Name = path
|
||||||
|
hdr.Uid = 0
|
||||||
|
hdr.Gid = 0
|
||||||
|
|
||||||
|
err = arch.WriteHeader(hdr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := root.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
io.Copy(arch, f)
|
||||||
|
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = arch.Close()
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteTo(resp)
|
||||||
|
}
|
||||||
|
27
cmd/dkl-local-server/ws-store.go
Normal file
27
cmd/dkl-local-server/ws-store.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
restful "github.com/emicklei/go-restful"
|
||||||
|
)
|
||||||
|
|
||||||
|
func wsStoreAddKey(req *restful.Request, resp *restful.Response) {
|
||||||
|
var passphrase string
|
||||||
|
|
||||||
|
err := req.ReadEntity(&passphrase)
|
||||||
|
if err != nil {
|
||||||
|
wsBadRequest(resp, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(passphrase) == 0 {
|
||||||
|
wsBadRequest(resp, "no passphrase given")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secStore.AddKey([]byte(passphrase))
|
||||||
|
err = secStore.SaveTo(secKeysStorePath())
|
||||||
|
if err != nil {
|
||||||
|
wsError(resp, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -10,6 +11,7 @@ import (
|
|||||||
|
|
||||||
cfsslconfig "github.com/cloudflare/cfssl/config"
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
||||||
"github.com/emicklei/go-restful"
|
"github.com/emicklei/go-restful"
|
||||||
|
"m.cluseau.fr/go/httperr"
|
||||||
|
|
||||||
"novit.tech/direktil/pkg/localconfig"
|
"novit.tech/direktil/pkg/localconfig"
|
||||||
|
|
||||||
@ -28,6 +30,10 @@ func registerWS(rest *restful.Container) {
|
|||||||
Reads("").
|
Reads("").
|
||||||
Writes("").
|
Writes("").
|
||||||
Doc("Try to unlock the store")).
|
Doc("Try to unlock the store")).
|
||||||
|
Route(ws.GET("/store.tar").To(wsStoreDownload).
|
||||||
|
Produces(mime.TAR).
|
||||||
|
Param(ws.QueryParameter("token", "the download token")).
|
||||||
|
Doc("Fetch the encrypted store")).
|
||||||
Route(ws.GET("/downloads/{token}/{asset}").To(wsDownload).
|
Route(ws.GET("/downloads/{token}/{asset}").To(wsDownload).
|
||||||
Param(ws.PathParameter("token", "the download token")).
|
Param(ws.PathParameter("token", "the download token")).
|
||||||
Param(ws.PathParameter("asset", "the requested asset")).
|
Param(ws.PathParameter("asset", "the requested asset")).
|
||||||
@ -42,12 +48,21 @@ func registerWS(rest *restful.Container) {
|
|||||||
Filter(adminAuth).
|
Filter(adminAuth).
|
||||||
HeaderParameter("Authorization", "Admin bearer token")
|
HeaderParameter("Authorization", "Admin bearer token")
|
||||||
|
|
||||||
|
// - store management
|
||||||
|
ws.Route(ws.POST("/store/add-key").To(wsStoreAddKey).
|
||||||
|
Consumes("application/json").Reads("").
|
||||||
|
Produces("application/json").
|
||||||
|
Doc("Add an unlock key to the store"))
|
||||||
|
|
||||||
// - downloads
|
// - downloads
|
||||||
ws.Route(ws.POST("/authorize-download").To(wsAuthorizeDownload).
|
ws.Route(ws.POST("/authorize-download").To(wsAuthorizeDownload).
|
||||||
|
Consumes("application/json").Reads(DownloadSpec{}).
|
||||||
|
Produces("application/json").
|
||||||
Doc("Create a download token for the given download"))
|
Doc("Create a download token for the given download"))
|
||||||
|
|
||||||
// - configs API
|
// - configs API
|
||||||
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
ws.Route(ws.POST("/configs").To(wsUploadConfig).
|
||||||
|
Consumes(mime.YAML).
|
||||||
Doc("Upload a new current configuration, archiving the previous one"))
|
Doc("Upload a new current configuration, archiving the previous one"))
|
||||||
|
|
||||||
// - clusters API
|
// - clusters API
|
||||||
@ -180,11 +195,20 @@ func wsNotFound(req *restful.Request, resp *restful.Response) {
|
|||||||
http.NotFound(resp.ResponseWriter, req.Request)
|
http.NotFound(resp.ResponseWriter, req.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wsBadRequest(resp *restful.Response, err string) {
|
||||||
|
httperr.New(http.StatusBadRequest, errors.New(err)).WriteJSON(resp.ResponseWriter)
|
||||||
|
}
|
||||||
|
|
||||||
func wsError(resp *restful.Response, err error) {
|
func wsError(resp *restful.Response, err error) {
|
||||||
log.Output(2, fmt.Sprint("request failed: ", err))
|
log.Output(2, fmt.Sprint("request failed: ", err))
|
||||||
resp.WriteErrorString(
|
|
||||||
http.StatusInternalServerError,
|
switch err := err.(type) {
|
||||||
http.StatusText(http.StatusInternalServerError))
|
case httperr.Error:
|
||||||
|
err.WriteJSON(resp.ResponseWriter)
|
||||||
|
|
||||||
|
default:
|
||||||
|
httperr.Internal(err).WriteJSON(resp.ResponseWriter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func wsRender(resp *restful.Response, sslCfg *cfsslconfig.Config, tmplStr string, value interface{}) {
|
func wsRender(resp *restful.Response, sslCfg *cfsslconfig.Config, tmplStr string, value interface{}) {
|
||||||
|
14
go.mod
14
go.mod
@ -13,12 +13,12 @@ require (
|
|||||||
github.com/miolini/datacounter v1.0.3
|
github.com/miolini/datacounter v1.0.3
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||||
golang.org/x/crypto v0.5.0
|
golang.org/x/crypto v0.6.0
|
||||||
gopkg.in/src-d/go-billy.v4 v4.3.2
|
gopkg.in/src-d/go-billy.v4 v4.3.2
|
||||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
k8s.io/apimachinery v0.26.1
|
k8s.io/apimachinery v0.26.1
|
||||||
m.cluseau.fr/go v0.0.0-20230206224905-5322a9bff2ec
|
m.cluseau.fr/go v0.0.0-20230213110602-74f6da73b5fd
|
||||||
novit.tech/direktil/pkg v0.0.0-20230201224712-5e39572dc50e
|
novit.tech/direktil/pkg v0.0.0-20230201224712-5e39572dc50e
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,15 +57,15 @@ require (
|
|||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 // indirect
|
github.com/zmap/zcrypto v0.0.0-20230205235340-d51ce4775101 // indirect
|
||||||
github.com/zmap/zlint/v3 v3.1.0 // indirect
|
github.com/zmap/zlint/v3 v3.1.0 // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/net v0.5.0 // indirect
|
golang.org/x/net v0.6.0 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
golang.org/x/text v0.6.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.5.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/klog/v2 v2.90.0 // indirect
|
k8s.io/klog/v2 v2.90.0 // indirect
|
||||||
k8s.io/utils v0.0.0-20230202215443-34013725500c // indirect
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
||||||
)
|
)
|
||||||
|
15
go.sum
15
go.sum
@ -847,6 +847,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
|||||||
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||||
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -885,6 +887,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -939,6 +943,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||||
|
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1039,6 +1045,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR
|
|||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||||
|
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -1050,6 +1057,8 @@ 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -1123,6 +1132,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
|||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
|
golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4=
|
||||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||||
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -1312,10 +1323,14 @@ k8s.io/utils v0.0.0-20230115233650-391b47cb4029 h1:L8zDtT4jrxj+TaQYD0k8KNlr556Wa
|
|||||||
k8s.io/utils v0.0.0-20230115233650-391b47cb4029/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20230115233650-391b47cb4029/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
k8s.io/utils v0.0.0-20230202215443-34013725500c h1:YVqDar2X7YiQa/DVAXFMDIfGF8uGrHQemlrwRU5NlVI=
|
k8s.io/utils v0.0.0-20230202215443-34013725500c h1:YVqDar2X7YiQa/DVAXFMDIfGF8uGrHQemlrwRU5NlVI=
|
||||||
k8s.io/utils v0.0.0-20230202215443-34013725500c/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20230202215443-34013725500c/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
|
||||||
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
m.cluseau.fr/go v0.0.0-20230205163051-2d3050134ad5 h1:ss7MlpIRrkji6QhhRn4sTkOf44Y6f3roO+7kFXs0Q0U=
|
m.cluseau.fr/go v0.0.0-20230205163051-2d3050134ad5 h1:ss7MlpIRrkji6QhhRn4sTkOf44Y6f3roO+7kFXs0Q0U=
|
||||||
m.cluseau.fr/go v0.0.0-20230205163051-2d3050134ad5/go.mod h1:1DYAavEumEsHasA2RbRUExaEodBB2NAYD+IcwmgVkRM=
|
m.cluseau.fr/go v0.0.0-20230205163051-2d3050134ad5/go.mod h1:1DYAavEumEsHasA2RbRUExaEodBB2NAYD+IcwmgVkRM=
|
||||||
m.cluseau.fr/go v0.0.0-20230206224905-5322a9bff2ec h1:Wc4FfO2RPm4N51mc/QY+S0yQJ9KEPE8UyjWZpeA5s/M=
|
m.cluseau.fr/go v0.0.0-20230206224905-5322a9bff2ec h1:Wc4FfO2RPm4N51mc/QY+S0yQJ9KEPE8UyjWZpeA5s/M=
|
||||||
m.cluseau.fr/go v0.0.0-20230206224905-5322a9bff2ec/go.mod h1:1DYAavEumEsHasA2RbRUExaEodBB2NAYD+IcwmgVkRM=
|
m.cluseau.fr/go v0.0.0-20230206224905-5322a9bff2ec/go.mod h1:1DYAavEumEsHasA2RbRUExaEodBB2NAYD+IcwmgVkRM=
|
||||||
|
m.cluseau.fr/go v0.0.0-20230213110602-74f6da73b5fd h1:GqAR3jXEm6I/Q+ajXG0eoerrUGNuMV3pnuFcicUY7fk=
|
||||||
|
m.cluseau.fr/go v0.0.0-20230213110602-74f6da73b5fd/go.mod h1:1DYAavEumEsHasA2RbRUExaEodBB2NAYD+IcwmgVkRM=
|
||||||
novit.nc/direktil/pkg v0.0.0-20220221171542-fd3ce3a1491b/go.mod h1:zwTVO6U0tXFEaga73megQIBK7yVIKZJVePaIh/UtdfU=
|
novit.nc/direktil/pkg v0.0.0-20220221171542-fd3ce3a1491b/go.mod h1:zwTVO6U0tXFEaga73megQIBK7yVIKZJVePaIh/UtdfU=
|
||||||
novit.tech/direktil/pkg v0.0.0-20220331152412-40403eca850f h1:Ka7zFkP01l4TW9JSmQQzaYVOq0i+qf+JIWAfyoreoSk=
|
novit.tech/direktil/pkg v0.0.0-20220331152412-40403eca850f h1:Ka7zFkP01l4TW9JSmQQzaYVOq0i+qf+JIWAfyoreoSk=
|
||||||
novit.tech/direktil/pkg v0.0.0-20220331152412-40403eca850f/go.mod h1:2Mir5x1eT/e295WeFGzzXa4siunKX4z+rmNPfVsXS0k=
|
novit.tech/direktil/pkg v0.0.0-20220331152412-40403eca850f/go.mod h1:2Mir5x1eT/e295WeFGzzXa4siunKX4z+rmNPfVsXS0k=
|
||||||
|
@ -17,3 +17,21 @@
|
|||||||
max-height: 100pt;
|
max-height: 100pt;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#store-infos {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
border-bottom: dashed 1pt;
|
||||||
|
margin-bottom: 1ex;
|
||||||
|
}
|
||||||
|
#store-infos > * {
|
||||||
|
display: block;
|
||||||
|
font-size: medium;
|
||||||
|
padding: 2pt 1ex;
|
||||||
|
margin: 0 0 0 1ex;
|
||||||
|
}
|
||||||
|
#store-infos > *:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
@ -41,15 +41,15 @@
|
|||||||
<template v-else-if="publicState.Store.New">
|
<template v-else-if="publicState.Store.New">
|
||||||
<p>Store is new.</p>
|
<p>Store is new.</p>
|
||||||
<form @submit="unlockStore" action="/public/unlock-store">
|
<form @submit="unlockStore" action="/public/unlock-store">
|
||||||
<input type="password" v-model="forms.store.pass1" name="passphrase" />
|
<input type="password" v-model="forms.store.pass1" name="passphrase" required />
|
||||||
<input type="password" v-model="forms.store.pass2" />
|
<input type="password" v-model="forms.store.pass2" required />
|
||||||
<input type="submit" value="initialize" :disabled="!forms.store.pass1 || forms.store.pass1 != forms.store.pass2" />
|
<input type="submit" value="initialize" :disabled="!forms.store.pass1 || forms.store.pass1 != forms.store.pass2" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="!publicState.Store.Open">
|
<template v-else-if="!publicState.Store.Open">
|
||||||
<p>Store is not open.</p>
|
<p>Store is not open.</p>
|
||||||
<form @submit="unlockStore" action="/public/unlock-store">
|
<form @submit="unlockStore" action="/public/unlock-store">
|
||||||
<input type="password" name="passphrase" v-model="forms.store.pass1" />
|
<input type="password" name="passphrase" v-model="forms.store.pass1" required />
|
||||||
<input type="submit" value="unlock" :disabled="!forms.store.pass1" />
|
<input type="submit" value="unlock" :disabled="!forms.store.pass1" />
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
@ -58,12 +58,23 @@
|
|||||||
<p v-else>Invalid token</p>
|
<p v-else>Invalid token</p>
|
||||||
|
|
||||||
<form @submit="setToken">
|
<form @submit="setToken">
|
||||||
<input type="password" v-model="forms.setToken" />
|
<input type="password" v-model="forms.setToken" required />
|
||||||
<input type="submit" value="set token"/>
|
<input type="submit" value="set token"/>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
<div id="store-infos">
|
||||||
|
<h2>Store</h2>
|
||||||
|
<a :href="'/public/store.tar?token='+state.Store.DownloadToken" target="_blank">download</a>
|
||||||
|
<form @submit="storeAddKey" action="/store/add-key">
|
||||||
|
<input type="password" v-model="forms.store.pass1" name="passphrase" autocomplete="new-password" required />
|
||||||
|
<input type="password" v-model="forms.store.pass2" autocomplete="new-password" required />
|
||||||
|
<input type="submit" value="add unlock phrase" :disabled="!forms.store.pass1 || forms.store.pass1 != forms.store.pass2" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="state.Clusters" id="clusters">
|
<div v-if="state.Clusters" id="clusters">
|
||||||
|
|
||||||
<h2>Clusters</h2>
|
<h2>Clusters</h2>
|
||||||
|
|
||||||
<div class="sheets">
|
<div class="sheets">
|
||||||
|
@ -60,6 +60,11 @@ createApp({
|
|||||||
this.session.token = this.forms.setToken
|
this.session.token = this.forms.setToken
|
||||||
this.forms.setToken = null
|
this.forms.setToken = null
|
||||||
},
|
},
|
||||||
|
storeAddKey() {
|
||||||
|
this.apiPost('/store/add-key', this.forms.store.pass1, (v) => {
|
||||||
|
this.forms.store = {}
|
||||||
|
})
|
||||||
|
},
|
||||||
unlockStore() {
|
unlockStore() {
|
||||||
this.apiPost('/public/unlock-store', this.forms.store.pass1, (v) => {
|
this.apiPost('/public/unlock-store', this.forms.store.pass1, (v) => {
|
||||||
this.forms.store = {}
|
this.forms.store = {}
|
||||||
|
Loading…
Reference in New Issue
Block a user