WIP: stuff for saving
This commit is contained in:
parent
f135020914
commit
7db71b94b1
11
again.go
11
again.go
|
@ -7,9 +7,16 @@ import (
|
||||||
webhooks "git.rootprojects.org/root/go-again/webhooks"
|
webhooks "git.rootprojects.org/root/go-again/webhooks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Webhook webhooks.Webhook
|
||||||
|
|
||||||
type Schedule struct {
|
type Schedule struct {
|
||||||
NextRunAt time.Time
|
ID string `json:"id" db:"id"`
|
||||||
Webhooks []webhooks.Webhook
|
AccessID string `json:"-" 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"`
|
||||||
|
Webhooks []Webhook `json:"webhooks" db"webhooks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://yourbasic.org/golang/time-change-convert-location-timezone/
|
// https://yourbasic.org/golang/time-change-convert-location-timezone/
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -85,7 +86,8 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScheduleDB interface {
|
type ScheduleDB interface {
|
||||||
List() ([]again.Schedule, error)
|
List(string) ([]*again.Schedule, error)
|
||||||
|
Set(again.Schedule) (*again.Schedule, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type scheduler struct {
|
type scheduler struct {
|
||||||
|
@ -93,12 +95,17 @@ type scheduler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) {
|
func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// note: no go-routines reading body in handlers to follow
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
token := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
||||||
if "" == token {
|
if 32 != len(token) {
|
||||||
http.Error(w, "Authorization Header did not contain a valid token", http.StatusForbidden)
|
http.Error(w, "Authorization Header did not contain a valid token", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(ctx, "token", token)
|
||||||
|
r = r.WithContext(ctx)
|
||||||
|
|
||||||
fmt.Println("whatever", r.Method, r.URL)
|
fmt.Println("whatever", r.Method, r.URL)
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
|
@ -106,7 +113,7 @@ func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) {
|
||||||
s.List(w, r)
|
s.List(w, r)
|
||||||
return
|
return
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
http.Error(w, "Not Implemented", http.StatusNotImplemented)
|
s.Create(w, r)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
http.Error(w, "Not Implemented", http.StatusNotImplemented)
|
http.Error(w, "Not Implemented", http.StatusNotImplemented)
|
||||||
|
@ -115,7 +122,8 @@ func (s *scheduler) Handle(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scheduler) List(w http.ResponseWriter, r *http.Request) {
|
func (s *scheduler) List(w http.ResponseWriter, r *http.Request) {
|
||||||
schedules, err := s.DB.List()
|
accessID := r.Context().Value("token").(string)
|
||||||
|
schedules, err := s.DB.List(accessID)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -127,3 +135,27 @@ func (s *scheduler) List(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
w.Write(buf)
|
w.Write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *scheduler) Create(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO validate user
|
||||||
|
accessID := r.Context().Value("token").(string)
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
sched := &again.Schedule{}
|
||||||
|
err := decoder.Decode(s)
|
||||||
|
if nil != err {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO validate and modify
|
||||||
|
sched.AccessID = accessID
|
||||||
|
s.DB.Set(*sched)
|
||||||
|
|
||||||
|
buf, err := json.Marshal(sched)
|
||||||
|
if nil != err {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Write(buf)
|
||||||
|
}
|
||||||
|
|
|
@ -6,18 +6,19 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
again "git.rootprojects.org/root/go-again"
|
again "git.rootprojects.org/root/go-again"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONDB struct {
|
type JSONDB struct {
|
||||||
dburl string
|
dburl string
|
||||||
file *os.File
|
path string
|
||||||
json *dbjson
|
json *dbjson
|
||||||
}
|
}
|
||||||
|
|
||||||
type dbjson struct {
|
type dbjson struct {
|
||||||
Schedules []again.Schedule `json:"schedules"`
|
Schedules []Schedule `json:"schedules"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Connect(dburl string) (*JSONDB, error) {
|
func Connect(dburl string) (*JSONDB, error) {
|
||||||
|
@ -50,9 +51,11 @@ func Connect(dburl string) (*JSONDB, error) {
|
||||||
stat, err := f.Stat()
|
stat, err := f.Stat()
|
||||||
if 0 == stat.Size() {
|
if 0 == stat.Size() {
|
||||||
_, err := f.Write([]byte(`{"schedules":[]}`))
|
_, err := f.Write([]byte(`{"schedules":[]}`))
|
||||||
|
f.Close()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700)
|
f, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0700)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -62,17 +65,102 @@ func Connect(dburl string) (*JSONDB, error) {
|
||||||
decoder := json.NewDecoder(f)
|
decoder := json.NewDecoder(f)
|
||||||
db := &dbjson{}
|
db := &dbjson{}
|
||||||
err = decoder.Decode(db)
|
err = decoder.Decode(db)
|
||||||
|
f.Close()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return nil, fmt.Errorf("Couldn't parse %q as JSON: %s", path, err)
|
return nil, fmt.Errorf("Couldn't parse %q as JSON: %s", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &JSONDB{
|
return &JSONDB{
|
||||||
dburl: dburl,
|
dburl: dburl,
|
||||||
file: f,
|
path: path,
|
||||||
json: db,
|
json: db,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *JSONDB) List() ([]again.Schedule, error) {
|
// A copy of again.Schedule, but with access_id json-able
|
||||||
return db.json.Schedules, nil
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,16 +87,22 @@
|
||||||
function newSchedule() {
|
function newSchedule() {
|
||||||
var $hook = $('.js-schedule');
|
var $hook = $('.js-schedule');
|
||||||
//var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value;
|
//var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value;
|
||||||
var hook = {
|
var schedule = {
|
||||||
|
schedule: {
|
||||||
date: $('.js-date', $hook).value,
|
date: $('.js-date', $hook).value,
|
||||||
time: $('.js-time', $hook).value,
|
time: $('.js-time', $hook).value,
|
||||||
tz: $('.js-tz', $hook).value,
|
tz: $('.js-tz', $hook).value
|
||||||
|
},
|
||||||
|
webhooks: []
|
||||||
|
};
|
||||||
|
var hook = {
|
||||||
comment: $('.js-comment', $hook).value,
|
comment: $('.js-comment', $hook).value,
|
||||||
method: $('.js-method', $hook).value,
|
method: $('.js-method', $hook).value,
|
||||||
url: $('.js-url', $hook).value,
|
url: $('.js-url', $hook).value,
|
||||||
headers: {}
|
headers: {}
|
||||||
};
|
};
|
||||||
console.log('schedule:', hook);
|
schedule.webhooks.push(hook);
|
||||||
|
console.log('schedule:', schedule);
|
||||||
$$('.js-header', $hook).forEach(function($head) {
|
$$('.js-header', $hook).forEach(function($head) {
|
||||||
var key = $('.js-key', $head).value;
|
var key = $('.js-key', $head).value;
|
||||||
var val = $('.js-value', $head).value;
|
var val = $('.js-value', $head).value;
|
||||||
|
@ -114,7 +120,7 @@
|
||||||
Authorization: getToken(),
|
Authorization: getToken(),
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(hook),
|
body: JSON.stringify(schedule),
|
||||||
cors: true
|
cors: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue