github.com/readium/readium-lcp-server@v0.0.0-20240509124024-799e77a0bbd6/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 "github.com/readium/readium-lcp-server/dbutils" 14 ) 15 16 // ErrNotFound signals content not found 17 var ErrNotFound = errors.New("Content not found") 18 19 // Index is an interface 20 type Index interface { 21 Get(id string) (Content, error) 22 Add(c Content) error 23 Update(c Content) error 24 Delete(id string) error 25 List() func() (Content, error) 26 } 27 28 // Content represents an encrypted resource 29 type Content struct { 30 ID string `json:"id"` 31 EncryptionKey []byte `json:"-"` 32 Location string `json:"location"` 33 Length int64 `json:"length"` //not exported in license spec? 34 Sha256 string `json:"sha256"` //not exported in license spec? 35 Type string `json:"type"` 36 } 37 38 type dbIndex struct { 39 db *sql.DB 40 dbGetByID *sql.Stmt 41 dbList *sql.Stmt 42 } 43 44 // Get returns a record by id 45 func (i dbIndex) Get(id string) (Content, error) { 46 row := i.dbGetByID.QueryRow(id) 47 var c Content 48 err := row.Scan(&c.ID, &c.EncryptionKey, &c.Location, &c.Length, &c.Sha256, &c.Type) 49 if err != nil { 50 err = ErrNotFound 51 } 52 return c, err 53 } 54 55 // Add inserts a record 56 func (i dbIndex) Add(c Content) error { 57 driver, _ := config.GetDatabase(config.Config.LcpServer.Database) 58 59 if driver == "postgres" { 60 _, err := i.db.Exec(dbutils.GetParamQuery(config.Config.LcpServer.Database, "INSERT INTO content (id,encryption_key,location,length,sha256,type) VALUES (?, ?::bytea, ?, ?, ?, ?)"), 61 c.ID, c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type) 62 return err 63 64 } else { 65 _, err := i.db.Exec("INSERT INTO content (id,encryption_key,location,length,sha256,type) VALUES (?, ?, ?, ?, ?, ?)", 66 c.ID, c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type) 67 return err 68 } 69 70 } 71 72 // Update updates a record 73 func (i dbIndex) Update(c Content) error { 74 driver, _ := config.GetDatabase(config.Config.LcpServer.Database) 75 76 if driver == "postgres" { 77 _, err := i.db.Exec(dbutils.GetParamQuery(config.Config.LcpServer.Database, "UPDATE content SET encryption_key=?::bytea , location=?, length=?, sha256=?, type=? WHERE id=?"), 78 c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type, c.ID) 79 return err 80 } else { 81 _, err := i.db.Exec("UPDATE content SET encryption_key=? , location=?, length=?, sha256=?, type=? WHERE id=?", 82 c.EncryptionKey, c.Location, c.Length, c.Sha256, c.Type, c.ID) 83 return err 84 } 85 86 } 87 88 // Delete deletes a record 89 func (i dbIndex) Delete(id string) error { 90 _, err := i.db.Exec("DELETE FROM content WHERE id=?", id) 91 return err 92 } 93 94 // List lists rows 95 func (i dbIndex) List() func() (Content, error) { 96 rows, err := i.dbList.Query() 97 if err != nil { 98 return func() (Content, error) { return Content{}, err } 99 } 100 return func() (Content, error) { 101 var c Content 102 var err error 103 if rows.Next() { 104 err = rows.Scan(&c.ID, &c.EncryptionKey, &c.Location, &c.Length, &c.Sha256, &c.Type) 105 } else { 106 rows.Close() 107 err = ErrNotFound 108 } 109 return c, err 110 } 111 } 112 113 // Open opens an SQL database and prepare db statements 114 func Open(db *sql.DB) (i Index, err error) { 115 driver, _ := config.GetDatabase(config.Config.LcpServer.Database) 116 117 // if sqlite, create the content table in the lcp db if it does not exist 118 if driver == "sqlite3" { 119 _, err = db.Exec(tableDef) 120 if err != nil { 121 log.Println("Error creating sqlite content table") 122 return 123 } 124 } 125 126 var dbGetByID *sql.Stmt 127 dbGetByID, err = db.Prepare(dbutils.GetParamQuery(config.Config.LcpServer.Database, "SELECT id,encryption_key,location,length,sha256,type FROM content WHERE id = ?")) 128 if err != nil { 129 return 130 } 131 dbList, err := db.Prepare("SELECT id,encryption_key,location,length,sha256,type FROM content") 132 if err != nil { 133 return 134 } 135 i = dbIndex{db, dbGetByID, dbList} 136 return 137 } 138 139 const tableDef = "CREATE TABLE IF NOT EXISTS content (" + 140 "id varchar(255) PRIMARY KEY," + 141 "encryption_key varchar(64) NOT NULL," + 142 "location text NOT NULL," + 143 "length bigint," + 144 "sha256 varchar(64)," + 145 "\"type\" varchar(255) NOT NULL default 'application/epub+zip')"