wip can send event

This commit is contained in:
AJ ONeal 2019-06-22 17:11:14 -06:00
parent 0c6003a894
commit f135020914
6 changed files with 188 additions and 166 deletions

View File

@ -3,10 +3,13 @@ package again
import ( import (
"fmt" "fmt"
"time" "time"
webhooks "git.rootprojects.org/root/go-again/webhooks"
) )
type Schedule struct { type Schedule struct {
NextRunAt time.Time NextRunAt time.Time
Webhooks []webhooks.Webhook
} }
// https://yourbasic.org/golang/time-change-convert-location-timezone/ // https://yourbasic.org/golang/time-change-convert-location-timezone/

View File

@ -75,7 +75,7 @@ func main() {
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
} }
//mux.Handle("/api/", http.HandlerFunc(handleFunc)) //mux.Handle("/api/", http.HandlerFunc(handleFunc))
mux.HandleFunc("/api/schedules", s.Handle) mux.HandleFunc("/api/v0/schedules", s.Handle)
// TODO Filebox FS // TODO Filebox FS
mux.Handle("/", http.FileServer(http.Dir("./public"))) mux.Handle("/", http.FileServer(http.Dir("./public")))

8
public/.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 120,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": true
}

View File

@ -5,7 +5,7 @@
var $ = window.$; var $ = window.$;
var $$ = window.$$; var $$ = window.$$;
var state = {}; var state = { account: { schedules: [] } };
var $grantTpl; var $grantTpl;
var $devTpl; var $devTpl;
@ -14,24 +14,13 @@
var $webhookTpl; var $webhookTpl;
var $webhookHeaderTpl; var $webhookHeaderTpl;
// TODO add offset based on date selected (i.e. MDT -0500) function pad(i) {
var tzdb = [ i = String(i);
'America/New_York', while (i.length < 2) {
'America/Chicago', i = '0' + i;
'America/Denver', }
'America/Phoenix', return i;
'America/Los_Angeles', }
'America/Sao_Paulo',
'Europe/London',
'Europe/Berlin',
'Europe/Moscow',
'Asia/Dubai',
'Asia/Kolkata',
'Asia/Hong_Kong',
'Asia/Tokyo',
'Pacific/Auckland',
'Australia/Sydney'
];
function run() { function run() {
$headerTpl = $('.js-new-webhook .js-header').outerHTML; $headerTpl = $('.js-new-webhook .js-header').outerHTML;
@ -44,6 +33,14 @@
// after blanking all inner templates // after blanking all inner templates
$devTpl = $('.js-schedule').outerHTML; $devTpl = $('.js-schedule').outerHTML;
// Pick a date and time on an even number
// between 10 and 15 minutes in the future
var d = new Date(Date.now() + 10 * 60 * 1000);
var minutes = d.getMinutes() + (5 - (d.getMinutes() % 5)) - d.getMinutes();
d = new Date(d.valueOf() + minutes * 60 * 1000);
$('.js-date').value = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
$('.js-time').value = pad(d.getHours()) + ':' + pad(d.getMinutes());
console.log('hello'); console.log('hello');
$('body').addEventListener('click', function(ev) { $('body').addEventListener('click', function(ev) {
@ -51,15 +48,9 @@
newWebhookHeader(ev.target); newWebhookHeader(ev.target);
} else if (ev.target.matches('.js-rm-header')) { } else if (ev.target.matches('.js-rm-header')) {
rmWebhookHeader(ev.target); rmWebhookHeader(ev.target);
} else if ( } else if (ev.target.matches('.js-delete') && ev.target.closest('.js-grant')) {
ev.target.matches('.js-delete') &&
ev.target.closest('.js-grant')
) {
deleteGrant(ev.target.closest('.js-grant')); deleteGrant(ev.target.closest('.js-grant'));
} else if ( } else if (ev.target.matches('.js-delete') && ev.target.closest('.js-webhook')) {
ev.target.matches('.js-delete') &&
ev.target.closest('.js-webhook')
) {
deleteWebhook(ev.target.closest('.js-webhook')); deleteWebhook(ev.target.closest('.js-webhook'));
} else { } else {
return; return;
@ -68,26 +59,44 @@
ev.stopPropagation(); ev.stopPropagation();
}); });
$('body').addEventListener('change', function(ev) {
var $hook = ev.target.closest('.js-new-webhook');
if (ev.target.matches('.js-url') && $hook) {
if (!$('.js-comment', $hook).value) {
$('.js-comment', $hook).value = ev.target.value.replace(/https:\/\//, '').replace(/\/.*/, '');
}
}
});
$('body').addEventListener('submit', function(ev) { $('body').addEventListener('submit', function(ev) {
if (ev.target.matches('.js-new-schedule')) { if (ev.target.matches('.js-new-schedule')) {
newSchedule(ev.target); newSchedule(ev.target);
} else if (ev.target.matches('.js-schedules-list')) {
doLogin();
} else if (ev.target.matches('.js-schedules-new')) {
scheduleTask();
} else { } else {
return; return;
} }
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
}); });
} }
function newSchedule() { function newSchedule() {
var $hook = $('.js-new-webhook'); 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 hook = {
date: $('.js-date', $hook).value,
time: $('.js-time', $hook).value,
tz: $('.js-tz', $hook).value,
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);
$$('.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;
@ -102,7 +111,7 @@
method: 'POST', method: 'POST',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
Authorization: JSON.parse(localStorage.getItem('session')).access_token, Authorization: getToken(),
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify(hook), body: JSON.stringify(hook),
@ -120,22 +129,15 @@
return; return;
*/ */
window window.fetch('/api/v0/schedules', opts).then(function(resp) {
.fetch('/api/iot/devices/' + deviceId + '/webhooks', opts)
.then(function(resp) {
return resp return resp
.json() .json()
.then(function(data) { .then(function(data) {
if (!data.webhook) { if (!data.schedule) {
throw new Error('something bad happened'); throw new Error('something bad happened');
return;
} }
state.account.devices state.account.schedules.webhooks.push(resp.data.schedule);
.filter(function(d) {
return d.accessToken == deviceId;
})[0]
.webhooks.push(resp.data.webhook);
displayAccount(state.account); displayAccount(state.account);
}) })
@ -168,13 +170,11 @@
method: 'DELETE', method: 'DELETE',
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
Authorization: JSON.parse(localStorage.getItem('session')).access_token Authorization: getToken()
}, },
cors: true cors: true
}; };
window window.fetch('/api/iot/devices/' + deviceId + '/webhooks/' + id, opts).then(function(resp) {
.fetch('/api/iot/devices/' + deviceId + '/webhooks/' + id, opts)
.then(function(resp) {
return resp.json().then(function(result) { return resp.json().then(function(result) {
if (!result.webhook) { if (!result.webhook) {
console.error(result); console.error(result);
@ -232,11 +232,7 @@
$('.js-comment', $grant).innerText = g.comment; $('.js-comment', $grant).innerText = g.comment;
$('.js-token', $grant).innerText = g.token; $('.js-token', $grant).innerText = g.token;
//TODO Math.floor(Date.now() / 1000); //TODO Math.floor(Date.now() / 1000);
var url = var url = 'https://test.therootcompany.com/api/v1/devices/' + d.accessToken + '/updates?since=' + 0;
'https://test.therootcompany.com/api/v1/devices/' +
d.accessToken +
'/updates?since=' +
0;
$('.js-example-curl .js-example-url', $grant).innerText = url; $('.js-example-curl .js-example-url', $grant).innerText = url;
$('.js-example-curl .js-example-token', $grant).innerText = g.token; $('.js-example-curl .js-example-token', $grant).innerText = g.token;
$('.js-example-js .js-example-url', $grant).innerText = url; $('.js-example-js .js-example-url', $grant).innerText = url;
@ -258,10 +254,11 @@
return resp.json().then(function(tzdb) { return resp.json().then(function(tzdb) {
console.info('[tzdb] received'); console.info('[tzdb] received');
var tz = Intl.DateTimeFormat().resolvedOptions().timeZone; var tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
var options = $$('.js-schedule-tz option'); var options = $$('.js-tz option');
var valOpt = options[0].outerHTML; // UTC var valOpt = options[0].outerHTML; // UTC
var spaceOpt = options[1].outerHTML; // ---- var spaceOpt = options[1].outerHTML; // ----
var innerHTML = $('.js-schedule-tz').innerHTML; var innerHTML = $('.js-tz').innerHTML;
/*
innerHTML = innerHTML =
'<option selected value="' + '<option selected value="' +
tz + tz +
@ -270,8 +267,9 @@
'</option>' + '</option>' +
spaceOpt + spaceOpt +
innerHTML.replace(/>UTC/, '>&nbsp;&nbsp;&nbsp;&nbsp;UTC'); innerHTML.replace(/>UTC/, '>&nbsp;&nbsp;&nbsp;&nbsp;UTC');
//$('.js-schedule-tz').innerHTML += spaceOpt; */
//$('.js-schedule-tz').innerHTML += valOpt.replace(/UTC/g, 'custom'); //$('.js-tz').innerHTML += spaceOpt;
//$('.js-tz').innerHTML += valOpt.replace(/UTC/g, 'custom');
Object.keys(tzdb) Object.keys(tzdb)
.sort() .sort()
.forEach(function(k) { .forEach(function(k) {
@ -287,15 +285,68 @@
areas.forEach(function(_tz) { areas.forEach(function(_tz) {
if (tz !== _tz) { if (tz !== _tz) {
innerHTML += valOpt.replace(/UTC/g, _tz); innerHTML += valOpt.replace(/UTC/g, _tz);
} else {
innerHTML += '<option selected value="' + tz + '">' + tz + '</option>';
} }
}); });
innerHTML += '</optgroup>'; innerHTML += '</optgroup>';
}); });
$('.js-schedule-tz').innerHTML = innerHTML; $('.js-tz').innerHTML = innerHTML;
console.info('[tzdb] loaded'); console.info('[tzdb] loaded');
run(); run();
}); });
}); });
var allSchedules = [];
function getToken() {
return JSON.parse(localStorage.getItem('session')).access_token;
}
function doLogin() {
localStorage.setItem(
'session',
JSON.stringify({
access_token: $('.js-auth-token').value
})
);
$('.js-schedules-list').hidden = true;
$('.js-schedules').hidden = false;
return window
.fetch('/api/v0/schedules', {
headers: { Authorization: getToken() }
})
.then(function(resp) {
return resp.json().then(function(schedules) {
allSchedules = schedules;
renderSchedules(schedules);
});
});
}
function renderSchedules(schedules) {
document.querySelector('.js-schedules-output').innerText = JSON.stringify(schedules, null, 2);
}
function scheduleTask() {
return window
.fetch('/api/v0/schedules/new', {
method: 'POST',
headers: {
Authorization: getToken(),
'Content-Type': 'application/json'
},
body: JSON.stringify(task)
})
.then(function(resp) {
return resp.json().then(function(schedule) {
console.log('New Schedule:', schedule);
allSchedules.push(schedule);
renderSchedules(allSchedules);
});
});
}
//window.addEventListener('load', run); //window.addEventListener('load', run);
})(); })();

View File

@ -10,26 +10,29 @@
<form class="js-schedules-list"> <form class="js-schedules-list">
<label <label
>Token: >Token:
<input class="js-auth-token" type="text" /> <input class="js-auth-token" type="text" required />
</label> </label>
<button>See Schedules</button> <button>Login</button>
</form> </form>
<pre><code class="js-schedules-output"> </code></pre> <pre><code class="js-schedules-output"> </code></pre>
<div class="js-schedules"> <div class="js-schedules" hidden>
<h2>Schedules</h2> <h2>Schedules</h2>
<div class="js-schedule"> <div class="js-schedule">
<form class="js-new-schedule"> <form class="js-new-schedule">
<label>Date: <input type="date" required/></label> <label>Date: <input type="date" class="js-date" required/></label>
<br /> <label
<label>Time: <input type="time" step="60" required/></label> >Time: <input type="time" class="js-time" step="60" required
<br /> /></label>
<!-- TODO combo box --> <!-- TODO combo box -->
<select class="js-schedule-tz"> <label
>Location:
<select class="js-tz">
<option value="UTC">UTC</option> <option value="UTC">UTC</option>
<option disabled>──────────</option> <option disabled>──────────</option>
</select> </select>
</label>
<br /> <br />
<div class="doc-webhooks-container"> <div class="doc-webhooks-container">
@ -54,11 +57,16 @@
</div> </div>
</div> </div>
<div class="js-new-webhook"> <div class="js-new-webhook">
<!--
<select class="js-template"> <select class="js-template">
<option value="" selected>Custom</option> <option value="webhook" selected>Custom Webhook</option>
<option value="dweet-v2">Dweet v2</option> <option value="requestbin">RequestBin</option>
<option value="mailgun">Maligun</option>
<option value="twilio">Twilio</option>
<option value="pushbullet">Pushbullet</option>
</select> </select>
<br /> <br />
-->
<input <input
class="js-comment" class="js-comment"
type="text" type="text"
@ -101,66 +109,6 @@
</div> </div>
<script src="./ajquery.js"></script> <script src="./ajquery.js"></script>
<script> <script src="./app.js"></script>
'use strict';
var allSchedules = [];
document.body.addEventListener('submit', function(ev) {
if (ev.target.matches('.js-schedules-list')) {
ev.preventDefault();
ev.stopPropagation();
getSchedules();
return;
} else if (ev.target.matches('.js-schedules-new')) {
ev.preventDefault();
ev.stopPropagation();
scheduleTask();
return;
}
});
function getToken() {
return document.querySelector('.js-auth-token').value;
}
function getSchedules() {
return window
.fetch('/api/schedules', {
headers: { Authorization: getToken() }
})
.then(function(resp) {
return resp.json().then(function(schedules) {
allSchedules = schedules;
renderSchedules(schedules);
});
});
}
function renderSchedules(schedules) {
document.querySelector(
'.js-schedules-output'
).innerText = JSON.stringify(schedules, null, 2);
}
function scheduleTask() {
return window
.fetch('/api/schedules/new', {
method: 'POST',
headers: {
Authorization: getToken(),
'Content-Type': 'application/json'
},
body: JSON.stringify(task)
})
.then(function(resp) {
return resp.json().then(function(schedule) {
console.log('New Schedule:', schedule);
allSchedules.push(schedule);
renderSchedules(allSchedules);
});
});
}
</script>
<script src="./hooks.js"></script>
</body> </body>
</html> </html>

12
webhooks/webhooks.go Normal file
View File

@ -0,0 +1,12 @@
package webooks
type Webhook struct {
Name string `json:"name"`
Method string `json:"method"`
URL string `json:"url"`
Auth map[string]string `json:"auth"`
Headers map[string]string `json:"headers"`
Form map[string]string `json:"form"`
JSON map[string]string `json:"json"`
Config map[string]string `json:"config"`
}