98 lines
1.9 KiB
Go
98 lines
1.9 KiB
Go
package websearch
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
const cacheSchema = `
|
|
CREATE TABLE IF NOT EXISTS responses (
|
|
key TEXT PRIMARY KEY,
|
|
fetched REAL NOT NULL,
|
|
payload TEXT NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS meta (
|
|
key TEXT PRIMARY KEY,
|
|
value REAL NOT NULL
|
|
);
|
|
`
|
|
|
|
type Cache struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func OpenCache(path string) (*Cache, error) {
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
return nil, err
|
|
}
|
|
db, err := sql.Open("sqlite", path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := db.Exec(cacheSchema); err != nil {
|
|
db.Close()
|
|
return nil, err
|
|
}
|
|
return &Cache{db: db}, nil
|
|
}
|
|
|
|
func (c *Cache) Close() error {
|
|
if c == nil || c.db == nil {
|
|
return nil
|
|
}
|
|
return c.db.Close()
|
|
}
|
|
|
|
func (c *Cache) Get(key string, ttl, now float64) (*Payload, error) {
|
|
var fetched float64
|
|
var raw string
|
|
err := c.db.QueryRow("SELECT fetched, payload FROM responses WHERE key = ?", key).Scan(&fetched, &raw)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if now-fetched > ttl {
|
|
return nil, nil
|
|
}
|
|
var p Payload
|
|
if err := json.Unmarshal([]byte(raw), &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return &p, nil
|
|
}
|
|
|
|
func (c *Cache) Put(key string, p Payload, now float64) error {
|
|
raw, err := json.Marshal(p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = c.db.Exec(
|
|
"INSERT OR REPLACE INTO responses (key, fetched, payload) VALUES (?, ?, ?)",
|
|
key, now, string(raw),
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (c *Cache) LastCall() (*float64, error) {
|
|
var v float64
|
|
err := c.db.QueryRow("SELECT value FROM meta WHERE key = 'last_call'").Scan(&v)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &v, nil
|
|
}
|
|
|
|
func (c *Cache) MarkCall(now float64) error {
|
|
_, err := c.db.Exec("INSERT OR REPLACE INTO meta (key, value) VALUES ('last_call', ?)", now)
|
|
return err
|
|
}
|