package jsondb import ( "encoding/json" "fmt" "net/url" "os" "strings" "time" again "git.rootprojects.org/root/go-again" ) type JSONDB struct { dburl string path string json *dbjson } type dbjson struct { Schedules []Schedule `json:"schedules"` } func Connect(dburl string) (*JSONDB, error) { u, err := url.Parse(dburl) if nil != err { return nil, err } // json:/abspath/to/db.json fmt.Println("url.Opaque:", u.Opaque) // json:///abspath/to/db.json fmt.Println("url.Path:", u.Path) fmt.Println(u) path := u.Opaque if "" == path { path = u.Path if "" == path { // json:relpath/to/db.json // json://relpath/to/db.json path = strings.TrimSuffix(u.Host+"/"+u.Path, "/") } } f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700) if nil != err { return nil, fmt.Errorf("Couldn't open %q: %s", path, err) } stat, err := f.Stat() if 0 == stat.Size() { _, err := f.Write([]byte(`{"schedules":[]}`)) f.Close() if nil != err { return nil, err } f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700) if nil != err { return nil, err } } decoder := json.NewDecoder(f) db := &dbjson{} err = decoder.Decode(db) f.Close() if nil != err { return nil, fmt.Errorf("Couldn't parse %q as JSON: %s", path, err) } return &JSONDB{ dburl: dburl, path: path, json: db, }, nil } // A copy of again.Schedule, but with access_id json-able type Schedule struct { ID string `json:"id" db:"id"` AccessID string `json:"access_id" db:"access_id"` Date string `json:"date" db:"date"` Time string `json:"time" db:"time"` TZ string `json:"tz" db:"tz"` NextRunAt time.Time `json:"next_run_at" db:"next_run_at"` Disabled bool `json:"disabled" db:"disabled"` Webhooks []again.Webhook `json:"webhooks" db"webhooks"` } func (db *JSONDB) List(accessID string) ([]*again.Schedule, error) { schedules := []*again.Schedule{} for i := range db.json.Schedules { s := db.json.Schedules[i] if !s.Disabled && accessID == s.AccessID { schedules = append(schedules, &again.Schedule{ ID: s.ID, AccessID: s.AccessID, Date: s.Date, Time: s.Time, TZ: s.TZ, NextRunAt: s.NextRunAt, Webhooks: s.Webhooks, }) } } return schedules, nil } func (db *JSONDB) get(id string) *Schedule { for i := range db.json.Schedules { schedule := db.json.Schedules[i] if id == schedule.AccessID { return &schedule } } return nil } func (db *JSONDB) Set(s again.Schedule) (*again.Schedule, error) { newSchedules := []Schedule{} for i := range db.json.Schedules { old := db.json.Schedules[i] if s.ID == old.AccessID { continue } newSchedules = append(newSchedules, old) } schedule := Schedule{ ID: s.ID, AccessID: s.AccessID, Date: s.Date, Time: s.Time, TZ: s.TZ, NextRunAt: s.NextRunAt, Webhooks: s.Webhooks, } newSchedules = append(newSchedules, schedule) err := db.save(s.AccessID) if nil != err { return nil, err } return &s, nil } func (db *JSONDB) save(accessID string) error { // TODO per-user files (w/ mutex lock or channel on open and write) f, err := os.OpenFile(db.path, os.O_RDWR|os.O_CREATE, 0700) if nil != err { return err } encoder := json.NewEncoder(f) err = encoder.Encode(db.json.Schedules) f.Close() if nil != err { return err } return nil }