2019-08-20 15:38:40 +00:00
package multiaccounts
import (
2020-11-24 13:13:46 +00:00
"context"
2019-08-20 15:38:40 +00:00
"database/sql"
2020-11-24 13:13:46 +00:00
"github.com/status-im/status-go/images"
2019-08-20 15:38:40 +00:00
"github.com/status-im/status-go/multiaccounts/migrations"
"github.com/status-im/status-go/sqlite"
)
// Account stores public information about account.
type Account struct {
2020-11-30 19:45:10 +00:00
Name string ` json:"name" `
Timestamp int64 ` json:"timestamp" `
2020-12-01 13:00:51 +00:00
Identicon string ` json:"identicon" `
2020-11-30 19:45:10 +00:00
KeycardPairing string ` json:"keycard-pairing" `
KeyUID string ` json:"key-uid" `
Images [ ] images . IdentityImage ` json:"images" `
2020-11-24 13:13:46 +00:00
}
2020-12-15 15:06:59 +00:00
type MultiAccountMarshaller interface {
ToMultiAccount ( ) * Account
}
2020-11-24 13:13:46 +00:00
type Database struct {
db * sql . DB
2019-08-20 15:38:40 +00:00
}
// InitializeDB creates db file at a given path and applies migrations.
func InitializeDB ( path string ) ( * Database , error ) {
db , err := sqlite . OpenUnecryptedDB ( path )
if err != nil {
return nil , err
}
err = migrations . Migrate ( db )
if err != nil {
return nil , err
}
return & Database { db : db } , nil
}
func ( db * Database ) Close ( ) error {
return db . db . Close ( )
}
2020-12-16 18:17:38 +00:00
func ( db * Database ) GetAccounts ( ) ( rst [ ] Account , err error ) {
2020-12-01 13:00:51 +00:00
rows , err := db . db . Query ( "SELECT a.name, a.loginTimestamp, a.identicon, a.keycardPairing, a.keyUid, ii.name, ii.image_payload, ii.width, ii.height, ii.file_size, ii.resize_target FROM accounts AS a LEFT JOIN identity_images AS ii ON ii.key_uid = a.keyUid ORDER BY loginTimestamp DESC" )
2019-08-20 15:38:40 +00:00
if err != nil {
return nil , err
}
2020-12-16 18:17:38 +00:00
defer func ( ) {
err = rows . Close ( )
} ( )
2020-11-30 19:45:10 +00:00
2019-08-20 15:38:40 +00:00
for rows . Next ( ) {
acc := Account { }
2020-12-01 18:15:53 +00:00
accLoginTimestamp := sql . NullInt64 { }
2020-12-01 13:00:51 +00:00
accIdenticon := sql . NullString { }
2020-11-30 19:45:10 +00:00
ii := & images . IdentityImage { }
iiName := sql . NullString { }
iiWidth := sql . NullInt64 { }
iiHeight := sql . NullInt64 { }
iiFileSize := sql . NullInt64 { }
iiResizeTarget := sql . NullInt64 { }
err = rows . Scan (
& acc . Name ,
& accLoginTimestamp ,
2020-12-01 13:00:51 +00:00
& accIdenticon ,
2020-11-30 19:45:10 +00:00
& acc . KeycardPairing ,
& acc . KeyUID ,
& iiName ,
& ii . Payload ,
& iiWidth ,
& iiHeight ,
& iiFileSize ,
& iiResizeTarget ,
)
2019-08-20 15:38:40 +00:00
if err != nil {
return nil , err
}
2020-11-30 19:45:10 +00:00
acc . Timestamp = accLoginTimestamp . Int64
2020-12-01 13:00:51 +00:00
acc . Identicon = accIdenticon . String
2020-11-30 19:45:10 +00:00
ii . KeyUID = acc . KeyUID
ii . Name = iiName . String
ii . Width = int ( iiWidth . Int64 )
ii . Height = int ( iiHeight . Int64 )
ii . FileSize = int ( iiFileSize . Int64 )
ii . ResizeTarget = int ( iiResizeTarget . Int64 )
if ii . Name == "" && len ( ii . Payload ) == 0 && ii . Width == 0 && ii . Height == 0 && ii . FileSize == 0 && ii . ResizeTarget == 0 {
ii = nil
}
2020-12-01 18:15:53 +00:00
// Last index
li := len ( rst ) - 1
// Don't process nil identity images
2020-11-30 19:45:10 +00:00
if ii != nil {
2020-12-01 18:15:53 +00:00
// attach the identity image to a previously created account if present, check keyUID matches
if len ( rst ) > 0 && rst [ li ] . KeyUID == acc . KeyUID {
rst [ li ] . Images = append ( rst [ li ] . Images , * ii )
// else attach the identity image to the newly created account
2020-11-30 19:45:10 +00:00
} else {
acc . Images = append ( acc . Images , * ii )
}
}
2020-12-01 18:15:53 +00:00
// Append newly created account only if this is the first loop or the keyUID doesn't match
if len ( rst ) == 0 || rst [ li ] . KeyUID != acc . KeyUID {
rst = append ( rst , acc )
}
2020-11-30 19:45:10 +00:00
}
2019-08-20 15:38:40 +00:00
return rst , nil
}
func ( db * Database ) SaveAccount ( account Account ) error {
2020-12-01 13:27:26 +00:00
_ , err := db . db . Exec ( "INSERT OR REPLACE INTO accounts (name, identicon, keycardPairing, keyUid) VALUES (?, ?, ?, ?)" , account . Name , account . Identicon , account . KeycardPairing , account . KeyUID )
2019-08-20 15:38:40 +00:00
return err
}
func ( db * Database ) UpdateAccount ( account Account ) error {
2020-12-01 13:00:51 +00:00
_ , err := db . db . Exec ( "UPDATE accounts SET name = ?, identicon = ?, keycardPairing = ? WHERE keyUid = ?" , account . Name , account . Identicon , account . KeycardPairing , account . KeyUID )
2019-08-20 15:38:40 +00:00
return err
}
2019-12-05 08:00:57 +00:00
func ( db * Database ) UpdateAccountTimestamp ( keyUID string , loginTimestamp int64 ) error {
_ , err := db . db . Exec ( "UPDATE accounts SET loginTimestamp = ? WHERE keyUid = ?" , loginTimestamp , keyUID )
2019-08-20 15:38:40 +00:00
return err
}
2019-12-05 08:00:57 +00:00
func ( db * Database ) DeleteAccount ( keyUID string ) error {
_ , err := db . db . Exec ( "DELETE FROM accounts WHERE keyUid = ?" , keyUID )
2019-08-20 15:38:40 +00:00
return err
}
2020-11-24 13:13:46 +00:00
// Account images
2020-12-16 18:17:38 +00:00
func ( db * Database ) GetIdentityImages ( keyUID string ) ( iis [ ] * images . IdentityImage , err error ) {
2020-11-24 23:16:19 +00:00
rows , err := db . db . Query ( ` SELECT key_uid, name, image_payload, width, height, file_size, resize_target FROM identity_images WHERE key_uid = ? ` , keyUID )
2020-11-24 13:13:46 +00:00
if err != nil {
return nil , err
}
2020-12-16 18:17:38 +00:00
defer func ( ) {
err = rows . Close ( )
} ( )
2020-11-24 13:13:46 +00:00
for rows . Next ( ) {
ii := & images . IdentityImage { }
err = rows . Scan ( & ii . KeyUID , & ii . Name , & ii . Payload , & ii . Width , & ii . Height , & ii . FileSize , & ii . ResizeTarget )
if err != nil {
return nil , err
}
iis = append ( iis , ii )
}
return iis , nil
}
2020-11-24 23:16:19 +00:00
func ( db * Database ) GetIdentityImage ( keyUID , it string ) ( * images . IdentityImage , error ) {
2020-11-24 13:13:46 +00:00
var ii images . IdentityImage
2020-12-15 15:28:05 +00:00
err := db . db . QueryRow ( "SELECT key_uid, name, image_payload, width, height, file_size, resize_target FROM identity_images WHERE key_uid = ? AND name = ?" , keyUID , it ) . Scan ( & ii . KeyUID , & ii . Name , & ii . Payload , & ii . Width , & ii . Height , & ii . FileSize , & ii . ResizeTarget )
if err == sql . ErrNoRows {
return nil , nil
} else if err != nil {
return nil , err
2020-11-24 13:13:46 +00:00
}
return & ii , nil
}
2020-12-16 18:17:38 +00:00
func ( db * Database ) StoreIdentityImages ( keyUID string , iis [ ] * images . IdentityImage ) ( err error ) {
2020-11-24 13:13:46 +00:00
// Because SQL INSERTs are triggered in a loop use a tx to ensure a single call to the DB.
tx , err := db . db . BeginTx ( context . Background ( ) , & sql . TxOptions { } )
if err != nil {
return err
}
defer func ( ) {
if err == nil {
err = tx . Commit ( )
return
}
// don't shadow original error
_ = tx . Rollback ( )
} ( )
for _ , ii := range iis {
if ii == nil {
continue
}
2020-11-30 19:45:10 +00:00
ii . KeyUID = keyUID
2020-11-24 13:13:46 +00:00
_ , err := tx . Exec (
"INSERT INTO identity_images (key_uid, name, image_payload, width, height, file_size, resize_target) VALUES (?, ?, ?, ?, ?, ?, ?)" ,
2020-11-30 19:45:10 +00:00
ii . KeyUID ,
2020-11-24 13:13:46 +00:00
ii . Name ,
ii . Payload ,
ii . Width ,
ii . Height ,
ii . FileSize ,
ii . ResizeTarget ,
)
if err != nil {
return err
}
}
return nil
}
2020-11-24 23:16:19 +00:00
func ( db * Database ) DeleteIdentityImage ( keyUID string ) error {
_ , err := db . db . Exec ( ` DELETE FROM identity_images WHERE key_uid = ? ` , keyUID )
2020-11-24 13:13:46 +00:00
return err
}