wip can send event
This commit is contained in:
		
							parent
							
								
									0c6003a894
								
							
						
					
					
						commit
						f135020914
					
				
							
								
								
									
										3
									
								
								again.go
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								again.go
									
									
									
									
									
								
							| @ -3,10 +3,13 @@ package again | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	webhooks "git.rootprojects.org/root/go-again/webhooks" | ||||
| ) | ||||
| 
 | ||||
| type Schedule struct { | ||||
| 	NextRunAt time.Time | ||||
| 	Webhooks  []webhooks.Webhook | ||||
| } | ||||
| 
 | ||||
| // https://yourbasic.org/golang/time-change-convert-location-timezone/ | ||||
|  | ||||
| @ -75,7 +75,7 @@ func main() { | ||||
| 		MaxHeaderBytes: 1 << 20, | ||||
| 	} | ||||
| 	//mux.Handle("/api/", http.HandlerFunc(handleFunc)) | ||||
| 	mux.HandleFunc("/api/schedules", s.Handle) | ||||
| 	mux.HandleFunc("/api/v0/schedules", s.Handle) | ||||
| 
 | ||||
| 	// TODO Filebox FS | ||||
| 	mux.Handle("/", http.FileServer(http.Dir("./public"))) | ||||
|  | ||||
							
								
								
									
										8
									
								
								public/.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								public/.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| { | ||||
|   "bracketSpacing": true, | ||||
|   "printWidth": 120, | ||||
|   "singleQuote": true, | ||||
|   "tabWidth": 2, | ||||
|   "trailingComma": "none", | ||||
|   "useTabs": true | ||||
| } | ||||
| @ -5,7 +5,7 @@ | ||||
| 	var $ = window.$; | ||||
| 	var $$ = window.$$; | ||||
| 
 | ||||
| 	var state = {}; | ||||
| 	var state = { account: { schedules: [] } }; | ||||
| 
 | ||||
| 	var $grantTpl; | ||||
| 	var $devTpl; | ||||
| @ -14,24 +14,13 @@ | ||||
| 	var $webhookTpl; | ||||
| 	var $webhookHeaderTpl; | ||||
| 
 | ||||
| 	// TODO add offset based on date selected (i.e. MDT -0500)
 | ||||
| 	var tzdb = [ | ||||
| 		'America/New_York', | ||||
| 		'America/Chicago', | ||||
| 		'America/Denver', | ||||
| 		'America/Phoenix', | ||||
| 		'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 pad(i) { | ||||
| 		i = String(i); | ||||
| 		while (i.length < 2) { | ||||
| 			i = '0' + i; | ||||
| 		} | ||||
| 		return i; | ||||
| 	} | ||||
| 
 | ||||
| 	function run() { | ||||
| 		$headerTpl = $('.js-new-webhook .js-header').outerHTML; | ||||
| @ -44,6 +33,14 @@ | ||||
| 		// after blanking all inner templates
 | ||||
| 		$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'); | ||||
| 
 | ||||
| 		$('body').addEventListener('click', function(ev) { | ||||
| @ -51,15 +48,9 @@ | ||||
| 				newWebhookHeader(ev.target); | ||||
| 			} else if (ev.target.matches('.js-rm-header')) { | ||||
| 				rmWebhookHeader(ev.target); | ||||
| 			} else if ( | ||||
| 				ev.target.matches('.js-delete') && | ||||
| 				ev.target.closest('.js-grant') | ||||
| 			) { | ||||
| 			} else if (ev.target.matches('.js-delete') && ev.target.closest('.js-grant')) { | ||||
| 				deleteGrant(ev.target.closest('.js-grant')); | ||||
| 			} else if ( | ||||
| 				ev.target.matches('.js-delete') && | ||||
| 				ev.target.closest('.js-webhook') | ||||
| 			) { | ||||
| 			} else if (ev.target.matches('.js-delete') && ev.target.closest('.js-webhook')) { | ||||
| 				deleteWebhook(ev.target.closest('.js-webhook')); | ||||
| 			} else { | ||||
| 				return; | ||||
| @ -68,26 +59,44 @@ | ||||
| 			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) { | ||||
| 			if (ev.target.matches('.js-new-schedule')) { | ||||
| 				newSchedule(ev.target); | ||||
| 			} else if (ev.target.matches('.js-schedules-list')) { | ||||
| 				doLogin(); | ||||
| 			} else if (ev.target.matches('.js-schedules-new')) { | ||||
| 				scheduleTask(); | ||||
| 			} else { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			ev.preventDefault(); | ||||
| 			ev.stopPropagation(); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	function newSchedule() { | ||||
| 		var $hook = $('.js-new-webhook'); | ||||
| 		var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value; | ||||
| 		var $hook = $('.js-schedule'); | ||||
| 		//var deviceId = $hook.closest('.js-schedule').querySelector('.js-id').value;
 | ||||
| 		var hook = { | ||||
| 			date: $('.js-date', $hook).value, | ||||
| 			time: $('.js-time', $hook).value, | ||||
| 			tz: $('.js-tz', $hook).value, | ||||
| 			comment: $('.js-comment', $hook).value, | ||||
| 			method: $('.js-method', $hook).value, | ||||
| 			url: $('.js-url', $hook).value, | ||||
| 			headers: {} | ||||
| 		}; | ||||
| 		console.log('schedule:', hook); | ||||
| 		$$('.js-header', $hook).forEach(function($head) { | ||||
| 			var key = $('.js-key', $head).value; | ||||
| 			var val = $('.js-value', $head).value; | ||||
| @ -102,7 +111,7 @@ | ||||
| 			method: 'POST', | ||||
| 			headers: { | ||||
| 				Accept: 'application/json', | ||||
| 				Authorization: JSON.parse(localStorage.getItem('session')).access_token, | ||||
| 				Authorization: getToken(), | ||||
| 				'Content-Type': 'application/json' | ||||
| 			}, | ||||
| 			body: JSON.stringify(hook), | ||||
| @ -110,39 +119,32 @@ | ||||
| 		}; | ||||
| 
 | ||||
| 		/* | ||||
|                 state.account.devices | ||||
|                         .filter(function(d) { | ||||
|                                 return d.accessToken == deviceId; | ||||
|                         })[0] | ||||
|                         .webhooks.push(hook); | ||||
|       state.account.devices | ||||
|         .filter(function(d) { | ||||
|           return d.accessToken == deviceId; | ||||
|         })[0] | ||||
|         .webhooks.push(hook); | ||||
| 
 | ||||
|                 displayAccount(state.account); | ||||
|                 return; | ||||
|       displayAccount(state.account); | ||||
|       return; | ||||
|     */ | ||||
| 
 | ||||
| 		window | ||||
| 			.fetch('/api/iot/devices/' + deviceId + '/webhooks', opts) | ||||
| 			.then(function(resp) { | ||||
| 				return resp | ||||
| 					.json() | ||||
| 					.then(function(data) { | ||||
| 						if (!data.webhook) { | ||||
| 							throw new Error('something bad happened'); | ||||
| 							return; | ||||
| 						} | ||||
| 		window.fetch('/api/v0/schedules', opts).then(function(resp) { | ||||
| 			return resp | ||||
| 				.json() | ||||
| 				.then(function(data) { | ||||
| 					if (!data.schedule) { | ||||
| 						throw new Error('something bad happened'); | ||||
| 					} | ||||
| 
 | ||||
| 						state.account.devices | ||||
| 							.filter(function(d) { | ||||
| 								return d.accessToken == deviceId; | ||||
| 							})[0] | ||||
| 							.webhooks.push(resp.data.webhook); | ||||
| 					state.account.schedules.webhooks.push(resp.data.schedule); | ||||
| 
 | ||||
| 						displayAccount(state.account); | ||||
| 					}) | ||||
| 					.catch(function(e) { | ||||
| 						window.alert(e.message); | ||||
| 					}); | ||||
| 			}); | ||||
| 					displayAccount(state.account); | ||||
| 				}) | ||||
| 				.catch(function(e) { | ||||
| 					window.alert(e.message); | ||||
| 				}); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	function newWebhookHeader($newHeader) { | ||||
| @ -168,35 +170,33 @@ | ||||
| 			method: 'DELETE', | ||||
| 			headers: { | ||||
| 				Accept: 'application/json', | ||||
| 				Authorization: JSON.parse(localStorage.getItem('session')).access_token | ||||
| 				Authorization: getToken() | ||||
| 			}, | ||||
| 			cors: true | ||||
| 		}; | ||||
| 		window | ||||
| 			.fetch('/api/iot/devices/' + deviceId + '/webhooks/' + id, opts) | ||||
| 			.then(function(resp) { | ||||
| 				return resp.json().then(function(result) { | ||||
| 					if (!result.webhook) { | ||||
| 						console.error(result); | ||||
| 						window.alert('something went wrong: ' + JSON.stringify(result)); | ||||
| 						return; | ||||
| 					} | ||||
| 					var index = -1; | ||||
| 					var dev = state.account.devices.filter(function(d, i) { | ||||
| 						return d.accessToken == deviceId; | ||||
| 					})[0]; | ||||
| 					dev.webhooks.some(function(g, i) { | ||||
| 						if (g.id === id) { | ||||
| 							index = i; | ||||
| 							return true; | ||||
| 						} | ||||
| 					}); | ||||
| 					if (index > -1) { | ||||
| 						dev.webhooks.splice(index, 1); | ||||
| 						displayAccount(state.account); | ||||
| 		window.fetch('/api/iot/devices/' + deviceId + '/webhooks/' + id, opts).then(function(resp) { | ||||
| 			return resp.json().then(function(result) { | ||||
| 				if (!result.webhook) { | ||||
| 					console.error(result); | ||||
| 					window.alert('something went wrong: ' + JSON.stringify(result)); | ||||
| 					return; | ||||
| 				} | ||||
| 				var index = -1; | ||||
| 				var dev = state.account.devices.filter(function(d, i) { | ||||
| 					return d.accessToken == deviceId; | ||||
| 				})[0]; | ||||
| 				dev.webhooks.some(function(g, i) { | ||||
| 					if (g.id === id) { | ||||
| 						index = i; | ||||
| 						return true; | ||||
| 					} | ||||
| 				}); | ||||
| 				if (index > -1) { | ||||
| 					dev.webhooks.splice(index, 1); | ||||
| 					displayAccount(state.account); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	function displayAccount(data) { | ||||
| @ -232,11 +232,7 @@ | ||||
| 				$('.js-comment', $grant).innerText = g.comment; | ||||
| 				$('.js-token', $grant).innerText = g.token; | ||||
| 				//TODO Math.floor(Date.now() / 1000);
 | ||||
| 				var url = | ||||
| 					'https://test.therootcompany.com/api/v1/devices/' + | ||||
| 					d.accessToken + | ||||
| 					'/updates?since=' + | ||||
| 					0; | ||||
| 				var url = '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-token', $grant).innerText = g.token; | ||||
| 				$('.js-example-js .js-example-url', $grant).innerText = url; | ||||
| @ -258,10 +254,11 @@ | ||||
| 		return resp.json().then(function(tzdb) { | ||||
| 			console.info('[tzdb] received'); | ||||
| 			var tz = Intl.DateTimeFormat().resolvedOptions().timeZone; | ||||
| 			var options = $$('.js-schedule-tz option'); | ||||
| 			var options = $$('.js-tz option'); | ||||
| 			var valOpt = options[0].outerHTML; // UTC
 | ||||
| 			var spaceOpt = options[1].outerHTML; // ----
 | ||||
| 			var innerHTML = $('.js-schedule-tz').innerHTML; | ||||
| 			var innerHTML = $('.js-tz').innerHTML; | ||||
| 			/* | ||||
| 			innerHTML = | ||||
| 				'<option selected value="' + | ||||
| 				tz + | ||||
| @ -270,8 +267,9 @@ | ||||
| 				'</option>' + | ||||
| 				spaceOpt + | ||||
| 				innerHTML.replace(/>UTC/, '>    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) | ||||
| 				.sort() | ||||
| 				.forEach(function(k) { | ||||
| @ -287,15 +285,68 @@ | ||||
| 					areas.forEach(function(_tz) { | ||||
| 						if (tz !== _tz) { | ||||
| 							innerHTML += valOpt.replace(/UTC/g, _tz); | ||||
| 						} else { | ||||
| 							innerHTML += '<option selected value="' + tz + '">' + tz + '</option>'; | ||||
| 						} | ||||
| 					}); | ||||
| 					innerHTML += '</optgroup>'; | ||||
| 				}); | ||||
| 			$('.js-schedule-tz').innerHTML = innerHTML; | ||||
| 			$('.js-tz').innerHTML = innerHTML; | ||||
| 
 | ||||
| 			console.info('[tzdb] loaded'); | ||||
| 			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);
 | ||||
| })(); | ||||
| @ -10,26 +10,29 @@ | ||||
| 		<form class="js-schedules-list"> | ||||
| 			<label | ||||
| 				>Token: | ||||
| 				<input class="js-auth-token" type="text" /> | ||||
| 				<input class="js-auth-token" type="text" required /> | ||||
| 			</label> | ||||
| 			<button>See Schedules</button> | ||||
| 			<button>Login</button> | ||||
| 		</form> | ||||
| 
 | ||||
| 		<pre><code class="js-schedules-output"> </code></pre> | ||||
| 
 | ||||
| 		<div class="js-schedules"> | ||||
| 		<div class="js-schedules" hidden> | ||||
| 			<h2>Schedules</h2> | ||||
| 			<div class="js-schedule"> | ||||
| 				<form class="js-new-schedule"> | ||||
| 					<label>Date: <input type="date" required/></label> | ||||
| 					<br /> | ||||
| 					<label>Time: <input type="time" step="60" required/></label> | ||||
| 					<br /> | ||||
| 					<label>Date: <input type="date" class="js-date" required/></label> | ||||
| 					<label | ||||
| 						>Time: <input type="time" class="js-time" step="60" required | ||||
| 					/></label> | ||||
| 					<!-- TODO combo box --> | ||||
| 					<select class="js-schedule-tz"> | ||||
| 						<option value="UTC">UTC</option> | ||||
| 						<option disabled>──────────</option> | ||||
| 					</select> | ||||
| 					<label | ||||
| 						>Location: | ||||
| 						<select class="js-tz"> | ||||
| 							<option value="UTC">UTC</option> | ||||
| 							<option disabled>──────────</option> | ||||
| 						</select> | ||||
| 					</label> | ||||
| 					<br /> | ||||
| 
 | ||||
| 					<div class="doc-webhooks-container"> | ||||
| @ -54,11 +57,16 @@ | ||||
| 							</div> | ||||
| 						</div> | ||||
| 						<div class="js-new-webhook"> | ||||
| 							<!-- | ||||
| 							<select class="js-template"> | ||||
| 								<option value="" selected>Custom</option> | ||||
| 								<option value="dweet-v2">Dweet v2</option> | ||||
| 								<option value="webhook" selected>Custom Webhook</option> | ||||
| 								<option value="requestbin">RequestBin</option> | ||||
| 								<option value="mailgun">Maligun</option> | ||||
| 								<option value="twilio">Twilio</option> | ||||
| 								<option value="pushbullet">Pushbullet</option> | ||||
| 							</select> | ||||
| 							<br /> | ||||
|               --> | ||||
| 							<input | ||||
| 								class="js-comment" | ||||
| 								type="text" | ||||
| @ -101,66 +109,6 @@ | ||||
| 		</div> | ||||
| 
 | ||||
| 		<script src="./ajquery.js"></script> | ||||
| 		<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> | ||||
| 		<script src="./app.js"></script> | ||||
| 	</body> | ||||
| </html> | ||||
|  | ||||
							
								
								
									
										12
									
								
								webhooks/webhooks.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								webhooks/webhooks.go
									
									
									
									
									
										Normal 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"` | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user