github.com/readium/readium-lcp-server@v0.0.0-20240101192032-6e95190e99f1/index/index.go (about) 1 // Copyright 2020 Readium Foundation. All rights reserved. 2 // Use of this source code is governed by a BSD-style license 3 // that can be found in the LICENSE file exposed on Github (readium) in the project repository. 4 5 package index 6 7 import ( 8 "database/sql" 9 "errors" 10 "log" 11 12 "github.com/readium/readium-lcp-server/config" 13 ) 14 15 // ErrNotFound signals content not found 16 var ErrNotFound = errors.New("Content not found") 17 18 // Index is an interface 19 type Index interface { 20 Get(id string) (Content, error) 21 Add(c Content) error 22 Update(c Content) error 23 Delete(id string) error 24 List() func() (Content, error) 25 } 26 27 // Content represents an encrypted resource 28 type Content struct { 29 ID string `json:"id"` 30 EncryptionKey []byte `json:"-"` 31 Location string `json:"location"` 32 Length int64 `json:"length"` //not exported in license spec? 33 Sha256 string `json:"sha256"` //not exported in license spec? 34 Type string `json:"type"` 35 } 36 37 type dbIndex struct { 38 db *sql.DB 39 dbGetByID *sql.Stmt 40 dbList *sql.Stmt 41 } 42 43 // Get returns a record by id 44 func (i dbIndex) Get(id string) (Content, error) { 45 row := i.dbGetByID.QueryRow(id) 46 var c Content 47 err := row.Scan(&c.ID, &c.EncryptionKey, &c.Location, &c.Length, &c.Sha256, &c.Type) 48 if err != nil { 49 err = ErrNotFound 50 } 51 return c, err 52 } 53 54 // Add inserts a record 55 func (i dbIndex) Add(c Content) error { 56 _, err := i.db.Exec("INSERT INTO content (id,encryption_key,location,length,sha256,type) VALUES (?, ?, ?, ?, ?, ?)", 57 c.ID, c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type) 58 return err 59 } 60 61 // Update updates a record 62 func (i dbIndex) Update(c Content) error { 63 _, err := i.db.Exec("UPDATE content SET encryption_key=? , location=?, length=?, sha256=?, type=? WHERE id=?", 64 c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type, c.ID) 65 return err 66 } 67 68 // Delete deletes a record 69 func (i dbIndex) Delete(id string) error { 70 _, err := i.db.Exec("DELETE FROM content WHERE id=?", id) 71 return err 72 } 73 74 // List lists rows 75 func (i dbIndex) List() func() (Content, error) { 76 rows, err := i.dbList.Query() 77 if err != nil { 78 return func() (Content, error) { return Content{}, err } 79 } 80 return func() (Content, error) { 81 var c Content 82 var err error 83 if rows.Next() { 84 err = rows.Scan(&c.ID, &c.EncryptionKey, &c.Location, &c.Length, &c.Sha256, &c.Type) 85 } else { 86 rows.Close() 87 err = ErrNotFound 88 } 89 return c, err 90 } 91 } 92 93 // Open opens an SQL database and prepare db statements 94 func Open(db *sql.DB) (i Index, err error) { 95 driver, _ := config.GetDatabase(config.Config.LcpServer.Database) 96 97 // if sqlite, create the content table in the lcp db if it does not exist 98 if driver == "sqlite3" { 99 _, err = db.Exec(tableDef) 100 if err != nil { 101 log.Println("Error creating sqlite content table") 102 return 103 } 104 } 105 106 var dbGetByID *sql.Stmt 107 dbGetByID, err = db.Prepare("SELECT id,encryption_key,location,length,sha256,type FROM content WHERE id = ?") 108 if err != nil { 109 return 110 } 111 dbList, err := db.Prepare("SELECT id,encryption_key,location,length,sha256,type FROM content") 112 if err != nil { 113 return 114 } 115 i = dbIndex{db, dbGetByID, dbList} 116 return 117 } 118 119 const tableDef = "CREATE TABLE IF NOT EXISTS content (" + 120 "id varchar(255) PRIMARY KEY," + 121 "encryption_key varchar(64) NOT NULL," + 122 "location text NOT NULL," + 123 "length bigint," + 124 "sha256 varchar(64)," + 125 "\"type\" varchar(255) NOT NULL default 'application/epub+zip')"