route/internal/database/postgres.go

179 lines
3.5 KiB
Go

// +build ignore
package database
import (
"bytes"
"database/sql"
"log"
"git.xeserv.us/xena/route/internal/database/dmigrations"
"github.com/Xe/uuid"
"github.com/brandur/simplebox"
"github.com/gchaincl/dotsql"
_ "github.com/lib/pq"
"golang.org/x/net/context"
)
type PostgresStorage struct {
ds *dotsql.DotSql
db *sql.DB
sb *simplebox.SimpleBox
//cs *postgresCertificateStorage
rs *postgresRouteStorage
//ts *postgresTokenStorage
}
type postgresCertificateStorage struct {
*PostgresStorage
}
type postgresRouteStorage struct {
*PostgresStorage
}
type postgresTokenStorage struct {
*PostgresStorage
}
// NewPostgresStorage creates a new Storage instance backed by postgres at the
// given URL.
func NewPostgresStorage(url string, key *[32]byte) (Storage, error) {
db, err := sql.Open("postgres", url)
if err != nil {
return nil, err
}
data, err := dmigrations.Asset("postgres.sql")
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(data)
ds, err := dotsql.Load(buf)
if err != nil {
return nil, err
}
for k := range ds.QueryMap() {
log.Printf("preparing %s", k)
stmt, err := ds.Prepare(db, k)
if err != nil {
db.Close()
return nil, err
}
defer stmt.Close()
}
p := &PostgresStorage{
db: db,
ds: ds,
sb: simplebox.NewFromSecretKey(key),
}
//p.cs = &postgresCertificateStorage{p}
p.rs = &postgresRouteStorage{p}
//p.ts = &postgresTokenStorage{p}
return p, nil
}
// Certs gets the certificate storage interface.
func (p *PostgresStorage) Certs() Certs { return nil }
// Routes gets the route storage interface.
func (p *PostgresStorage) Routes() Routes { return p.rs }
// Tokens gets the token storage interface.
func (p *PostgresStorage) Tokens() Tokens { return nil }
// Close cleans up resources for this Storage.
func (p *PostgresStorage) Close() error { return p.db.Close() }
// interface compliance
var (
_ Storage = &PostgresStorage{}
//_ Certs = &postgresCertificateStorage{}
_ Routes = &postgresRouteStorage{}
//_ Tokens = &postgresTokenStorage{}
)
func (p *postgresRouteStorage) getRouteInner(ctx context.Context, arg string, kind string) (Route, error) {
r, err := p.ds.QueryRow(p.db, kind, arg)
if err != nil {
return Route{}, err
}
rt := Route{}
err = (&rt).Scan(r)
if err != nil {
return Route{}, err
}
return rt, nil
}
func (p *postgresRouteStorage) Get(ctx context.Context, id string) (Route, error) {
return p.getRouteInner(ctx, id, "find-one-route-by-id")
}
func (p *postgresRouteStorage) GetHost(ctx context.Context, host string) (Route, error) {
return p.getRouteInner(ctx, host, "find-one-route-by-host")
}
func (p *postgresRouteStorage) GetAll(ctx context.Context, user string) ([]Route, error) {
var result []Route
rows, err := p.ds.Query(p.db, "find-all-routes-for-user", user)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
rt := &Route{}
if err := rt.Scan(rows); err != nil {
return nil, err
}
result = append(result, *rt)
}
if err := rows.Err(); err != nil {
return nil, err
}
return result, nil
}
func (p *postgresRouteStorage) Put(ctx context.Context, r Route) (Route, error) {
if r.ID == "" {
r.ID = uuid.New()
}
_, err := p.ds.Exec(p.db, "insert-route", r.ID, r.Hostname)
if err != nil {
return Route{}, err
}
return p.Get(ctx, r.ID)
}
func (p *postgresRouteStorage) Delete(ctx context.Context, r Route) (Route, error) {
rt, err := p.Get(ctx, r.ID)
if err != nil {
return Route{}, err
}
_, err = p.ds.Exec(p.db, "delete-one-route", rt.ID, rt.Hostname)
if err != nil {
return Route{}, err
}
return rt, nil
}