2019-09-10 17:52:45 +02:00
|
|
|
package db
|
2019-08-25 21:33:56 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2019-09-10 17:52:45 +02:00
|
|
|
"time"
|
2019-08-25 21:33:56 +02:00
|
|
|
|
2019-09-10 17:52:45 +02:00
|
|
|
"github.com/pkg/errors"
|
2019-08-25 21:33:56 +02:00
|
|
|
bolt "go.etcd.io/bbolt"
|
2019-09-10 17:52:45 +02:00
|
|
|
|
2019-09-15 17:43:09 +02:00
|
|
|
"gitea.hashru.nl/dsprenkels/rushlink/gobmarsh"
|
2019-08-25 21:33:56 +02:00
|
|
|
)
|
|
|
|
|
2019-09-10 17:52:45 +02:00
|
|
|
var DB *bolt.DB
|
|
|
|
|
2019-08-25 21:33:56 +02:00
|
|
|
// The current database version
|
|
|
|
//
|
|
|
|
// If we alter the database format, we bump this number and write a new
|
|
|
|
// database migration in migrateDatabase().
|
|
|
|
const CURRENT_MIGRATE_VERSION = 1
|
|
|
|
|
|
|
|
// Bucket storing everything that is not a bulk value. This includes stuff like
|
|
|
|
// the database version, secret site-wide keys.
|
|
|
|
const BUCKET_CONF = "conf"
|
|
|
|
|
2019-08-31 00:02:01 +02:00
|
|
|
// The main bucket for paste values and URL redirects
|
|
|
|
const BUCKET_PASTES = "pastes"
|
2019-08-25 21:33:56 +02:00
|
|
|
|
|
|
|
// This value stores the current migration version. If this value is less than
|
|
|
|
// CURRENT_MIGRATE_VERSION, the database has to be migrated.
|
|
|
|
const KEY_MIGRATE_VERSION = "migrate_version"
|
|
|
|
|
2019-09-10 17:52:45 +02:00
|
|
|
// Open the bolt database
|
2019-11-08 22:17:05 +01:00
|
|
|
func Open(path string) error {
|
|
|
|
if path == "" {
|
2019-09-19 21:29:25 +02:00
|
|
|
return errors.New("database not set")
|
|
|
|
}
|
|
|
|
|
2019-09-10 17:52:45 +02:00
|
|
|
var err error
|
2019-11-08 22:17:05 +01:00
|
|
|
DB, err = bolt.Open(path, 0666, &bolt.Options{Timeout: 1 * time.Second})
|
2019-09-10 17:52:45 +02:00
|
|
|
if err != nil {
|
2019-11-08 22:17:05 +01:00
|
|
|
return errors.Wrapf(err, "failed to open database at '%v'", path)
|
2019-09-10 17:52:45 +02:00
|
|
|
}
|
|
|
|
return DB.Update(migrateDatabase)
|
|
|
|
}
|
|
|
|
|
2019-09-19 21:29:25 +02:00
|
|
|
// Close the bolt database
|
|
|
|
func Close() error {
|
|
|
|
if DB == nil {
|
|
|
|
panic("no open database")
|
|
|
|
}
|
|
|
|
return DB.Close()
|
|
|
|
}
|
|
|
|
|
2019-08-25 21:33:56 +02:00
|
|
|
// Initialize and migrate the database to the current version
|
|
|
|
func migrateDatabase(tx *bolt.Tx) error {
|
|
|
|
dbVersion, err := dbVersion(tx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Migrate the database to version 1
|
|
|
|
if dbVersion < 1 {
|
|
|
|
log.Println("migrating database to version 1")
|
|
|
|
|
|
|
|
// Create conf bucket
|
|
|
|
_, err := tx.CreateBucket([]byte(BUCKET_CONF))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-08-31 00:02:01 +02:00
|
|
|
// Create paste bucket
|
|
|
|
_, err = tx.CreateBucket([]byte(BUCKET_PASTES))
|
2019-08-25 21:33:56 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the version number
|
|
|
|
if err := setDBVersion(tx, 1); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the current migrate version from the database
|
|
|
|
func dbVersion(tx *bolt.Tx) (int, error) {
|
|
|
|
conf := tx.Bucket([]byte(BUCKET_CONF))
|
|
|
|
if conf == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
dbVersionBytes := conf.Get([]byte(KEY_MIGRATE_VERSION))
|
|
|
|
if dbVersionBytes == nil {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Version was already stored
|
|
|
|
var dbVersion int
|
|
|
|
if err := gobmarsh.Unmarshal(dbVersionBytes, &dbVersion); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if dbVersion == 0 {
|
|
|
|
return 0, fmt.Errorf("database version is invalid (%v)", dbVersion)
|
|
|
|
}
|
|
|
|
if dbVersion > CURRENT_MIGRATE_VERSION {
|
|
|
|
return 0, fmt.Errorf("database version is too recent (%v > %v)", dbVersion, CURRENT_MIGRATE_VERSION)
|
|
|
|
}
|
|
|
|
return dbVersion, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the current migrate version in the database
|
|
|
|
func setDBVersion(tx *bolt.Tx, version int) error {
|
|
|
|
conf, err := tx.CreateBucketIfNotExists([]byte(BUCKET_CONF))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
versionBytes, err := gobmarsh.Marshal(version)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return conf.Put([]byte(KEY_MIGRATE_VERSION), versionBytes)
|
|
|
|
}
|