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