package sqlite import ( "database/sql" "strings" "time" "gitlab.com/tozd/go/errors" "go.alanpearce.eu/website/internal/buffer" "go.alanpearce.eu/website/internal/storage" "go.alanpearce.eu/x/log" ) type Reader struct { db *sql.DB log *log.Logger queries struct { getFile *sql.Stmt } } func NewReader(dbPath string, log *log.Logger) (r *Reader, err error) { db, err := openDB(dbPath) if err != nil { return nil, errors.WithMessage(err, "could not open SQLite database") } r = &Reader{ log: log, db: db, } r.queries.getFile, err = r.db.Prepare(` SELECT file.content_type, file.last_modified, file.etag, content.body FROM url INNER JOIN file ON file.url_id = url.id INNER JOIN content ON content.file_id = file.id WHERE url.path = ? AND content.encoding = 'identity' `) if err != nil { return nil, errors.WithMessage(err, "preparing select statement") } return r, nil } func (r *Reader) GetFile(filename string) (*storage.File, error) { file := &storage.File{ Encodings: make(map[string]*buffer.Buffer, 1), } content := []byte{} var unixTime int64 err := r.queries.getFile.QueryRow(filename).Scan( &file.ContentType, &unixTime, &file.Etag, &content, ) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, errors.WithMessage(err, "querying database") } file.LastModified = time.Unix(unixTime, 0) file.Encodings["identity"] = buffer.NewBuffer(content) return file, nil } // TODO write specialised query func (r *Reader) CanonicalisePath(path string) (cPath string, differs bool) { cPath = path if strings.HasSuffix(path, "/index.html") { cPath, differs = strings.CutSuffix(path, "index.html") } else if !strings.HasSuffix(path, "/") { file, err := r.GetFile(path + "/") if err != nil { r.log.Warn("error canonicalising path", "path", path, "error", err) return } if file != nil { cPath, differs = path+"/", true } } return }