mirror of
https://github.com/nkanaev/yarr.git
synced 2025-05-24 00:33:14 +00:00
switch to the standard logger
This commit is contained in:
parent
3539433a9d
commit
cc7bdc5b76
@ -6,7 +6,6 @@
|
||||
|
||||
- fix: scrollTo/scrollBy with smooth behavior instead of scrollTop
|
||||
|
||||
- ref: switch to the standard logger
|
||||
- ref: organize "server" package using KonMari method
|
||||
https://github.com/gin-gonic/gin : nice example of router api
|
||||
|
||||
|
25
src/main.go
25
src/main.go
@ -18,6 +18,9 @@ var Version string = "0.0"
|
||||
var GitHash string = "unknown"
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
var addr, db, authfile, certfile, keyfile string
|
||||
var ver, open bool
|
||||
flag.StringVar(&addr, "addr", "127.0.0.1:7070", "address to run server on")
|
||||
@ -43,28 +46,26 @@ func main() {
|
||||
server.BasePath = strings.TrimSuffix(server.BasePath, "/")
|
||||
}
|
||||
|
||||
logger := log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
configPath, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to get config dir: ", err)
|
||||
log.Fatal("Failed to get config dir: ", err)
|
||||
}
|
||||
|
||||
if db == "" {
|
||||
storagePath := filepath.Join(configPath, "yarr")
|
||||
if err := os.MkdirAll(storagePath, 0755); err != nil {
|
||||
logger.Fatal("Failed to create app config dir: ", err)
|
||||
log.Fatal("Failed to create app config dir: ", err)
|
||||
}
|
||||
db = filepath.Join(storagePath, "storage.db")
|
||||
}
|
||||
|
||||
logger.Printf("using db file %s", db)
|
||||
log.Printf("using db file %s", db)
|
||||
|
||||
var username, password string
|
||||
if authfile != "" {
|
||||
f, err := os.Open(authfile)
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to open auth file: ", err)
|
||||
log.Fatal("Failed to open auth file: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
@ -72,7 +73,7 @@ func main() {
|
||||
line := scanner.Text()
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 2 {
|
||||
logger.Fatalf("Invalid auth: %v (expected `username:password`)", line)
|
||||
log.Fatalf("Invalid auth: %v (expected `username:password`)", line)
|
||||
}
|
||||
username = parts[0]
|
||||
password = parts[1]
|
||||
@ -81,15 +82,15 @@ func main() {
|
||||
}
|
||||
|
||||
if (certfile != "" || keyfile != "") && (certfile == "" || keyfile == "") {
|
||||
logger.Fatalf("Both cert & key files are required")
|
||||
log.Fatalf("Both cert & key files are required")
|
||||
}
|
||||
|
||||
store, err := storage.New(db, logger)
|
||||
store, err := storage.New(db)
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to initialise database: ", err)
|
||||
log.Fatal("Failed to initialise database: ", err)
|
||||
}
|
||||
|
||||
srv := server.New(store, logger, addr)
|
||||
srv := server.New(store, addr)
|
||||
|
||||
if certfile != "" && keyfile != "" {
|
||||
srv.CertFile = certfile
|
||||
@ -101,7 +102,7 @@ func main() {
|
||||
srv.Password = password
|
||||
}
|
||||
|
||||
logger.Printf("starting server at %s", srv.GetAddr())
|
||||
log.Printf("starting server at %s", srv.GetAddr())
|
||||
if open {
|
||||
platform.Open(srv.GetAddr())
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/nkanaev/yarr/src/assets"
|
||||
"html"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@ -94,7 +95,7 @@ func FolderListHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
} else if req.Method == "POST" {
|
||||
var body FolderCreateForm
|
||||
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -120,7 +121,7 @@ func FolderHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "PUT" {
|
||||
var body FolderUpdateForm
|
||||
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -174,14 +175,14 @@ func FeedListHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
} else if req.Method == "POST" {
|
||||
var form FeedCreateForm
|
||||
if err := json.NewDecoder(req.Body).Decode(&form); err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
feed, sources, err := discoverFeed(form.Url)
|
||||
if err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
writeJSON(rw, map[string]string{"status": "notfound"})
|
||||
return
|
||||
}
|
||||
@ -201,7 +202,7 @@ func FeedListHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
db(req).UpdateFeedIcon(storedFeed.Id, icon)
|
||||
}
|
||||
if err != nil {
|
||||
handler(req).log.Printf("Failed to find favicon for %s (%d): %s", storedFeed.FeedLink, storedFeed.Id, err)
|
||||
log.Printf("Failed to find favicon for %s (%d): %s", storedFeed.FeedLink, storedFeed.Id, err)
|
||||
}
|
||||
|
||||
writeJSON(rw, map[string]string{"status": "success"})
|
||||
@ -227,7 +228,7 @@ func FeedHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
body := make(map[string]interface{})
|
||||
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -262,7 +263,7 @@ func ItemHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
var body ItemUpdateForm
|
||||
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -347,12 +348,12 @@ func OPMLImportHandler(rw http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "POST" {
|
||||
file, _, err := req.FormFile("opml")
|
||||
if err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
doc, err := parseOPML(file)
|
||||
if err != nil {
|
||||
handler(req).log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
for _, outline := range doc.Outlines {
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
type Handler struct {
|
||||
Addr string
|
||||
db *storage.Storage
|
||||
log *log.Logger
|
||||
feedQueue chan storage.Feed
|
||||
queueSize *int32
|
||||
refreshRate chan int64
|
||||
@ -27,11 +26,10 @@ type Handler struct {
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
func New(db *storage.Storage, logger *log.Logger, addr string) *Handler {
|
||||
func New(db *storage.Storage, addr string) *Handler {
|
||||
queueSize := int32(0)
|
||||
return &Handler{
|
||||
db: db,
|
||||
log: logger,
|
||||
feedQueue: make(chan storage.Feed, 3000),
|
||||
queueSize: &queueSize,
|
||||
Addr: addr,
|
||||
@ -58,7 +56,7 @@ func (h *Handler) Start() {
|
||||
err = s.ListenAndServe()
|
||||
}
|
||||
if err != http.ErrServerClosed {
|
||||
h.log.Fatal(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +122,7 @@ func (h *Handler) startJobs() {
|
||||
items, err := listItems(feed, h.db)
|
||||
atomic.AddInt32(h.queueSize, -1)
|
||||
if err != nil {
|
||||
h.log.Printf("Failed to fetch %s (%d): %s", feed.FeedLink, feed.Id, err)
|
||||
log.Printf("Failed to fetch %s (%d): %s", feed.FeedLink, feed.Id, err)
|
||||
h.db.SetFeedError(feed.Id, err)
|
||||
continue
|
||||
}
|
||||
@ -136,7 +134,7 @@ func (h *Handler) startJobs() {
|
||||
h.db.UpdateFeedIcon(feed.Id, icon)
|
||||
}
|
||||
if err != nil {
|
||||
h.log.Printf("Failed to search favicon for %s (%s): %s", feed.Link, feed.FeedLink, err)
|
||||
log.Printf("Failed to search favicon for %s (%s): %s", feed.Link, feed.FeedLink, err)
|
||||
}
|
||||
}
|
||||
case <-delTicker.C:
|
||||
@ -190,7 +188,7 @@ func (h Handler) requiresAuth() bool {
|
||||
}
|
||||
|
||||
func (h *Handler) fetchAllFeeds() {
|
||||
h.log.Print("Refreshing all feeds")
|
||||
log.Print("Refreshing all feeds")
|
||||
h.db.ResetFeedErrors()
|
||||
for _, feed := range h.db.ListFeeds() {
|
||||
h.fetchFeed(feed)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"log"
|
||||
"html"
|
||||
"net/url"
|
||||
)
|
||||
@ -60,7 +61,7 @@ func (s *Storage) CreateFeed(title, description, link, feedLink string, folderId
|
||||
func (s *Storage) DeleteFeed(feedId int64) bool {
|
||||
_, err := s.db.Exec(`delete from feeds where id = ?`, feedId)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
@ -89,7 +90,7 @@ func (s *Storage) ListFeeds() []Feed {
|
||||
order by title collate nocase
|
||||
`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
@ -104,7 +105,7 @@ func (s *Storage) ListFeeds() []Feed {
|
||||
&f.HasIcon,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
result = append(result, f)
|
||||
@ -137,7 +138,7 @@ func (s *Storage) GetFeed(id int64) *Feed {
|
||||
|
||||
func (s *Storage) ResetFeedErrors() {
|
||||
if _, err := s.db.Exec(`delete from feed_errors`); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +150,7 @@ func (s *Storage) SetFeedError(feedID int64, lastError error) {
|
||||
feedID, lastError.Error(),
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +159,7 @@ func (s *Storage) GetFeedErrors() map[int64]string {
|
||||
|
||||
rows, err := s.db.Query(`select feed_id, error from feed_errors`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return errors
|
||||
}
|
||||
|
||||
@ -166,7 +167,7 @@ func (s *Storage) GetFeedErrors() map[int64]string {
|
||||
var id int64
|
||||
var error string
|
||||
if err = rows.Scan(&id, &error); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
errors[id] = error
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Folder struct {
|
||||
@ -25,19 +26,19 @@ func (s *Storage) CreateFolder(title string) *Folder {
|
||||
var id int64
|
||||
numrows, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
if numrows == 1 {
|
||||
id, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err = s.db.QueryRow(`select id, is_expanded from folders where title=?`, title).Scan(&id, &expanded)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -47,7 +48,7 @@ func (s *Storage) CreateFolder(title string) *Folder {
|
||||
func (s *Storage) DeleteFolder(folderId int64) bool {
|
||||
_, err := s.db.Exec(`delete from folders where id = ?`, folderId)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
@ -70,14 +71,14 @@ func (s *Storage) ListFolders() []Folder {
|
||||
order by title collate nocase
|
||||
`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
var f Folder
|
||||
err = rows.Scan(&f.Id, &f.Title, &f.IsExpanded)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
result = append(result, f)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -16,7 +17,7 @@ func (s *Storage) ListHTTPStates() map[int64]HTTPState {
|
||||
result := make(map[int64]HTTPState)
|
||||
rows, err := s.db.Query(`select feed_id, last_refreshed, last_modified, etag from http_states`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
@ -28,7 +29,7 @@ func (s *Storage) ListHTTPStates() map[int64]HTTPState {
|
||||
&state.Etag,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
result[state.FeedID] = state
|
||||
@ -67,6 +68,6 @@ func (s *Storage) SetHTTPState(feedID int64, lastModified, etag string) {
|
||||
lastModified, etag,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
xhtml "golang.org/x/net/html"
|
||||
@ -73,7 +74,7 @@ type MarkFilter struct {
|
||||
func (s *Storage) CreateItems(items []Item) bool {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return false
|
||||
}
|
||||
now := time.Now()
|
||||
@ -105,16 +106,16 @@ func (s *Storage) CreateItems(items []Item) bool {
|
||||
item.DateUpdated, now,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
if err = tx.Rollback(); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -175,7 +176,7 @@ func (s *Storage) ListItems(filter ItemFilter, offset, limit int, newestFirst bo
|
||||
`, predicate, order, limit, offset)
|
||||
rows, err := s.db.Query(query, args...)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
@ -196,7 +197,7 @@ func (s *Storage) ListItems(filter ItemFilter, offset, limit int, newestFirst bo
|
||||
&x.PodcastURL,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
result = append(result, x)
|
||||
@ -251,7 +252,7 @@ func (s *Storage) MarkItemsRead(filter MarkFilter) bool {
|
||||
`, READ, predicate, STARRED)
|
||||
_, err := s.db.Exec(query, args...)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
@ -273,7 +274,7 @@ func (s *Storage) FeedStats() []FeedStat {
|
||||
group by feed_id
|
||||
`, UNREAD, STARRED))
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
@ -310,7 +311,7 @@ func (s *Storage) SyncSearch() {
|
||||
where search_rowid is null;
|
||||
`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -327,7 +328,7 @@ func (s *Storage) SyncSearch() {
|
||||
item.Title, HTMLText(item.Description), HTMLText(item.Content),
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
if numrows, err := result.RowsAffected(); err == nil && numrows == 1 {
|
||||
@ -351,7 +352,7 @@ func (s *Storage) DeleteOldItems() {
|
||||
`, STARRED))
|
||||
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
@ -370,16 +371,16 @@ func (s *Storage) DeleteOldItems() {
|
||||
time.Now().Add(-time.Hour*24*90), // 90 days
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
num, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return
|
||||
}
|
||||
if num > 0 {
|
||||
s.log.Printf("Deleted %d old items (%d)", num, feedId)
|
||||
log.Printf("Deleted %d old items (%d)", num, feedId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ var migrations = []func(*sql.Tx)error{
|
||||
|
||||
var maxVersion = int64(len(migrations))
|
||||
|
||||
func migrate(db *sql.DB, log *log.Logger) error {
|
||||
func migrate(db *sql.DB) error {
|
||||
var version int64
|
||||
db.QueryRow("pragma user_version").Scan(&version);
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
package storage
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
)
|
||||
|
||||
func settingsDefaults() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
@ -28,7 +31,7 @@ func (s *Storage) GetSettingsValue(key string) interface{} {
|
||||
}
|
||||
var valDecoded interface{}
|
||||
if err := json.Unmarshal([]byte(val), &valDecoded); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
return valDecoded
|
||||
@ -48,7 +51,7 @@ func (s *Storage) GetSettings() map[string]interface{} {
|
||||
result := settingsDefaults()
|
||||
rows, err := s.db.Query(`select key, val from settings;`)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
@ -58,7 +61,7 @@ func (s *Storage) GetSettings() map[string]interface{} {
|
||||
|
||||
rows.Scan(&key, &val)
|
||||
if err = json.Unmarshal([]byte(val), &valDecoded); err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
result[key] = valDecoded
|
||||
@ -74,7 +77,7 @@ func (s *Storage) UpdateSettings(kv map[string]interface{}) bool {
|
||||
}
|
||||
valEncoded, err := json.Marshal(val)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return false
|
||||
}
|
||||
_, err = s.db.Exec(`
|
||||
@ -83,7 +86,7 @@ func (s *Storage) UpdateSettings(kv map[string]interface{}) bool {
|
||||
key, valEncoded, valEncoded,
|
||||
)
|
||||
if err != nil {
|
||||
s.log.Print(err)
|
||||
log.Print(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -3,15 +3,13 @@ package storage
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
db *sql.DB
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
func New(path string, log *log.Logger) (*Storage, error) {
|
||||
func New(path string) (*Storage, error) {
|
||||
db, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -19,8 +17,8 @@ func New(path string, log *log.Logger) (*Storage, error) {
|
||||
|
||||
db.SetMaxOpenConns(1)
|
||||
|
||||
if err = migrate(db, log); err != nil {
|
||||
if err = migrate(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Storage{db: db, log: log}, nil
|
||||
return &Storage{db: db}, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user