Add KeyUID to IdentifiedAccountInfo
This field will be used as a deterministic id of multiaccount on status-react side instead of master key address. The reason why it is calculated as sha256 of public key is that this way is already used on keycard side and it will simplify integration. Rename keycardKeyUid to keyUid As long as this field will be present in all multiaccounts from now on it shouldn't be named as keycard specific.
This commit is contained in:
parent
10635d555f
commit
5f6c7008e1
2
Makefile
2
Makefile
|
@ -163,7 +163,7 @@ setup-build: lint-install release-install gomobile-install ##@other Prepare proj
|
|||
setup: setup-build setup-dev tidy ##@other Prepare project for development and building
|
||||
|
||||
generate: ##@other Regenerate assets and other auto-generated stuff
|
||||
go generate ./static ./static/mailserver_db_migrations ./t
|
||||
go generate ./static ./static/mailserver_db_migrations ./t ./multiaccounts/...
|
||||
|
||||
prepare-release: clean-release
|
||||
mkdir -p $(RELEASE_DIR)
|
||||
|
|
|
@ -2,10 +2,11 @@ package generator
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
protocol "github.com/status-im/status-go/protocol/types"
|
||||
statusproto "github.com/status-im/status-go/protocol/types"
|
||||
)
|
||||
|
||||
type account struct {
|
||||
|
@ -14,7 +15,7 @@ type account struct {
|
|||
}
|
||||
|
||||
func (a *account) toAccountInfo() AccountInfo {
|
||||
publicKeyHex := protocol.EncodeHex(crypto.FromECDSAPub(&a.privateKey.PublicKey))
|
||||
publicKeyHex := statusproto.EncodeHex(crypto.FromECDSAPub(&a.privateKey.PublicKey))
|
||||
addressHex := crypto.PubkeyToAddress(a.privateKey.PublicKey).Hex()
|
||||
|
||||
return AccountInfo{
|
||||
|
@ -25,9 +26,12 @@ func (a *account) toAccountInfo() AccountInfo {
|
|||
|
||||
func (a *account) toIdentifiedAccountInfo(id string) IdentifiedAccountInfo {
|
||||
info := a.toAccountInfo()
|
||||
keyUID := sha256.Sum256(crypto.FromECDSAPub(&a.privateKey.PublicKey))
|
||||
keyUIDHex := statusproto.EncodeHex(keyUID[:])
|
||||
return IdentifiedAccountInfo{
|
||||
AccountInfo: info,
|
||||
ID: id,
|
||||
KeyUID: keyUIDHex,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +53,12 @@ type AccountInfo struct {
|
|||
type IdentifiedAccountInfo struct {
|
||||
AccountInfo
|
||||
ID string `json:"id"`
|
||||
// KeyUID is calculated as sha256 of the master public key and used for key
|
||||
// identification. This is the only information available about the master
|
||||
// key stored on a keycard before the card is paired.
|
||||
// KeyUID name is chosen over KeyID in order to make it consistent with
|
||||
// the name already used in Status and Keycard codebases.
|
||||
KeyUID string `json:"keyUid"`
|
||||
}
|
||||
|
||||
// GeneratedAccountInfo contains IdentifiedAccountInfo and the mnemonic of an account.
|
||||
|
|
|
@ -15,7 +15,7 @@ type Account struct {
|
|||
Timestamp int64 `json:"timestamp"`
|
||||
PhotoPath string `json:"photo-path"`
|
||||
KeycardPairing string `json:"keycard-pairing"`
|
||||
KeycardKeyUID string `json:"keycard-key-uid"`
|
||||
KeyUID string `json:"key-uid"`
|
||||
}
|
||||
|
||||
// InitializeDB creates db file at a given path and applies migrations.
|
||||
|
@ -40,7 +40,7 @@ func (db *Database) Close() error {
|
|||
}
|
||||
|
||||
func (db *Database) GetAccounts() ([]Account, error) {
|
||||
rows, err := db.db.Query("SELECT address, name, loginTimestamp, photoPath, keycardPairing, keycardKeyUid from accounts ORDER BY loginTimestamp DESC")
|
||||
rows, err := db.db.Query("SELECT address, name, loginTimestamp, photoPath, keycardPairing, keyUid from accounts ORDER BY loginTimestamp DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func (db *Database) GetAccounts() ([]Account, error) {
|
|||
inthelper := sql.NullInt64{}
|
||||
for rows.Next() {
|
||||
acc := Account{}
|
||||
err = rows.Scan(&acc.Address, &acc.Name, &inthelper, &acc.PhotoPath, &acc.KeycardPairing, &acc.KeycardKeyUID)
|
||||
err = rows.Scan(&acc.Address, &acc.Name, &inthelper, &acc.PhotoPath, &acc.KeycardPairing, &acc.KeyUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ func (db *Database) GetAccounts() ([]Account, error) {
|
|||
}
|
||||
|
||||
func (db *Database) SaveAccount(account Account) error {
|
||||
_, err := db.db.Exec("INSERT OR REPLACE INTO accounts (address, name, photoPath, keycardPairing, keycardKeyUid) VALUES (?, ?, ?, ?, ?)", account.Address, account.Name, account.PhotoPath, account.KeycardPairing, account.KeycardKeyUID)
|
||||
_, err := db.db.Exec("INSERT OR REPLACE INTO accounts (address, name, photoPath, keycardPairing, keyUid) VALUES (?, ?, ?, ?, ?)", account.Address, account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *Database) UpdateAccount(account Account) error {
|
||||
_, err := db.db.Exec("UPDATE accounts SET name = ?, photoPath = ?, keycardPairing = ?, keycardKeyUid = ? WHERE address = ?", account.Name, account.PhotoPath, account.KeycardPairing, account.KeycardKeyUID, account.Address)
|
||||
_, err := db.db.Exec("UPDATE accounts SET name = ?, photoPath = ?, keycardPairing = ?, keyUid = ? WHERE address = ?", account.Name, account.PhotoPath, account.KeycardPairing, account.KeyUID, account.Address)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,68 +1,216 @@
|
|||
// Code generated by go-bindata. DO NOT EDIT.
|
||||
// sources:
|
||||
// 0001_accounts.down.sql (21B)
|
||||
// 0001_accounts.up.sql (177B)
|
||||
// doc.go (74B)
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindata_read(data []byte, name string) ([]byte, error) {
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
gz.Close()
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
var __0001_accounts_down_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x48\x4c\x4e\xce\x2f\xcd\x2b\x29\xb6\xe6\x02\x04\x00\x00\xff\xff\x96\x1e\x13\xa1\x15\x00\x00\x00")
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
digest [sha256.Size]byte
|
||||
}
|
||||
|
||||
func _0001_accounts_down_sql() ([]byte, error) {
|
||||
return bindata_read(
|
||||
__0001_accounts_down_sql,
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var __0001_accountsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x48\x4c\x4e\xce\x2f\xcd\x2b\x29\xb6\xe6\x02\x04\x00\x00\xff\xff\x96\x1e\x13\xa1\x15\x00\x00\x00")
|
||||
|
||||
func _0001_accountsDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__0001_accountsDownSql,
|
||||
"0001_accounts.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
var __0001_accounts_up_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\xcb\x31\x8e\x84\x20\x14\x00\xd0\x9e\x53\xfc\x72\x37\xe1\x06\x5b\xa1\xcb\xae\x44\x47\x0d\x7e\x47\x2d\x7f\x84\x28\x99\x11\x8c\x30\x85\xb7\x9f\xc4\x6e\xda\x97\xbc\x5c\x4b\x81\x12\x50\x64\x95\x04\xf5\x07\x75\x83\x20\x47\xd5\x61\x07\x34\xcf\xe1\xe5\x53\x84\x2f\x46\xc6\x1c\x36\x46\xb8\x0b\x9d\x17\x42\x43\xab\xd5\x4d\xe8\x09\x4a\x39\x71\xe6\x69\xb3\x80\x72\xc4\x2b\xd7\x7d\x55\x71\xf6\x0c\x8b\xf3\xe8\x36\x1b\x13\x6d\x3b\x64\xea\x1f\x54\x8d\x9c\xed\x6b\x48\xa1\xa5\xb4\x5e\x81\xb3\x87\x3d\x67\x3a\x4c\x4b\xee\x70\x7e\xf9\xc4\xd2\x9e\xbd\x33\x97\xb1\x6f\x18\x14\x16\x4d\x8f\xa0\x9b\x41\xfd\xfe\xb0\x77\x00\x00\x00\xff\xff\x96\x21\x96\xe1\xb8\x00\x00\x00")
|
||||
func _0001_accountsDownSql() (*asset, error) {
|
||||
bytes, err := _0001_accountsDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func _0001_accounts_up_sql() ([]byte, error) {
|
||||
return bindata_read(
|
||||
__0001_accounts_up_sql,
|
||||
info := bindataFileInfo{name: "0001_accounts.down.sql", size: 21, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0x61, 0x4c, 0x18, 0xfc, 0xc, 0xdf, 0x5c, 0x1f, 0x5e, 0xd3, 0xbd, 0xfa, 0x12, 0x5e, 0x8d, 0x8d, 0x8b, 0xb9, 0x5f, 0x99, 0x46, 0x63, 0xa5, 0xe3, 0xa6, 0x8a, 0x4, 0xf1, 0x73, 0x8a, 0xe9}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __0001_accountsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x44\xcb\xb1\xae\x82\x30\x14\x80\xe1\xbd\x4f\x71\xc6\x7b\x93\xbe\x81\x53\xc1\x2a\x8d\x08\xa4\x1c\x04\xc6\x13\xda\x40\xa3\xb4\x84\xd6\xc1\xb7\x37\x61\x71\xfc\xff\xe4\xcb\xb5\x14\x28\x01\x45\x56\x4a\x50\x17\xa8\x6a\x04\x39\xa8\x16\x5b\xa0\x69\x0a\x6f\x9f\x22\xfc\x31\x32\x66\xb7\x31\xc2\x43\xe8\xbc\x10\x1a\x1a\xad\xee\x42\x8f\x70\x93\x23\x67\x9e\x56\x0b\x28\x07\x3c\x70\xd5\x95\x25\x67\xaf\x30\x3b\x8f\x6e\xb5\x31\xd1\xba\x41\xa6\xae\xa0\x2a\xe4\x6c\x5b\x42\x0a\x0d\xa5\xe5\x00\x9c\x3d\xed\x67\xa2\xdd\x34\xe4\x76\xe7\xe7\xdf\xec\x9c\x39\x82\xfd\x43\xaf\xb0\xa8\x3b\x04\x5d\xf7\xea\x7c\x62\xdf\x00\x00\x00\xff\xff\xf9\xc7\x34\x46\xb1\x00\x00\x00")
|
||||
|
||||
func _0001_accountsUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__0001_accountsUpSql,
|
||||
"0001_accounts.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
var _doc_go = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||
func _0001_accountsUpSql() (*asset, error) {
|
||||
bytes, err := _0001_accountsUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func doc_go() ([]byte, error) {
|
||||
return bindata_read(
|
||||
_doc_go,
|
||||
info := bindataFileInfo{name: "0001_accounts.up.sql", size: 177, mode: os.FileMode(0644), modTime: time.Unix(1574408465, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0x24, 0xcf, 0x7a, 0x7b, 0xb0, 0xf0, 0x95, 0x3f, 0x45, 0x63, 0x9c, 0xe1, 0xca, 0x8f, 0xe4, 0x1a, 0x83, 0x8b, 0x19, 0x1f, 0x11, 0x98, 0xc4, 0x2b, 0xa5, 0x2f, 0x79, 0x96, 0xc2, 0x8f, 0x75}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_docGo,
|
||||
"doc.go",
|
||||
)
|
||||
}
|
||||
|
||||
func docGo() (*asset, error) {
|
||||
bytes, err := docGoBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1573216280, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
return f()
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||
func AssetString(name string) (string, error) {
|
||||
data, err := Asset(name)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// MustAssetString is like AssetString but panics when Asset would return an
|
||||
// error. It simplifies safe initialization of global variables.
|
||||
func MustAssetString(name string) string {
|
||||
return string(MustAsset(name))
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||
// error if the asset could not be found or the digest could not be loaded.
|
||||
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.digest, nil
|
||||
}
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||
}
|
||||
|
||||
// Digests returns a map of all known files and their checksums.
|
||||
func Digests() (map[string][sha256.Size]byte, error) {
|
||||
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||
for name := range _bindata {
|
||||
a, err := _bindata[name]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp[name] = a.digest
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
|
@ -73,11 +221,14 @@ func AssetNames() []string {
|
|||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() ([]byte, error){
|
||||
"0001_accounts.down.sql": _0001_accounts_down_sql,
|
||||
"0001_accounts.up.sql": _0001_accounts_up_sql,
|
||||
"doc.go": doc_go,
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"0001_accounts.down.sql": _0001_accountsDownSql,
|
||||
|
||||
"0001_accounts.up.sql": _0001_accountsUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
|
@ -87,15 +238,15 @@ var _bindata = map[string]func() ([]byte, error){
|
|||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(canonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
|
@ -107,21 +258,62 @@ func AssetDir(name string) ([]string, error) {
|
|||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for name := range node.Children {
|
||||
rv = append(rv, name)
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type _bintree_t struct {
|
||||
Func func() ([]byte, error)
|
||||
Children map[string]*_bintree_t
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
|
||||
"0001_accounts.down.sql": &_bintree_t{_0001_accounts_down_sql, map[string]*_bintree_t{
|
||||
}},
|
||||
"0001_accounts.up.sql": &_bintree_t{_0001_accounts_up_sql, map[string]*_bintree_t{
|
||||
}},
|
||||
"doc.go": &_bintree_t{doc_go, map[string]*_bintree_t{
|
||||
}},
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"0001_accounts.down.sql": &bintree{_0001_accountsDownSql, map[string]*bintree{}},
|
||||
"0001_accounts.up.sql": &bintree{_0001_accountsUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively.
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ name TEXT NOT NULL,
|
|||
loginTimestamp BIG INT,
|
||||
photoPath TEXT,
|
||||
keycardPairing TEXT,
|
||||
keycardKeyUid TEXT
|
||||
keyUid TEXT
|
||||
) WITHOUT ROWID;
|
||||
|
|
Loading…
Reference in New Issue