github.com/status-im/status-go@v1.1.0/services/browsers/database.go (about)

     1  package browsers
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  
     7  	"github.com/mat/besticon/besticon"
     8  
     9  	"github.com/ethereum/go-ethereum/log"
    10  )
    11  
    12  // Database sql wrapper for operations with browser objects.
    13  type Database struct {
    14  	db *sql.DB
    15  }
    16  
    17  // Close closes database.
    18  func (db Database) Close() error {
    19  	return db.db.Close()
    20  }
    21  
    22  func NewDB(db *sql.DB) *Database {
    23  	return &Database{db: db}
    24  }
    25  
    26  type BookmarksType string
    27  
    28  type Bookmark struct {
    29  	URL       string `json:"url"`
    30  	Name      string `json:"name"`
    31  	ImageURL  string `json:"imageUrl"`
    32  	Removed   bool   `json:"removed"`
    33  	Clock     uint64 `json:"-"` //used to sync
    34  	DeletedAt uint64 `json:"deletedAt,omitempty"`
    35  }
    36  type Browser struct {
    37  	ID           string   `json:"browser-id"`
    38  	Name         string   `json:"name"`
    39  	Timestamp    uint64   `json:"timestamp"`
    40  	Dapp         bool     `json:"dapp?"`
    41  	HistoryIndex int      `json:"history-index"`
    42  	History      []string `json:"history,omitempty"`
    43  }
    44  
    45  func (db *Database) GetBookmarks() ([]*Bookmark, error) {
    46  	rows, err := db.db.Query(`SELECT url, name, image_url, removed, deleted_at FROM bookmarks`)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	defer rows.Close()
    51  
    52  	var rst []*Bookmark
    53  	for rows.Next() {
    54  		bookmark := &Bookmark{}
    55  		err := rows.Scan(&bookmark.URL, &bookmark.Name, &bookmark.ImageURL, &bookmark.Removed, &bookmark.DeletedAt)
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  
    60  		rst = append(rst, bookmark)
    61  	}
    62  
    63  	return rst, nil
    64  }
    65  
    66  func (db *Database) StoreBookmark(bookmark Bookmark) (Bookmark, error) {
    67  	insert, err := db.db.Prepare("INSERT OR REPLACE INTO bookmarks (url, name, image_url, removed, clock, deleted_at) VALUES (?, ?, ?, ?, ?, ?)")
    68  
    69  	if err != nil {
    70  		return bookmark, err
    71  	}
    72  
    73  	// Get the right icon
    74  	finder := besticon.IconFinder{}
    75  	icons, iconError := finder.FetchIcons(bookmark.URL)
    76  
    77  	if iconError == nil && len(icons) > 0 {
    78  		icon := finder.IconInSizeRange(besticon.SizeRange{Min: 48, Perfect: 48, Max: 100})
    79  		if icon != nil {
    80  			bookmark.ImageURL = icon.URL
    81  		} else {
    82  			bookmark.ImageURL = icons[0].URL
    83  		}
    84  	} else {
    85  		log.Error("error getting the bookmark icon", "iconError", iconError)
    86  	}
    87  
    88  	_, err = insert.Exec(bookmark.URL, bookmark.Name, bookmark.ImageURL, bookmark.Removed, bookmark.Clock, bookmark.DeletedAt)
    89  	return bookmark, err
    90  }
    91  
    92  func (db *Database) StoreBookmarkWithoutFetchIcon(bookmark *Bookmark, tx *sql.Tx) (err error) {
    93  	if tx == nil {
    94  		tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
    95  		if err != nil {
    96  			return err
    97  		}
    98  		defer func() {
    99  			if err == nil {
   100  				err = tx.Commit()
   101  				return
   102  			}
   103  			// don't shadow original error
   104  			_ = tx.Rollback()
   105  		}()
   106  	}
   107  
   108  	insert, err := tx.Prepare("INSERT OR REPLACE INTO bookmarks (url, name, image_url, removed, clock, deleted_at) VALUES (?, ?, ?, ?, ?, ?)")
   109  
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	defer insert.Close()
   115  
   116  	_, err = insert.Exec(bookmark.URL, bookmark.Name, bookmark.ImageURL, bookmark.Removed, bookmark.Clock, bookmark.DeletedAt)
   117  	return err
   118  }
   119  
   120  func (db *Database) StoreSyncBookmarks(bookmarks []*Bookmark) ([]*Bookmark, error) {
   121  	tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	defer func() {
   127  		if err == nil {
   128  			err = tx.Commit()
   129  			return
   130  		}
   131  		// don't shadow original error
   132  		_ = tx.Rollback()
   133  	}()
   134  
   135  	var storedBookmarks []*Bookmark
   136  	for _, bookmark := range bookmarks {
   137  		shouldSync, err := db.shouldSyncBookmark(bookmark, tx)
   138  		if err != nil {
   139  			return storedBookmarks, err
   140  		}
   141  		if shouldSync {
   142  			err := db.StoreBookmarkWithoutFetchIcon(bookmark, tx)
   143  			if err != nil {
   144  				return storedBookmarks, err
   145  			}
   146  			storedBookmarks = append(storedBookmarks, bookmark)
   147  		}
   148  	}
   149  	return storedBookmarks, nil
   150  }
   151  
   152  func (db *Database) shouldSyncBookmark(bookmark *Bookmark, tx *sql.Tx) (shouldSync bool, err error) {
   153  	if tx == nil {
   154  		tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
   155  		if err != nil {
   156  			return false, err
   157  		}
   158  		defer func() {
   159  			if err == nil {
   160  				err = tx.Commit()
   161  				return
   162  			}
   163  			// don't shadow original error
   164  			_ = tx.Rollback()
   165  		}()
   166  	}
   167  	qr := tx.QueryRow(`SELECT 1 FROM bookmarks WHERE url = ? AND clock > ?`, bookmark.URL, bookmark.Clock)
   168  	var result int
   169  	err = qr.Scan(&result)
   170  	switch err {
   171  	case sql.ErrNoRows:
   172  		// Query does not match, therefore synced_at value is not older than the new clock value or id was not found
   173  		return true, nil
   174  	case nil:
   175  		// Error is nil, therefore query matched and synced_at is older than the new clock
   176  		return false, nil
   177  	default:
   178  		// Error is not nil and is not sql.ErrNoRows, therefore pass out the error
   179  		return false, err
   180  	}
   181  }
   182  
   183  func (db *Database) UpdateBookmark(originalURL string, bookmark Bookmark) error {
   184  	insert, err := db.db.Prepare("UPDATE bookmarks SET url = ?, name = ?, image_url = ?, removed = ?, clock = ?, deleted_at = ? WHERE url = ?")
   185  	if err != nil {
   186  		return err
   187  	}
   188  	_, err = insert.Exec(bookmark.URL, bookmark.Name, bookmark.ImageURL, bookmark.Removed, bookmark.Clock, bookmark.DeletedAt, originalURL)
   189  	return err
   190  }
   191  
   192  func (db *Database) DeleteBookmark(url string) error {
   193  	_, err := db.db.Exec(`DELETE FROM bookmarks WHERE url = ?`, url)
   194  	return err
   195  }
   196  
   197  func (db *Database) RemoveBookmark(url string) error {
   198  	_, err := db.db.Exec(`UPDATE bookmarks SET removed = 1 WHERE url = ?`, url)
   199  	return err
   200  }