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')"