├── .gitignore ├── Makefile ├── README.md ├── assets ├── .prettierrc ├── events.html ├── htmx.min.js ├── layout.html ├── monitors.html ├── output.css ├── package-lock.json ├── package.json ├── row.html ├── styles.css └── tailwind.config.js ├── cmd └── tevents │ └── main.go ├── db ├── db.go └── schema.sql ├── event.go ├── go.mod ├── go.sum ├── http.go ├── http_test.go ├── notifier.go └── screenshots └── events-monitors.png /.gitignore: -------------------------------------------------------------------------------- 1 | db.sqlite 2 | node_modules 3 | ./tevents 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: tailwind 4 | go build ./cmd/tevents 5 | 6 | run: 7 | go run ./cmd/tevents 8 | 9 | watch: 10 | nodemon --watch '*' -e html,go --exec go run ./cmd/tevents --signal SIGTERM 11 | 12 | tailwind: 13 | cd assets && npx tailwindcss -i ./styles.css -o ./output.css 14 | 15 | tailwind-watch: 16 | cd assets && npx tailwindcss -i ./styles.css -o ./output.css --watch 17 | 18 | test: 19 | go test ./... 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tevents 2 | 3 | tevents is a small tool to be deployed in your [tailnet](https://tailscale.com/kb/1136/tailnet/) to collect events/hooks from other services. 4 | By using [tsnet](https://tailscale.com/blog/tsnet-virtual-private-services/), it can be deployed as a virtual private service. This allows to centrally collect 5 | events in your network and display them on a web interface. 6 | 7 | ![](./screenshots/events-monitors.png) 8 | 9 | ## Usage 10 | 11 | All required resources are embedded in the executable. 12 | 13 | ``` 14 | git clone github.com/rverton/tevents 15 | cd tevents && make 16 | export TS_AUTHKEY=tskey-auth-xyu # can be retrieved via admin panel 17 | go build -o tevents ./cmd/tevents 18 | ./tevents # all assets are embedded in this binary 19 | ``` 20 | 21 | ## Events 22 | An event holds the following fields: 23 | 24 | ``` 25 | origin: (unique) identifier of the sender 26 | type: type of the event (event or monitor-event) 27 | body: content 28 | owner: tailnet owner who send the event 29 | ``` 30 | 31 | There are two different types of events: 32 | 33 | - Log events are simple one-time HTTP requests to to notify of a specific event. 34 | - Monitor events are events that are sent periodically and allow you to graph execution. This allows for example to watch cron jobs for execution. 35 | 36 | ## Submission 37 | 38 | Events can be submitted via HTTP POST. 39 | 40 | ``` 41 | # example for log event 42 | curl http://tevents/.log?origin=networkwatcher -d "new device found connected to network" 43 | 44 | # example for monitoring a cron job executed every morning 45 | 0 1 * * * /usr/local/bin/backup.sh && curl -X POST http://tevents/.monitor?origin=cron:backup 46 | ``` 47 | 48 | ## Development 49 | 50 | All relevant tasks can be done via `make`: 51 | 52 | ``` 53 | make watch # restart web server on code changes 54 | make tailwind-watch # watch tailwind css changes 55 | make tailwind # only build tailwind resources 56 | make run # run web server 57 | make test # run all tests 58 | make # build executable with all assets embedded 59 | ``` 60 | 61 | ### ToDo 62 | 63 | - [ ] Add tests for event/monitor hook handler 64 | - [ ] Make hour interval configurable 65 | - [ ] Filter, search and pagination 66 | - [ ] Allow to remove monitors (and all corresponding events) 67 | - [x] Clear functionality 68 | -------------------------------------------------------------------------------- /assets/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": ["*.html"], 5 | "options": { 6 | "parser": "go-template" 7 | } 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /assets/events.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 | 5 | {{ range .Events }} 6 | {{template "row.html" . }} 7 | {{ else }} 8 |
There are currently no events in your database.
9 | {{ end }} 10 |
11 | 12 | 13 |
14 | 15 | curl http://tevents/.log?origin=networkwatcher -d "example 16 | event" 18 |
19 | {{ end }} 20 | -------------------------------------------------------------------------------- /assets/htmx.min.js: -------------------------------------------------------------------------------- 1 | (function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else{e.htmx=e.htmx||t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var z={onLoad:t,process:mt,on:D,off:X,trigger:ee,ajax:or,find:C,findAll:R,closest:A,values:function(e,t){var r=Bt(e,t||"post");return r.values},remove:O,addClass:q,removeClass:T,toggleClass:L,takeClass:H,defineExtension:dr,removeExtension:vr,logAll:E,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false,getCacheBusterParam:false},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=z.config.wsBinaryType;return t},version:"1.8.5"};var r={addTriggerHandler:ft,bodyContains:re,canAccessLocalStorage:S,filterValues:Wt,hasAttribute:o,getAttributeValue:J,getClosestMatch:h,getExpressionVars:rr,getHeaders:_t,getInputValues:Bt,getInternalData:K,getSwapSpecification:Gt,getTriggerSpecs:Xe,getTarget:se,makeFragment:f,mergeObjects:ne,makeSettleInfo:Zt,oobSwap:V,selectAndSwap:Oe,settleImmediately:At,shouldCancel:Ve,triggerEvent:ee,triggerErrorEvent:Q,withExtensions:wt};var n=["get","post","put","delete","patch"];var i=n.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function v(e){if(e==undefined){return undefined}if(e.slice(-2)=="ms"){return parseFloat(e.slice(0,-2))||undefined}if(e.slice(-1)=="s"){return parseFloat(e.slice(0,-1))*1e3||undefined}if(e.slice(-1)=="m"){return parseFloat(e.slice(0,-1))*1e3*60||undefined}return parseFloat(e)||undefined}function G(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function J(e,t){return G(e,t)||G(e,"data-"+t)}function u(e){return e.parentElement}function $(){return document}function h(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function a(e,t,r){var n=J(t,r);var i=J(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function Z(t,r){var n=null;h(t,function(e){return n=a(t,e,r)});if(n!=="unset"){return n}}function d(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function s(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function l(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=$().createDocumentFragment()}return i}function f(e){if(z.config.useTemplateFragments){var t=l("",0);return t.querySelector("template").content}else{var r=s(e);switch(r){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return l(""+e+"
",1);case"col":return l(""+e+"
",2);case"tr":return l(""+e+"
",2);case"td":case"th":return l(""+e+"
",3);case"script":return l("
"+e+"
",1);default:return l(e,0)}}}function te(e){if(e){e()}}function g(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function p(e){return g(e,"Function")}function m(e){return g(e,"Object")}function K(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function y(e){var t=[];if(e){for(var r=0;r=0}function re(e){if(e.getRootNode&&e.getRootNode()instanceof ShadowRoot){return $().body.contains(e.getRootNode().host)}else{return $().body.contains(e)}}function b(e){return e.trim().split(/\s+/)}function ne(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function w(e){try{return JSON.parse(e)}catch(e){St(e);return null}}function S(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function e(e){return Qt($().body,function(){return eval(e)})}function t(t){var e=z.on("htmx:load",function(e){t(e.detail.elt)});return e}function E(){z.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function C(e,t){if(t){return e.querySelector(t)}else{return C($(),e)}}function R(e,t){if(t){return e.querySelectorAll(t)}else{return R($(),e)}}function O(e,t){e=M(e);if(t){setTimeout(function(){O(e)},t)}else{e.parentElement.removeChild(e)}}function q(e,t,r){e=M(e);if(r){setTimeout(function(){q(e,t)},r)}else{e.classList&&e.classList.add(t)}}function T(e,t,r){e=M(e);if(r){setTimeout(function(){T(e,t)},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function L(e,t){e=M(e);e.classList.toggle(t)}function H(e,t){e=M(e);Y(e.parentElement.children,function(e){T(e,t)});q(e,t)}function A(e,t){e=M(e);if(e.closest){return e.closest(t)}else{do{if(e==null||d(e,t)){return e}}while(e=e&&u(e));return null}}function N(e,t){if(t.indexOf("closest ")===0){return[A(e,t.substr(8))]}else if(t.indexOf("find ")===0){return[C(e,t.substr(5))]}else if(t.indexOf("next ")===0){return[I(e,t.substr(5))]}else if(t.indexOf("previous ")===0){return[k(e,t.substr(9))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else{return $().querySelectorAll(t)}}var I=function(e,t){var r=$().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function ie(e,t){if(t){return N(e,t)[0]}else{return N($().body,e)[0]}}function M(e){if(g(e,"String")){return C(e)}else{return e}}function P(e,t,r){if(p(t)){return{target:$().body,event:e,listener:t}}else{return{target:M(e),event:t,listener:r}}}function D(t,r,n){pr(function(){var e=P(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=p(r);return e?r:n}function X(t,r,n){pr(function(){var e=P(t,r,n);e.target.removeEventListener(e.event,e.listener)});return p(r)?r:n}var ae=$().createElement("output");function F(e,t){var r=Z(e,t);if(r){if(r==="this"){return[oe(e,t)]}else{var n=N(e,r);if(n.length===0){St('The selector "'+r+'" on '+t+" returned no matches!");return[ae]}else{return n}}}}function oe(e,t){return h(e,function(e){return J(e,t)!=null})}function se(e){var t=Z(e,"hx-target");if(t){if(t==="this"){return oe(e,"hx-target")}else{return ie(e,t)}}else{var r=K(e);if(r.boosted){return $().body}else{return e}}}function B(e){var t=z.config.attributesToSettle;for(var r=0;r0){o=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{o=e}var r=$().querySelectorAll(t);if(r){Y(r,function(e){var t;var r=i.cloneNode(true);t=$().createDocumentFragment();t.appendChild(r);if(!U(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!ee(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){Ce(o,e,e,t,a)}Y(a.elts,function(e){ee(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);Q($().body,"htmx:oobErrorNoTarget",{content:i})}return e}function _(e,t,r){var n=Z(e,"hx-select-oob");if(n){var i=n.split(",");for(let e=0;e0){var t=n.querySelector(e.tagName+"[id='"+e.id+"']");if(t&&t!==n){var r=e.cloneNode();j(e,t);i.tasks.push(function(){j(e,r)})}}})}function ue(e){return function(){T(e,z.config.addedClass);mt(e);ht(e);fe(e);ee(e,"htmx:load")}}function fe(e){var t="[autofocus]";var r=d(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function ce(e,t,r,n){le(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;q(i,z.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(ue(i))}}}function he(e,t){var r=0;while(r-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function Oe(e,t,r,n,i){i.title=Re(n);var a=f(n);if(a){_(r,a,i);a=Ee(r,a);W(a);return Ce(e,r,t,a,i)}}function qe(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=w(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!m(o)){o={value:o}}ee(r,a,o)}}}else{ee(r,n,[])}}var Te=/\s/;var Le=/[\s,]/;var He=/[_$a-zA-Z]/;var Ae=/[_$a-zA-Z0-9]/;var Ne=['"',"'","/"];var Ie=/[^\s]/;function ke(e){var t=[];var r=0;while(r0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=Qt(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){Q($().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if(Me(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function c(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var De="input, textarea, select";function Xe(e){var t=J(e,"hx-trigger");var r=[];if(t){var n=ke(t);do{c(n,Ie);var f=n.length;var i=c(n,/[,\[\s]/);if(i!==""){if(i==="every"){var a={trigger:"every"};c(n,Ie);a.pollInterval=v(c(n,/[,\[\s]/));c(n,Ie);var o=Pe(e,n,"event");if(o){a.eventFilter=o}r.push(a)}else if(i.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:i.substr(4)})}else{var s={trigger:i};var o=Pe(e,n,"event");if(o){s.eventFilter=o}while(n.length>0&&n[0]!==","){c(n,Ie);var l=n.shift();if(l==="changed"){s.changed=true}else if(l==="once"){s.once=true}else if(l==="consume"){s.consume=true}else if(l==="delay"&&n[0]===":"){n.shift();s.delay=v(c(n,Le))}else if(l==="from"&&n[0]===":"){n.shift();var u=c(n,Le);if(u==="closest"||u==="find"||u==="next"||u==="previous"){n.shift();u+=" "+c(n,Le)}s.from=u}else if(l==="target"&&n[0]===":"){n.shift();s.target=c(n,Le)}else if(l==="throttle"&&n[0]===":"){n.shift();s.throttle=v(c(n,Le))}else if(l==="queue"&&n[0]===":"){n.shift();s.queue=c(n,Le)}else if((l==="root"||l==="threshold")&&n[0]===":"){n.shift();s[l]=c(n,Le)}else{Q(e,"htmx:syntax:error",{token:n.shift()})}}r.push(s)}}if(n.length===f){Q(e,"htmx:syntax:error",{token:n.shift()})}c(n,Ie)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(d(e,"form")){return[{trigger:"submit"}]}else if(d(e,'input[type="button"]')){return[{trigger:"click"}]}else if(d(e,De)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Fe(e){K(e).cancelled=true}function Be(e,t,r){var n=K(e);n.timeout=setTimeout(function(){if(re(e)&&n.cancelled!==true){if(!We(r,xt("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}Be(e,t,r)}},r.pollInterval)}function je(e){return location.hostname===e.hostname&&G(e,"href")&&G(e,"href").indexOf("#")!==0}function Ue(t,r,e){if(t.tagName==="A"&&je(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=G(t,"href")}else{var a=G(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=G(t,"action")}e.forEach(function(e){ze(t,function(e,t){lr(n,i,e,t)},r,e,true)})}}function Ve(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(d(t,'input[type="submit"], button')&&A(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function _e(e,t){return K(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function We(e,t){var r=e.eventFilter;if(r){try{return r(t)!==true}catch(e){Q($().body,"htmx:eventFilter:error",{error:e,source:r.source});return true}}return false}function ze(a,o,e,s,l){var t;if(s.from){t=N(a,s.from)}else{t=[a]}Y(t,function(n){var i=function(e){if(!re(a)){n.removeEventListener(s.trigger,i);return}if(_e(a,e)){return}if(l||Ve(e,a)){e.preventDefault()}if(We(s,e)){return}var t=K(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}var r=K(a);if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!d(e.target,s.target)){return}}if(s.once){if(r.triggeredOnce){return}else{r.triggeredOnce=true}}if(s.changed){if(r.lastValue===a.value){return}else{r.lastValue=a.value}}if(r.delayed){clearTimeout(r.delayed)}if(r.throttle){return}if(s.throttle){if(!r.throttle){o(a,e);r.throttle=setTimeout(function(){r.throttle=null},s.throttle)}}else if(s.delay){r.delayed=setTimeout(function(){o(a,e)},s.delay)}else{o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var Ge=false;var Je=null;function $e(){if(!Je){Je=function(){Ge=true};window.addEventListener("scroll",Je);setInterval(function(){if(Ge){Ge=false;Y($().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){Ze(e)})}},200)}}function Ze(t){if(!o(t,"data-hx-revealed")&&x(t)){t.setAttribute("data-hx-revealed","true");var e=K(t);if(e.initHash){ee(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){ee(t,"revealed")},{once:true})}}}function Ke(e,t,r){var n=b(r);for(var i=0;i=0){var t=tt(n);setTimeout(function(){Ye(s,r,n+1)},t)}};t.onopen=function(e){n=0};K(s).webSocket=t;t.addEventListener("message",function(e){if(Qe(s)){return}var t=e.data;wt(s,function(e){t=e.transformResponse(t,null,s)});var r=Zt(s);var n=f(t);var i=y(n.children);for(var a=0;a0){ee(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(Ve(e,u)){e.preventDefault()}})}else{Q(u,"htmx:noWebSocketSourceError")}}function tt(e){var t=z.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}St('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function rt(e,t,r){var n=b(r);for(var i=0;iz.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){Q($().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Ot(e){if(!S()){return null}var t=w(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){ee($().body,"htmx:historyCacheMissLoad",o);var e=f(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Ct();var r=Zt(t);var n=Re(this.response);if(n){var i=C("title");if(i){i.innerHTML=n}else{window.document.title=n}}Se(t,e,r);At(r.tasks);Et=a;ee($().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{Q($().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function It(e){Tt();e=e||location.pathname+location.search;var t=Ot(e);if(t){var r=f(t.content);var n=Ct();var i=Zt(n);Se(n,r,i);At(i.tasks);document.title=t.title;window.scrollTo(0,t.scroll);Et=e;ee($().body,"htmx:historyRestore",{path:e,item:t})}else{if(z.config.refreshOnHistoryMiss){window.location.reload(true)}else{Nt(e)}}}function kt(e){var t=F(e,"hx-indicator");if(t==null){t=[e]}Y(t,function(e){var t=K(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,z.config.requestClass)});return t}function Mt(e){Y(e,function(e){var t=K(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,z.config.requestClass)}})}function Pt(e,t){for(var r=0;r=0}function Gt(e,t){var r=t?t:Z(e,"hx-swap");var n={swapStyle:K(e).boosted?"innerHTML":z.config.defaultSwapStyle,swapDelay:z.config.defaultSwapDelay,settleDelay:z.config.defaultSettleDelay};if(K(e).boosted&&!zt(e)){n["show"]="top"}if(r){var i=b(r);if(i.length>0){n["swapStyle"]=i[0];for(var a=1;a0?l.join(":"):null;n["scroll"]=f;n["scrollTarget"]=u}if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var u=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=u}if(o.indexOf("focus-scroll:")===0){var d=o.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}}}}return n}function Jt(e){return Z(e,"hx-encoding")==="multipart/form-data"||d(e,"form")&&G(e,"enctype")==="multipart/form-data"}function $t(t,r,n){var i=null;wt(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(Jt(r)){return Vt(n)}else{return Ut(n)}}}function Zt(e){return{tasks:[],elts:[e]}}function Kt(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=ie(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=ie(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:z.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:z.config.scrollBehavior})}}}function Yt(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=J(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=Qt(e,function(){return Function("return ("+a+")")()},{})}else{s=w(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return Yt(u(e),t,r,n)}function Qt(e,t,r){if(z.config.allowEval){return t()}else{Q(e,"htmx:evalDisallowedError");return r}}function er(e,t){return Yt(e,"hx-vars",true,t)}function tr(e,t){return Yt(e,"hx-vals",false,t)}function rr(e){return ne(er(e),tr(e))}function nr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function ir(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){Q($().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function ar(e,t){return e.getAllResponseHeaders().match(t)}function or(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||g(r,"String")){return lr(e,t,null,null,{targetOverride:M(r),returnPromise:true})}else{return lr(e,t,M(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:M(r.target),swapOverride:r.swap,returnPromise:true})}}else{return lr(e,t,null,null,{returnPromise:true})}}function sr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function lr(e,t,n,r,i,f){var c=null;var h=null;i=i!=null?i:{};if(i.returnPromise&&typeof Promise!=="undefined"){var d=new Promise(function(e,t){c=e;h=t})}if(n==null){n=$().body}var v=i.handler||fr;if(!re(n)){return}var g=i.targetOverride||se(n);if(g==null||g==ae){Q(n,"htmx:targetError",{target:J(n,"hx-target")});return}if(!f){var p=function(){return lr(e,t,n,r,i,true)};var m={target:g,elt:n,path:t,verb:e,triggeringEvent:r,etc:i,issueRequest:p};if(ee(n,"htmx:confirm",m)===false){return}}var y=n;var a=K(n);var x=Z(n,"hx-sync");var b=null;var w=false;if(x){var S=x.split(":");var E=S[0].trim();if(E==="this"){y=oe(n,"hx-sync")}else{y=ie(n,E)}x=(S[1]||"drop").trim();a=K(y);if(x==="drop"&&a.xhr&&a.abortable!==true){return}else if(x==="abort"){if(a.xhr){return}else{w=true}}else if(x==="replace"){ee(y,"htmx:abort")}else if(x.indexOf("queue")===0){var C=x.split(" ");b=(C[1]||"last").trim()}}if(a.xhr){if(a.abortable){ee(y,"htmx:abort")}else{if(b==null){if(r){var R=K(r);if(R&&R.triggerSpec&&R.triggerSpec.queue){b=R.triggerSpec.queue}}if(b==null){b="last"}}if(a.queuedRequests==null){a.queuedRequests=[]}if(b==="first"&&a.queuedRequests.length===0){a.queuedRequests.push(function(){lr(e,t,n,r,i)})}else if(b==="all"){a.queuedRequests.push(function(){lr(e,t,n,r,i)})}else if(b==="last"){a.queuedRequests=[];a.queuedRequests.push(function(){lr(e,t,n,r,i)})}return}}var o=new XMLHttpRequest;a.xhr=o;a.abortable=w;var s=function(){a.xhr=null;a.abortable=false;if(a.queuedRequests!=null&&a.queuedRequests.length>0){var e=a.queuedRequests.shift();e()}};var O=Z(n,"hx-prompt");if(O){var q=prompt(O);if(q===null||!ee(n,"htmx:prompt",{prompt:q,target:g})){te(c);s();return d}}var T=Z(n,"hx-confirm");if(T){if(!confirm(T)){te(c);s();return d}}var L=_t(n,g,q);if(i.headers){L=ne(L,i.headers)}var H=Bt(n,e);var A=H.errors;var N=H.values;if(i.values){N=ne(N,i.values)}var I=rr(n);var k=ne(N,I);var M=Wt(k,n);if(e!=="get"&&!Jt(n)){L["Content-Type"]="application/x-www-form-urlencoded"}if(z.config.getCacheBusterParam&&e==="get"){M["org.htmx.cache-buster"]=G(g,"id")||"true"}if(t==null||t===""){t=$().location.href}var P=Yt(n,"hx-request");var D=K(n).boosted;var l={boosted:D,parameters:M,unfilteredParameters:k,headers:L,target:g,verb:e,errors:A,withCredentials:i.credentials||P.credentials||z.config.withCredentials,timeout:i.timeout||P.timeout||z.config.timeout,path:t,triggeringEvent:r};if(!ee(n,"htmx:configRequest",l)){te(c);s();return d}t=l.path;e=l.verb;L=l.headers;M=l.parameters;A=l.errors;if(A&&A.length>0){ee(n,"htmx:validation:halted",l);te(c);s();return d}var X=t.split("#");var F=X[0];var B=X[1];var j=null;if(e==="get"){j=F;var U=Object.keys(M).length!==0;if(U){if(j.indexOf("?")<0){j+="?"}else{j+="&"}j+=Ut(M);if(B){j+="#"+B}}o.open("GET",j,true)}else{o.open(e.toUpperCase(),t,true)}o.overrideMimeType("text/html");o.withCredentials=l.withCredentials;o.timeout=l.timeout;if(P.noHeaders){}else{for(var V in L){if(L.hasOwnProperty(V)){var _=L[V];nr(o,V,_)}}}var u={xhr:o,target:g,requestConfig:l,etc:i,boosted:D,pathInfo:{requestPath:t,finalRequestPath:j||t,anchor:B}};o.onload=function(){try{var e=sr(n);u.pathInfo.responsePath=ir(o);v(n,u);Mt(W);ee(n,"htmx:afterRequest",u);ee(n,"htmx:afterOnLoad",u);if(!re(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(re(r)){t=r}}if(t){ee(t,"htmx:afterRequest",u);ee(t,"htmx:afterOnLoad",u)}}te(c);s()}catch(e){Q(n,"htmx:onLoadError",ne({error:e},u));throw e}};o.onerror=function(){Mt(W);Q(n,"htmx:afterRequest",u);Q(n,"htmx:sendError",u);te(h);s()};o.onabort=function(){Mt(W);Q(n,"htmx:afterRequest",u);Q(n,"htmx:sendAbort",u);te(h);s()};o.ontimeout=function(){Mt(W);Q(n,"htmx:afterRequest",u);Q(n,"htmx:timeout",u);te(h);s()};if(!ee(n,"htmx:beforeRequest",u)){te(c);s();return d}var W=kt(n);Y(["loadstart","loadend","progress","abort"],function(t){Y([o,o.upload],function(e){e.addEventListener(t,function(e){ee(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});ee(n,"htmx:beforeSend",u);o.send(e==="get"?null:$t(o,n,M));return d}function ur(e,t){var r=t.xhr;var n=null;var i=null;if(ar(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(ar(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(ar(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=Z(e,"hx-push-url");var f=Z(e,"hx-replace-url");var c=K(e).boosted;var l=null;var u=null;if(s){l="push";u=s}else if(f){l="replace";u=f}else if(c){l="push";u=o||a}if(u){if(u==="false"){return{}}if(u==="true"){u=o||a}if(t.pathInfo.anchor&&u.indexOf("#")===-1){u=u+"#"+t.pathInfo.anchor}return{type:l,path:u}}else{return{}}}function fr(s,l){var u=l.xhr;var f=l.target;var n=l.etc;if(!ee(s,"htmx:beforeOnLoad",l))return;if(ar(u,/HX-Trigger:/i)){qe(u,"HX-Trigger",s)}if(ar(u,/HX-Location:/i)){Tt();var e=u.getResponseHeader("HX-Location");var c;if(e.indexOf("{")===0){c=w(e);e=c["path"];delete c["path"]}or("GET",e,c).then(function(){Lt(e)});return}if(ar(u,/HX-Redirect:/i)){location.href=u.getResponseHeader("HX-Redirect");return}if(ar(u,/HX-Refresh:/i)){if("true"===u.getResponseHeader("HX-Refresh")){location.reload();return}}if(ar(u,/HX-Retarget:/i)){l.target=$().querySelector(u.getResponseHeader("HX-Retarget"))}var h=ur(s,l);var i=u.status>=200&&u.status<400&&u.status!==204;var d=u.response;var t=u.status>=400;var r=ne({shouldSwap:i,serverResponse:d,isError:t},l);if(!ee(f,"htmx:beforeSwap",r))return;f=r.target;d=r.serverResponse;t=r.isError;l.failed=t;l.successful=!t;if(r.shouldSwap){if(u.status===286){Fe(s)}wt(s,function(e){d=e.transformResponse(d,u,s)});if(h.type){Tt()}var a=n.swapOverride;if(ar(u,/HX-Reswap:/i)){a=u.getResponseHeader("HX-Reswap")}var c=Gt(s,a);f.classList.add(z.config.swappingClass);var o=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var n=Zt(f);Oe(c.swapStyle,f,s,d,n);if(t.elt&&!re(t.elt)&&t.elt.id){var r=document.getElementById(t.elt.id);var i={preventScroll:c.focusScroll!==undefined?!c.focusScroll:!z.config.defaultFocusScroll};if(r){if(t.start&&r.setSelectionRange){try{r.setSelectionRange(t.start,t.end)}catch(e){}}r.focus(i)}}f.classList.remove(z.config.swappingClass);Y(n.elts,function(e){if(e.classList){e.classList.add(z.config.settlingClass)}ee(e,"htmx:afterSwap",l)});if(ar(u,/HX-Trigger-After-Swap:/i)){var a=s;if(!re(s)){a=$().body}qe(u,"HX-Trigger-After-Swap",a)}var o=function(){Y(n.tasks,function(e){e.call()});Y(n.elts,function(e){if(e.classList){e.classList.remove(z.config.settlingClass)}ee(e,"htmx:afterSettle",l)});if(h.type){if(h.type==="push"){Lt(h.path);ee($().body,"htmx:pushedIntoHistory",{path:h.path})}else{Ht(h.path);ee($().body,"htmx:replacedInHistory",{path:h.path})}}if(l.pathInfo.anchor){var e=C("#"+l.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title){var t=C("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}Kt(n.elts,c);if(ar(u,/HX-Trigger-After-Settle:/i)){var r=s;if(!re(s)){r=$().body}qe(u,"HX-Trigger-After-Settle",r)}};if(c.settleDelay>0){setTimeout(o,c.settleDelay)}else{o()}}catch(e){Q(s,"htmx:swapError",l);throw e}};if(c.swapDelay>0){setTimeout(o,c.swapDelay)}else{o()}}if(t){Q(s,"htmx:responseError",ne({error:"Response Status Error Code "+u.status+" from "+l.pathInfo.requestPath},l))}}var cr={};function hr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function dr(e,t){if(t.init){t.init(r)}cr[e]=ne(hr(),t)}function vr(e){delete cr[e]}function gr(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=J(e,"hx-ext");if(t){Y(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=cr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return gr(u(e),r,n)}function pr(e){if($().readyState!=="loading"){e()}else{$().addEventListener("DOMContentLoaded",e)}}function mr(){if(z.config.includeIndicatorStyles!==false){$().head.insertAdjacentHTML("beforeend","")}}function yr(){var e=$().querySelector('meta[name="htmx-config"]');if(e){return w(e.content)}else{return null}}function xr(){var e=yr();if(e){z.config=ne(z.config,e)}}pr(function(){xr();mr();var e=$().body;mt(e);var t=$().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=K(t);if(r&&r.xhr){r.xhr.abort()}});window.onpopstate=function(e){if(e.state&&e.state.htmx){It();Y(t,function(e){ee(e,"htmx:restored",{document:$(),triggerEvent:ee})})}};setTimeout(function(){ee(e,"htmx:load",{})},0)});return z}()}); -------------------------------------------------------------------------------- /assets/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 67 | 68 |
69 | {{ block "main" . }}{{ end }} 70 |
71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /assets/monitors.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 | {{ range $key, $value := .EventGroups }} 4 |
7 |
{{ $key }}
8 |
9 | {{ range $value }} 10 | {{ if . }} 11 |
12 | {{ else }} 13 |
14 | {{ end }} 15 | {{ end }} 16 |
17 |
18 | {{ else }} 19 |
There are currently no events in your database.
20 | {{ end }} 21 |
22 | 23 |
24 | {{ .LastHours }} hours displayed 25 |
26 | 27 | 28 |
29 |
30 | curl -X POST http://tevents/.monitor?origin=cron:example
32 |
33 | {{ end }} 34 | -------------------------------------------------------------------------------- /assets/output.css: -------------------------------------------------------------------------------- 1 | /* 2 | ! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com 3 | */ 4 | 5 | /* 6 | 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 7 | 2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) 8 | */ 9 | 10 | *, 11 | ::before, 12 | ::after { 13 | box-sizing: border-box; 14 | /* 1 */ 15 | border-width: 0; 16 | /* 2 */ 17 | border-style: solid; 18 | /* 2 */ 19 | border-color: #e5e7eb; 20 | /* 2 */ 21 | } 22 | 23 | ::before, 24 | ::after { 25 | --tw-content: ''; 26 | } 27 | 28 | /* 29 | 1. Use a consistent sensible line-height in all browsers. 30 | 2. Prevent adjustments of font size after orientation changes in iOS. 31 | 3. Use a more readable tab size. 32 | 4. Use the user's configured `sans` font-family by default. 33 | 5. Use the user's configured `sans` font-feature-settings by default. 34 | */ 35 | 36 | html { 37 | line-height: 1.5; 38 | /* 1 */ 39 | -webkit-text-size-adjust: 100%; 40 | /* 2 */ 41 | -moz-tab-size: 4; 42 | /* 3 */ 43 | -o-tab-size: 4; 44 | tab-size: 4; 45 | /* 3 */ 46 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 47 | /* 4 */ 48 | font-feature-settings: normal; 49 | /* 5 */ 50 | } 51 | 52 | /* 53 | 1. Remove the margin in all browsers. 54 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 55 | */ 56 | 57 | body { 58 | margin: 0; 59 | /* 1 */ 60 | line-height: inherit; 61 | /* 2 */ 62 | } 63 | 64 | /* 65 | 1. Add the correct height in Firefox. 66 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 67 | 3. Ensure horizontal rules are visible by default. 68 | */ 69 | 70 | hr { 71 | height: 0; 72 | /* 1 */ 73 | color: inherit; 74 | /* 2 */ 75 | border-top-width: 1px; 76 | /* 3 */ 77 | } 78 | 79 | /* 80 | Add the correct text decoration in Chrome, Edge, and Safari. 81 | */ 82 | 83 | abbr:where([title]) { 84 | -webkit-text-decoration: underline dotted; 85 | text-decoration: underline dotted; 86 | } 87 | 88 | /* 89 | Remove the default font size and weight for headings. 90 | */ 91 | 92 | h1, 93 | h2, 94 | h3, 95 | h4, 96 | h5, 97 | h6 { 98 | font-size: inherit; 99 | font-weight: inherit; 100 | } 101 | 102 | /* 103 | Reset links to optimize for opt-in styling instead of opt-out. 104 | */ 105 | 106 | a { 107 | color: inherit; 108 | text-decoration: inherit; 109 | } 110 | 111 | /* 112 | Add the correct font weight in Edge and Safari. 113 | */ 114 | 115 | b, 116 | strong { 117 | font-weight: bolder; 118 | } 119 | 120 | /* 121 | 1. Use the user's configured `mono` font family by default. 122 | 2. Correct the odd `em` font sizing in all browsers. 123 | */ 124 | 125 | code, 126 | kbd, 127 | samp, 128 | pre { 129 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 130 | /* 1 */ 131 | font-size: 1em; 132 | /* 2 */ 133 | } 134 | 135 | /* 136 | Add the correct font size in all browsers. 137 | */ 138 | 139 | small { 140 | font-size: 80%; 141 | } 142 | 143 | /* 144 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 145 | */ 146 | 147 | sub, 148 | sup { 149 | font-size: 75%; 150 | line-height: 0; 151 | position: relative; 152 | vertical-align: baseline; 153 | } 154 | 155 | sub { 156 | bottom: -0.25em; 157 | } 158 | 159 | sup { 160 | top: -0.5em; 161 | } 162 | 163 | /* 164 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 165 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 166 | 3. Remove gaps between table borders by default. 167 | */ 168 | 169 | table { 170 | text-indent: 0; 171 | /* 1 */ 172 | border-color: inherit; 173 | /* 2 */ 174 | border-collapse: collapse; 175 | /* 3 */ 176 | } 177 | 178 | /* 179 | 1. Change the font styles in all browsers. 180 | 2. Remove the margin in Firefox and Safari. 181 | 3. Remove default padding in all browsers. 182 | */ 183 | 184 | button, 185 | input, 186 | optgroup, 187 | select, 188 | textarea { 189 | font-family: inherit; 190 | /* 1 */ 191 | font-size: 100%; 192 | /* 1 */ 193 | font-weight: inherit; 194 | /* 1 */ 195 | line-height: inherit; 196 | /* 1 */ 197 | color: inherit; 198 | /* 1 */ 199 | margin: 0; 200 | /* 2 */ 201 | padding: 0; 202 | /* 3 */ 203 | } 204 | 205 | /* 206 | Remove the inheritance of text transform in Edge and Firefox. 207 | */ 208 | 209 | button, 210 | select { 211 | text-transform: none; 212 | } 213 | 214 | /* 215 | 1. Correct the inability to style clickable types in iOS and Safari. 216 | 2. Remove default button styles. 217 | */ 218 | 219 | button, 220 | [type='button'], 221 | [type='reset'], 222 | [type='submit'] { 223 | -webkit-appearance: button; 224 | /* 1 */ 225 | background-color: transparent; 226 | /* 2 */ 227 | background-image: none; 228 | /* 2 */ 229 | } 230 | 231 | /* 232 | Use the modern Firefox focus style for all focusable elements. 233 | */ 234 | 235 | :-moz-focusring { 236 | outline: auto; 237 | } 238 | 239 | /* 240 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 241 | */ 242 | 243 | :-moz-ui-invalid { 244 | box-shadow: none; 245 | } 246 | 247 | /* 248 | Add the correct vertical alignment in Chrome and Firefox. 249 | */ 250 | 251 | progress { 252 | vertical-align: baseline; 253 | } 254 | 255 | /* 256 | Correct the cursor style of increment and decrement buttons in Safari. 257 | */ 258 | 259 | ::-webkit-inner-spin-button, 260 | ::-webkit-outer-spin-button { 261 | height: auto; 262 | } 263 | 264 | /* 265 | 1. Correct the odd appearance in Chrome and Safari. 266 | 2. Correct the outline style in Safari. 267 | */ 268 | 269 | [type='search'] { 270 | -webkit-appearance: textfield; 271 | /* 1 */ 272 | outline-offset: -2px; 273 | /* 2 */ 274 | } 275 | 276 | /* 277 | Remove the inner padding in Chrome and Safari on macOS. 278 | */ 279 | 280 | ::-webkit-search-decoration { 281 | -webkit-appearance: none; 282 | } 283 | 284 | /* 285 | 1. Correct the inability to style clickable types in iOS and Safari. 286 | 2. Change font properties to `inherit` in Safari. 287 | */ 288 | 289 | ::-webkit-file-upload-button { 290 | -webkit-appearance: button; 291 | /* 1 */ 292 | font: inherit; 293 | /* 2 */ 294 | } 295 | 296 | /* 297 | Add the correct display in Chrome and Safari. 298 | */ 299 | 300 | summary { 301 | display: list-item; 302 | } 303 | 304 | /* 305 | Removes the default spacing and border for appropriate elements. 306 | */ 307 | 308 | blockquote, 309 | dl, 310 | dd, 311 | h1, 312 | h2, 313 | h3, 314 | h4, 315 | h5, 316 | h6, 317 | hr, 318 | figure, 319 | p, 320 | pre { 321 | margin: 0; 322 | } 323 | 324 | fieldset { 325 | margin: 0; 326 | padding: 0; 327 | } 328 | 329 | legend { 330 | padding: 0; 331 | } 332 | 333 | ol, 334 | ul, 335 | menu { 336 | list-style: none; 337 | margin: 0; 338 | padding: 0; 339 | } 340 | 341 | /* 342 | Prevent resizing textareas horizontally by default. 343 | */ 344 | 345 | textarea { 346 | resize: vertical; 347 | } 348 | 349 | /* 350 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 351 | 2. Set the default placeholder color to the user's configured gray 400 color. 352 | */ 353 | 354 | input::-moz-placeholder, textarea::-moz-placeholder { 355 | opacity: 1; 356 | /* 1 */ 357 | color: #9ca3af; 358 | /* 2 */ 359 | } 360 | 361 | input::placeholder, 362 | textarea::placeholder { 363 | opacity: 1; 364 | /* 1 */ 365 | color: #9ca3af; 366 | /* 2 */ 367 | } 368 | 369 | /* 370 | Set the default cursor for buttons. 371 | */ 372 | 373 | button, 374 | [role="button"] { 375 | cursor: pointer; 376 | } 377 | 378 | /* 379 | Make sure disabled buttons don't get the pointer cursor. 380 | */ 381 | 382 | :disabled { 383 | cursor: default; 384 | } 385 | 386 | /* 387 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 388 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 389 | This can trigger a poorly considered lint error in some tools but is included by design. 390 | */ 391 | 392 | img, 393 | svg, 394 | video, 395 | canvas, 396 | audio, 397 | iframe, 398 | embed, 399 | object { 400 | display: block; 401 | /* 1 */ 402 | vertical-align: middle; 403 | /* 2 */ 404 | } 405 | 406 | /* 407 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 408 | */ 409 | 410 | img, 411 | video { 412 | max-width: 100%; 413 | height: auto; 414 | } 415 | 416 | /* Make elements with the HTML hidden attribute stay hidden by default */ 417 | 418 | [hidden] { 419 | display: none; 420 | } 421 | 422 | *, ::before, ::after { 423 | --tw-border-spacing-x: 0; 424 | --tw-border-spacing-y: 0; 425 | --tw-translate-x: 0; 426 | --tw-translate-y: 0; 427 | --tw-rotate: 0; 428 | --tw-skew-x: 0; 429 | --tw-skew-y: 0; 430 | --tw-scale-x: 1; 431 | --tw-scale-y: 1; 432 | --tw-pan-x: ; 433 | --tw-pan-y: ; 434 | --tw-pinch-zoom: ; 435 | --tw-scroll-snap-strictness: proximity; 436 | --tw-ordinal: ; 437 | --tw-slashed-zero: ; 438 | --tw-numeric-figure: ; 439 | --tw-numeric-spacing: ; 440 | --tw-numeric-fraction: ; 441 | --tw-ring-inset: ; 442 | --tw-ring-offset-width: 0px; 443 | --tw-ring-offset-color: #fff; 444 | --tw-ring-color: rgb(59 130 246 / 0.5); 445 | --tw-ring-offset-shadow: 0 0 #0000; 446 | --tw-ring-shadow: 0 0 #0000; 447 | --tw-shadow: 0 0 #0000; 448 | --tw-shadow-colored: 0 0 #0000; 449 | --tw-blur: ; 450 | --tw-brightness: ; 451 | --tw-contrast: ; 452 | --tw-grayscale: ; 453 | --tw-hue-rotate: ; 454 | --tw-invert: ; 455 | --tw-saturate: ; 456 | --tw-sepia: ; 457 | --tw-drop-shadow: ; 458 | --tw-backdrop-blur: ; 459 | --tw-backdrop-brightness: ; 460 | --tw-backdrop-contrast: ; 461 | --tw-backdrop-grayscale: ; 462 | --tw-backdrop-hue-rotate: ; 463 | --tw-backdrop-invert: ; 464 | --tw-backdrop-opacity: ; 465 | --tw-backdrop-saturate: ; 466 | --tw-backdrop-sepia: ; 467 | } 468 | 469 | ::backdrop { 470 | --tw-border-spacing-x: 0; 471 | --tw-border-spacing-y: 0; 472 | --tw-translate-x: 0; 473 | --tw-translate-y: 0; 474 | --tw-rotate: 0; 475 | --tw-skew-x: 0; 476 | --tw-skew-y: 0; 477 | --tw-scale-x: 1; 478 | --tw-scale-y: 1; 479 | --tw-pan-x: ; 480 | --tw-pan-y: ; 481 | --tw-pinch-zoom: ; 482 | --tw-scroll-snap-strictness: proximity; 483 | --tw-ordinal: ; 484 | --tw-slashed-zero: ; 485 | --tw-numeric-figure: ; 486 | --tw-numeric-spacing: ; 487 | --tw-numeric-fraction: ; 488 | --tw-ring-inset: ; 489 | --tw-ring-offset-width: 0px; 490 | --tw-ring-offset-color: #fff; 491 | --tw-ring-color: rgb(59 130 246 / 0.5); 492 | --tw-ring-offset-shadow: 0 0 #0000; 493 | --tw-ring-shadow: 0 0 #0000; 494 | --tw-shadow: 0 0 #0000; 495 | --tw-shadow-colored: 0 0 #0000; 496 | --tw-blur: ; 497 | --tw-brightness: ; 498 | --tw-contrast: ; 499 | --tw-grayscale: ; 500 | --tw-hue-rotate: ; 501 | --tw-invert: ; 502 | --tw-saturate: ; 503 | --tw-sepia: ; 504 | --tw-drop-shadow: ; 505 | --tw-backdrop-blur: ; 506 | --tw-backdrop-brightness: ; 507 | --tw-backdrop-contrast: ; 508 | --tw-backdrop-grayscale: ; 509 | --tw-backdrop-hue-rotate: ; 510 | --tw-backdrop-invert: ; 511 | --tw-backdrop-opacity: ; 512 | --tw-backdrop-saturate: ; 513 | --tw-backdrop-sepia: ; 514 | } 515 | 516 | .m-5 { 517 | margin: 1.25rem; 518 | } 519 | 520 | .mt-1 { 521 | margin-top: 0.25rem; 522 | } 523 | 524 | .ml-6 { 525 | margin-left: 1.5rem; 526 | } 527 | 528 | .block { 529 | display: block; 530 | } 531 | 532 | .flex { 533 | display: flex; 534 | } 535 | 536 | .inline-flex { 537 | display: inline-flex; 538 | } 539 | 540 | .table { 541 | display: table; 542 | } 543 | 544 | .h-7 { 545 | height: 1.75rem; 546 | } 547 | 548 | .w-2 { 549 | width: 0.5rem; 550 | } 551 | 552 | .flex-row { 553 | flex-direction: row; 554 | } 555 | 556 | .flex-col { 557 | flex-direction: column; 558 | } 559 | 560 | .items-start { 561 | align-items: flex-start; 562 | } 563 | 564 | .items-center { 565 | align-items: center; 566 | } 567 | 568 | .justify-center { 569 | justify-content: center; 570 | } 571 | 572 | .justify-between { 573 | justify-content: space-between; 574 | } 575 | 576 | .space-x-3 > :not([hidden]) ~ :not([hidden]) { 577 | --tw-space-x-reverse: 0; 578 | margin-right: calc(0.75rem * var(--tw-space-x-reverse)); 579 | margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); 580 | } 581 | 582 | .space-x-5 > :not([hidden]) ~ :not([hidden]) { 583 | --tw-space-x-reverse: 0; 584 | margin-right: calc(1.25rem * var(--tw-space-x-reverse)); 585 | margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); 586 | } 587 | 588 | .space-x-1 > :not([hidden]) ~ :not([hidden]) { 589 | --tw-space-x-reverse: 0; 590 | margin-right: calc(0.25rem * var(--tw-space-x-reverse)); 591 | margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); 592 | } 593 | 594 | .space-x-0 > :not([hidden]) ~ :not([hidden]) { 595 | --tw-space-x-reverse: 0; 596 | margin-right: calc(0px * var(--tw-space-x-reverse)); 597 | margin-left: calc(0px * calc(1 - var(--tw-space-x-reverse))); 598 | } 599 | 600 | .whitespace-nowrap { 601 | white-space: nowrap; 602 | } 603 | 604 | .rounded-lg { 605 | border-radius: 0.5rem; 606 | } 607 | 608 | .rounded-md { 609 | border-radius: 0.375rem; 610 | } 611 | 612 | .rounded { 613 | border-radius: 0.25rem; 614 | } 615 | 616 | .border { 617 | border-width: 1px; 618 | } 619 | 620 | .border-b { 621 | border-bottom-width: 1px; 622 | } 623 | 624 | .border-transparent { 625 | border-color: transparent; 626 | } 627 | 628 | .border-gray-100 { 629 | --tw-border-opacity: 1; 630 | border-color: rgb(243 244 246 / var(--tw-border-opacity)); 631 | } 632 | 633 | .bg-gray-100 { 634 | --tw-bg-opacity: 1; 635 | background-color: rgb(243 244 246 / var(--tw-bg-opacity)); 636 | } 637 | 638 | .bg-white { 639 | --tw-bg-opacity: 1; 640 | background-color: rgb(255 255 255 / var(--tw-bg-opacity)); 641 | } 642 | 643 | .bg-slate-100 { 644 | --tw-bg-opacity: 1; 645 | background-color: rgb(241 245 249 / var(--tw-bg-opacity)); 646 | } 647 | 648 | .bg-rose-600 { 649 | --tw-bg-opacity: 1; 650 | background-color: rgb(225 29 72 / var(--tw-bg-opacity)); 651 | } 652 | 653 | .bg-green-600 { 654 | --tw-bg-opacity: 1; 655 | background-color: rgb(22 163 74 / var(--tw-bg-opacity)); 656 | } 657 | 658 | .bg-gray-400 { 659 | --tw-bg-opacity: 1; 660 | background-color: rgb(156 163 175 / var(--tw-bg-opacity)); 661 | } 662 | 663 | .p-1 { 664 | padding: 0.25rem; 665 | } 666 | 667 | .p-0\.5 { 668 | padding: 0.125rem; 669 | } 670 | 671 | .p-0 { 672 | padding: 0px; 673 | } 674 | 675 | .py-2 { 676 | padding-top: 0.5rem; 677 | padding-bottom: 0.5rem; 678 | } 679 | 680 | .px-4 { 681 | padding-left: 1rem; 682 | padding-right: 1rem; 683 | } 684 | 685 | .py-\[0\.4375rem\] { 686 | padding-top: 0.4375rem; 687 | padding-bottom: 0.4375rem; 688 | } 689 | 690 | .px-2 { 691 | padding-left: 0.5rem; 692 | padding-right: 0.5rem; 693 | } 694 | 695 | .py-1 { 696 | padding-top: 0.25rem; 697 | padding-bottom: 0.25rem; 698 | } 699 | 700 | .pl-2 { 701 | padding-left: 0.5rem; 702 | } 703 | 704 | .pr-2 { 705 | padding-right: 0.5rem; 706 | } 707 | 708 | .pt-3 { 709 | padding-top: 0.75rem; 710 | } 711 | 712 | .text-center { 713 | text-align: center; 714 | } 715 | 716 | .font-mono { 717 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 718 | } 719 | 720 | .text-sm { 721 | font-size: 0.875rem; 722 | line-height: 1.25rem; 723 | } 724 | 725 | .text-xs { 726 | font-size: 0.75rem; 727 | line-height: 1rem; 728 | } 729 | 730 | .font-medium { 731 | font-weight: 500; 732 | } 733 | 734 | .font-semibold { 735 | font-weight: 600; 736 | } 737 | 738 | .font-extralight { 739 | font-weight: 200; 740 | } 741 | 742 | .text-gray-500 { 743 | --tw-text-opacity: 1; 744 | color: rgb(107 114 128 / var(--tw-text-opacity)); 745 | } 746 | 747 | .text-slate-900 { 748 | --tw-text-opacity: 1; 749 | color: rgb(15 23 42 / var(--tw-text-opacity)); 750 | } 751 | 752 | .text-slate-600 { 753 | --tw-text-opacity: 1; 754 | color: rgb(71 85 105 / var(--tw-text-opacity)); 755 | } 756 | 757 | .text-white { 758 | --tw-text-opacity: 1; 759 | color: rgb(255 255 255 / var(--tw-text-opacity)); 760 | } 761 | 762 | .text-gray-400 { 763 | --tw-text-opacity: 1; 764 | color: rgb(156 163 175 / var(--tw-text-opacity)); 765 | } 766 | 767 | .text-gray-600 { 768 | --tw-text-opacity: 1; 769 | color: rgb(75 85 99 / var(--tw-text-opacity)); 770 | } 771 | 772 | .shadow { 773 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); 774 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); 775 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 776 | } 777 | 778 | .shadow-sm { 779 | --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); 780 | --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); 781 | box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); 782 | } 783 | 784 | .transition { 785 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; 786 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; 787 | transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; 788 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 789 | transition-duration: 150ms; 790 | } 791 | 792 | .hover\:bg-rose-700:hover { 793 | --tw-bg-opacity: 1; 794 | background-color: rgb(190 18 60 / var(--tw-bg-opacity)); 795 | } 796 | 797 | .focus\:outline-none:focus { 798 | outline: 2px solid transparent; 799 | outline-offset: 2px; 800 | } 801 | 802 | .focus\:ring-2:focus { 803 | --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); 804 | --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); 805 | box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); 806 | } 807 | 808 | .focus\:ring-rose-500:focus { 809 | --tw-ring-opacity: 1; 810 | --tw-ring-color: rgb(244 63 94 / var(--tw-ring-opacity)); 811 | } 812 | 813 | .focus\:ring-offset-2:focus { 814 | --tw-ring-offset-width: 2px; 815 | } 816 | 817 | @media (min-width: 640px) { 818 | .sm\:flex-row { 819 | flex-direction: row; 820 | } 821 | 822 | .sm\:space-x-5 > :not([hidden]) ~ :not([hidden]) { 823 | --tw-space-x-reverse: 0; 824 | margin-right: calc(1.25rem * var(--tw-space-x-reverse)); 825 | margin-left: calc(1.25rem * calc(1 - var(--tw-space-x-reverse))); 826 | } 827 | } 828 | 829 | @media (min-width: 1024px) { 830 | .lg\:ml-2 { 831 | margin-left: 0.5rem; 832 | } 833 | 834 | .lg\:pr-3 { 835 | padding-right: 0.75rem; 836 | } 837 | } 838 | -------------------------------------------------------------------------------- /assets/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "assets", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "tailwindcss": "^3.2.4" 9 | } 10 | }, 11 | "node_modules/@nodelib/fs.scandir": { 12 | "version": "2.1.5", 13 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 14 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 15 | "dev": true, 16 | "dependencies": { 17 | "@nodelib/fs.stat": "2.0.5", 18 | "run-parallel": "^1.1.9" 19 | }, 20 | "engines": { 21 | "node": ">= 8" 22 | } 23 | }, 24 | "node_modules/@nodelib/fs.stat": { 25 | "version": "2.0.5", 26 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 27 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 28 | "dev": true, 29 | "engines": { 30 | "node": ">= 8" 31 | } 32 | }, 33 | "node_modules/@nodelib/fs.walk": { 34 | "version": "1.2.8", 35 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 36 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 37 | "dev": true, 38 | "dependencies": { 39 | "@nodelib/fs.scandir": "2.1.5", 40 | "fastq": "^1.6.0" 41 | }, 42 | "engines": { 43 | "node": ">= 8" 44 | } 45 | }, 46 | "node_modules/acorn": { 47 | "version": "7.4.1", 48 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 49 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 50 | "dev": true, 51 | "bin": { 52 | "acorn": "bin/acorn" 53 | }, 54 | "engines": { 55 | "node": ">=0.4.0" 56 | } 57 | }, 58 | "node_modules/acorn-node": { 59 | "version": "1.8.2", 60 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 61 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 62 | "dev": true, 63 | "dependencies": { 64 | "acorn": "^7.0.0", 65 | "acorn-walk": "^7.0.0", 66 | "xtend": "^4.0.2" 67 | } 68 | }, 69 | "node_modules/acorn-walk": { 70 | "version": "7.2.0", 71 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 72 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", 73 | "dev": true, 74 | "engines": { 75 | "node": ">=0.4.0" 76 | } 77 | }, 78 | "node_modules/anymatch": { 79 | "version": "3.1.3", 80 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 81 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 82 | "dev": true, 83 | "dependencies": { 84 | "normalize-path": "^3.0.0", 85 | "picomatch": "^2.0.4" 86 | }, 87 | "engines": { 88 | "node": ">= 8" 89 | } 90 | }, 91 | "node_modules/arg": { 92 | "version": "5.0.2", 93 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 94 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 95 | "dev": true 96 | }, 97 | "node_modules/binary-extensions": { 98 | "version": "2.2.0", 99 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 100 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 101 | "dev": true, 102 | "engines": { 103 | "node": ">=8" 104 | } 105 | }, 106 | "node_modules/braces": { 107 | "version": "3.0.2", 108 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 109 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 110 | "dev": true, 111 | "dependencies": { 112 | "fill-range": "^7.0.1" 113 | }, 114 | "engines": { 115 | "node": ">=8" 116 | } 117 | }, 118 | "node_modules/camelcase-css": { 119 | "version": "2.0.1", 120 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 121 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 122 | "dev": true, 123 | "engines": { 124 | "node": ">= 6" 125 | } 126 | }, 127 | "node_modules/chokidar": { 128 | "version": "3.5.3", 129 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 130 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 131 | "dev": true, 132 | "funding": [ 133 | { 134 | "type": "individual", 135 | "url": "https://paulmillr.com/funding/" 136 | } 137 | ], 138 | "dependencies": { 139 | "anymatch": "~3.1.2", 140 | "braces": "~3.0.2", 141 | "glob-parent": "~5.1.2", 142 | "is-binary-path": "~2.1.0", 143 | "is-glob": "~4.0.1", 144 | "normalize-path": "~3.0.0", 145 | "readdirp": "~3.6.0" 146 | }, 147 | "engines": { 148 | "node": ">= 8.10.0" 149 | }, 150 | "optionalDependencies": { 151 | "fsevents": "~2.3.2" 152 | } 153 | }, 154 | "node_modules/chokidar/node_modules/glob-parent": { 155 | "version": "5.1.2", 156 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 157 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 158 | "dev": true, 159 | "dependencies": { 160 | "is-glob": "^4.0.1" 161 | }, 162 | "engines": { 163 | "node": ">= 6" 164 | } 165 | }, 166 | "node_modules/color-name": { 167 | "version": "1.1.4", 168 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 169 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 170 | "dev": true 171 | }, 172 | "node_modules/cssesc": { 173 | "version": "3.0.0", 174 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 175 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 176 | "dev": true, 177 | "bin": { 178 | "cssesc": "bin/cssesc" 179 | }, 180 | "engines": { 181 | "node": ">=4" 182 | } 183 | }, 184 | "node_modules/defined": { 185 | "version": "1.0.1", 186 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 187 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", 188 | "dev": true, 189 | "funding": { 190 | "url": "https://github.com/sponsors/ljharb" 191 | } 192 | }, 193 | "node_modules/detective": { 194 | "version": "5.2.1", 195 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 196 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 197 | "dev": true, 198 | "dependencies": { 199 | "acorn-node": "^1.8.2", 200 | "defined": "^1.0.0", 201 | "minimist": "^1.2.6" 202 | }, 203 | "bin": { 204 | "detective": "bin/detective.js" 205 | }, 206 | "engines": { 207 | "node": ">=0.8.0" 208 | } 209 | }, 210 | "node_modules/didyoumean": { 211 | "version": "1.2.2", 212 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 213 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 214 | "dev": true 215 | }, 216 | "node_modules/dlv": { 217 | "version": "1.1.3", 218 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 219 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 220 | "dev": true 221 | }, 222 | "node_modules/fast-glob": { 223 | "version": "3.2.12", 224 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 225 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 226 | "dev": true, 227 | "dependencies": { 228 | "@nodelib/fs.stat": "^2.0.2", 229 | "@nodelib/fs.walk": "^1.2.3", 230 | "glob-parent": "^5.1.2", 231 | "merge2": "^1.3.0", 232 | "micromatch": "^4.0.4" 233 | }, 234 | "engines": { 235 | "node": ">=8.6.0" 236 | } 237 | }, 238 | "node_modules/fast-glob/node_modules/glob-parent": { 239 | "version": "5.1.2", 240 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 241 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 242 | "dev": true, 243 | "dependencies": { 244 | "is-glob": "^4.0.1" 245 | }, 246 | "engines": { 247 | "node": ">= 6" 248 | } 249 | }, 250 | "node_modules/fastq": { 251 | "version": "1.15.0", 252 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 253 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 254 | "dev": true, 255 | "dependencies": { 256 | "reusify": "^1.0.4" 257 | } 258 | }, 259 | "node_modules/fill-range": { 260 | "version": "7.0.1", 261 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 262 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 263 | "dev": true, 264 | "dependencies": { 265 | "to-regex-range": "^5.0.1" 266 | }, 267 | "engines": { 268 | "node": ">=8" 269 | } 270 | }, 271 | "node_modules/fsevents": { 272 | "version": "2.3.2", 273 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 274 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 275 | "dev": true, 276 | "hasInstallScript": true, 277 | "optional": true, 278 | "os": [ 279 | "darwin" 280 | ], 281 | "engines": { 282 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 283 | } 284 | }, 285 | "node_modules/function-bind": { 286 | "version": "1.1.1", 287 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 288 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 289 | "dev": true 290 | }, 291 | "node_modules/glob-parent": { 292 | "version": "6.0.2", 293 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 294 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 295 | "dev": true, 296 | "dependencies": { 297 | "is-glob": "^4.0.3" 298 | }, 299 | "engines": { 300 | "node": ">=10.13.0" 301 | } 302 | }, 303 | "node_modules/has": { 304 | "version": "1.0.3", 305 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 306 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 307 | "dev": true, 308 | "dependencies": { 309 | "function-bind": "^1.1.1" 310 | }, 311 | "engines": { 312 | "node": ">= 0.4.0" 313 | } 314 | }, 315 | "node_modules/is-binary-path": { 316 | "version": "2.1.0", 317 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 318 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 319 | "dev": true, 320 | "dependencies": { 321 | "binary-extensions": "^2.0.0" 322 | }, 323 | "engines": { 324 | "node": ">=8" 325 | } 326 | }, 327 | "node_modules/is-core-module": { 328 | "version": "2.11.0", 329 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 330 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 331 | "dev": true, 332 | "dependencies": { 333 | "has": "^1.0.3" 334 | }, 335 | "funding": { 336 | "url": "https://github.com/sponsors/ljharb" 337 | } 338 | }, 339 | "node_modules/is-extglob": { 340 | "version": "2.1.1", 341 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 342 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 343 | "dev": true, 344 | "engines": { 345 | "node": ">=0.10.0" 346 | } 347 | }, 348 | "node_modules/is-glob": { 349 | "version": "4.0.3", 350 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 351 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 352 | "dev": true, 353 | "dependencies": { 354 | "is-extglob": "^2.1.1" 355 | }, 356 | "engines": { 357 | "node": ">=0.10.0" 358 | } 359 | }, 360 | "node_modules/is-number": { 361 | "version": "7.0.0", 362 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 363 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 364 | "dev": true, 365 | "engines": { 366 | "node": ">=0.12.0" 367 | } 368 | }, 369 | "node_modules/lilconfig": { 370 | "version": "2.0.6", 371 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 372 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 373 | "dev": true, 374 | "engines": { 375 | "node": ">=10" 376 | } 377 | }, 378 | "node_modules/merge2": { 379 | "version": "1.4.1", 380 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 381 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 382 | "dev": true, 383 | "engines": { 384 | "node": ">= 8" 385 | } 386 | }, 387 | "node_modules/micromatch": { 388 | "version": "4.0.5", 389 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 390 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 391 | "dev": true, 392 | "dependencies": { 393 | "braces": "^3.0.2", 394 | "picomatch": "^2.3.1" 395 | }, 396 | "engines": { 397 | "node": ">=8.6" 398 | } 399 | }, 400 | "node_modules/minimist": { 401 | "version": "1.2.7", 402 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 403 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 404 | "dev": true, 405 | "funding": { 406 | "url": "https://github.com/sponsors/ljharb" 407 | } 408 | }, 409 | "node_modules/nanoid": { 410 | "version": "3.3.4", 411 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 412 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 413 | "dev": true, 414 | "bin": { 415 | "nanoid": "bin/nanoid.cjs" 416 | }, 417 | "engines": { 418 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 419 | } 420 | }, 421 | "node_modules/normalize-path": { 422 | "version": "3.0.0", 423 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 424 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 425 | "dev": true, 426 | "engines": { 427 | "node": ">=0.10.0" 428 | } 429 | }, 430 | "node_modules/object-hash": { 431 | "version": "3.0.0", 432 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 433 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 434 | "dev": true, 435 | "engines": { 436 | "node": ">= 6" 437 | } 438 | }, 439 | "node_modules/path-parse": { 440 | "version": "1.0.7", 441 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 442 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 443 | "dev": true 444 | }, 445 | "node_modules/picocolors": { 446 | "version": "1.0.0", 447 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 448 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 449 | "dev": true 450 | }, 451 | "node_modules/picomatch": { 452 | "version": "2.3.1", 453 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 454 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 455 | "dev": true, 456 | "engines": { 457 | "node": ">=8.6" 458 | }, 459 | "funding": { 460 | "url": "https://github.com/sponsors/jonschlinkert" 461 | } 462 | }, 463 | "node_modules/pify": { 464 | "version": "2.3.0", 465 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 466 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 467 | "dev": true, 468 | "engines": { 469 | "node": ">=0.10.0" 470 | } 471 | }, 472 | "node_modules/postcss": { 473 | "version": "8.4.21", 474 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", 475 | "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", 476 | "dev": true, 477 | "funding": [ 478 | { 479 | "type": "opencollective", 480 | "url": "https://opencollective.com/postcss/" 481 | }, 482 | { 483 | "type": "tidelift", 484 | "url": "https://tidelift.com/funding/github/npm/postcss" 485 | } 486 | ], 487 | "dependencies": { 488 | "nanoid": "^3.3.4", 489 | "picocolors": "^1.0.0", 490 | "source-map-js": "^1.0.2" 491 | }, 492 | "engines": { 493 | "node": "^10 || ^12 || >=14" 494 | } 495 | }, 496 | "node_modules/postcss-import": { 497 | "version": "14.1.0", 498 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 499 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 500 | "dev": true, 501 | "dependencies": { 502 | "postcss-value-parser": "^4.0.0", 503 | "read-cache": "^1.0.0", 504 | "resolve": "^1.1.7" 505 | }, 506 | "engines": { 507 | "node": ">=10.0.0" 508 | }, 509 | "peerDependencies": { 510 | "postcss": "^8.0.0" 511 | } 512 | }, 513 | "node_modules/postcss-js": { 514 | "version": "4.0.0", 515 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", 516 | "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", 517 | "dev": true, 518 | "dependencies": { 519 | "camelcase-css": "^2.0.1" 520 | }, 521 | "engines": { 522 | "node": "^12 || ^14 || >= 16" 523 | }, 524 | "funding": { 525 | "type": "opencollective", 526 | "url": "https://opencollective.com/postcss/" 527 | }, 528 | "peerDependencies": { 529 | "postcss": "^8.3.3" 530 | } 531 | }, 532 | "node_modules/postcss-load-config": { 533 | "version": "3.1.4", 534 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 535 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 536 | "dev": true, 537 | "dependencies": { 538 | "lilconfig": "^2.0.5", 539 | "yaml": "^1.10.2" 540 | }, 541 | "engines": { 542 | "node": ">= 10" 543 | }, 544 | "funding": { 545 | "type": "opencollective", 546 | "url": "https://opencollective.com/postcss/" 547 | }, 548 | "peerDependencies": { 549 | "postcss": ">=8.0.9", 550 | "ts-node": ">=9.0.0" 551 | }, 552 | "peerDependenciesMeta": { 553 | "postcss": { 554 | "optional": true 555 | }, 556 | "ts-node": { 557 | "optional": true 558 | } 559 | } 560 | }, 561 | "node_modules/postcss-nested": { 562 | "version": "6.0.0", 563 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 564 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 565 | "dev": true, 566 | "dependencies": { 567 | "postcss-selector-parser": "^6.0.10" 568 | }, 569 | "engines": { 570 | "node": ">=12.0" 571 | }, 572 | "funding": { 573 | "type": "opencollective", 574 | "url": "https://opencollective.com/postcss/" 575 | }, 576 | "peerDependencies": { 577 | "postcss": "^8.2.14" 578 | } 579 | }, 580 | "node_modules/postcss-selector-parser": { 581 | "version": "6.0.11", 582 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 583 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 584 | "dev": true, 585 | "dependencies": { 586 | "cssesc": "^3.0.0", 587 | "util-deprecate": "^1.0.2" 588 | }, 589 | "engines": { 590 | "node": ">=4" 591 | } 592 | }, 593 | "node_modules/postcss-value-parser": { 594 | "version": "4.2.0", 595 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 596 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 597 | "dev": true 598 | }, 599 | "node_modules/queue-microtask": { 600 | "version": "1.2.3", 601 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 602 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 603 | "dev": true, 604 | "funding": [ 605 | { 606 | "type": "github", 607 | "url": "https://github.com/sponsors/feross" 608 | }, 609 | { 610 | "type": "patreon", 611 | "url": "https://www.patreon.com/feross" 612 | }, 613 | { 614 | "type": "consulting", 615 | "url": "https://feross.org/support" 616 | } 617 | ] 618 | }, 619 | "node_modules/quick-lru": { 620 | "version": "5.1.1", 621 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 622 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 623 | "dev": true, 624 | "engines": { 625 | "node": ">=10" 626 | }, 627 | "funding": { 628 | "url": "https://github.com/sponsors/sindresorhus" 629 | } 630 | }, 631 | "node_modules/read-cache": { 632 | "version": "1.0.0", 633 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 634 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 635 | "dev": true, 636 | "dependencies": { 637 | "pify": "^2.3.0" 638 | } 639 | }, 640 | "node_modules/readdirp": { 641 | "version": "3.6.0", 642 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 643 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 644 | "dev": true, 645 | "dependencies": { 646 | "picomatch": "^2.2.1" 647 | }, 648 | "engines": { 649 | "node": ">=8.10.0" 650 | } 651 | }, 652 | "node_modules/resolve": { 653 | "version": "1.22.1", 654 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 655 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 656 | "dev": true, 657 | "dependencies": { 658 | "is-core-module": "^2.9.0", 659 | "path-parse": "^1.0.7", 660 | "supports-preserve-symlinks-flag": "^1.0.0" 661 | }, 662 | "bin": { 663 | "resolve": "bin/resolve" 664 | }, 665 | "funding": { 666 | "url": "https://github.com/sponsors/ljharb" 667 | } 668 | }, 669 | "node_modules/reusify": { 670 | "version": "1.0.4", 671 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 672 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 673 | "dev": true, 674 | "engines": { 675 | "iojs": ">=1.0.0", 676 | "node": ">=0.10.0" 677 | } 678 | }, 679 | "node_modules/run-parallel": { 680 | "version": "1.2.0", 681 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 682 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 683 | "dev": true, 684 | "funding": [ 685 | { 686 | "type": "github", 687 | "url": "https://github.com/sponsors/feross" 688 | }, 689 | { 690 | "type": "patreon", 691 | "url": "https://www.patreon.com/feross" 692 | }, 693 | { 694 | "type": "consulting", 695 | "url": "https://feross.org/support" 696 | } 697 | ], 698 | "dependencies": { 699 | "queue-microtask": "^1.2.2" 700 | } 701 | }, 702 | "node_modules/source-map-js": { 703 | "version": "1.0.2", 704 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 705 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 706 | "dev": true, 707 | "engines": { 708 | "node": ">=0.10.0" 709 | } 710 | }, 711 | "node_modules/supports-preserve-symlinks-flag": { 712 | "version": "1.0.0", 713 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 714 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 715 | "dev": true, 716 | "engines": { 717 | "node": ">= 0.4" 718 | }, 719 | "funding": { 720 | "url": "https://github.com/sponsors/ljharb" 721 | } 722 | }, 723 | "node_modules/tailwindcss": { 724 | "version": "3.2.4", 725 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", 726 | "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", 727 | "dev": true, 728 | "dependencies": { 729 | "arg": "^5.0.2", 730 | "chokidar": "^3.5.3", 731 | "color-name": "^1.1.4", 732 | "detective": "^5.2.1", 733 | "didyoumean": "^1.2.2", 734 | "dlv": "^1.1.3", 735 | "fast-glob": "^3.2.12", 736 | "glob-parent": "^6.0.2", 737 | "is-glob": "^4.0.3", 738 | "lilconfig": "^2.0.6", 739 | "micromatch": "^4.0.5", 740 | "normalize-path": "^3.0.0", 741 | "object-hash": "^3.0.0", 742 | "picocolors": "^1.0.0", 743 | "postcss": "^8.4.18", 744 | "postcss-import": "^14.1.0", 745 | "postcss-js": "^4.0.0", 746 | "postcss-load-config": "^3.1.4", 747 | "postcss-nested": "6.0.0", 748 | "postcss-selector-parser": "^6.0.10", 749 | "postcss-value-parser": "^4.2.0", 750 | "quick-lru": "^5.1.1", 751 | "resolve": "^1.22.1" 752 | }, 753 | "bin": { 754 | "tailwind": "lib/cli.js", 755 | "tailwindcss": "lib/cli.js" 756 | }, 757 | "engines": { 758 | "node": ">=12.13.0" 759 | }, 760 | "peerDependencies": { 761 | "postcss": "^8.0.9" 762 | } 763 | }, 764 | "node_modules/to-regex-range": { 765 | "version": "5.0.1", 766 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 767 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 768 | "dev": true, 769 | "dependencies": { 770 | "is-number": "^7.0.0" 771 | }, 772 | "engines": { 773 | "node": ">=8.0" 774 | } 775 | }, 776 | "node_modules/util-deprecate": { 777 | "version": "1.0.2", 778 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 779 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 780 | "dev": true 781 | }, 782 | "node_modules/xtend": { 783 | "version": "4.0.2", 784 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 785 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 786 | "dev": true, 787 | "engines": { 788 | "node": ">=0.4" 789 | } 790 | }, 791 | "node_modules/yaml": { 792 | "version": "1.10.2", 793 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 794 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 795 | "dev": true, 796 | "engines": { 797 | "node": ">= 6" 798 | } 799 | } 800 | }, 801 | "dependencies": { 802 | "@nodelib/fs.scandir": { 803 | "version": "2.1.5", 804 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 805 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 806 | "dev": true, 807 | "requires": { 808 | "@nodelib/fs.stat": "2.0.5", 809 | "run-parallel": "^1.1.9" 810 | } 811 | }, 812 | "@nodelib/fs.stat": { 813 | "version": "2.0.5", 814 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 815 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 816 | "dev": true 817 | }, 818 | "@nodelib/fs.walk": { 819 | "version": "1.2.8", 820 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 821 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 822 | "dev": true, 823 | "requires": { 824 | "@nodelib/fs.scandir": "2.1.5", 825 | "fastq": "^1.6.0" 826 | } 827 | }, 828 | "acorn": { 829 | "version": "7.4.1", 830 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", 831 | "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", 832 | "dev": true 833 | }, 834 | "acorn-node": { 835 | "version": "1.8.2", 836 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", 837 | "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", 838 | "dev": true, 839 | "requires": { 840 | "acorn": "^7.0.0", 841 | "acorn-walk": "^7.0.0", 842 | "xtend": "^4.0.2" 843 | } 844 | }, 845 | "acorn-walk": { 846 | "version": "7.2.0", 847 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", 848 | "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", 849 | "dev": true 850 | }, 851 | "anymatch": { 852 | "version": "3.1.3", 853 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 854 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 855 | "dev": true, 856 | "requires": { 857 | "normalize-path": "^3.0.0", 858 | "picomatch": "^2.0.4" 859 | } 860 | }, 861 | "arg": { 862 | "version": "5.0.2", 863 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 864 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 865 | "dev": true 866 | }, 867 | "binary-extensions": { 868 | "version": "2.2.0", 869 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 870 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 871 | "dev": true 872 | }, 873 | "braces": { 874 | "version": "3.0.2", 875 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 876 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 877 | "dev": true, 878 | "requires": { 879 | "fill-range": "^7.0.1" 880 | } 881 | }, 882 | "camelcase-css": { 883 | "version": "2.0.1", 884 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", 885 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", 886 | "dev": true 887 | }, 888 | "chokidar": { 889 | "version": "3.5.3", 890 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 891 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 892 | "dev": true, 893 | "requires": { 894 | "anymatch": "~3.1.2", 895 | "braces": "~3.0.2", 896 | "fsevents": "~2.3.2", 897 | "glob-parent": "~5.1.2", 898 | "is-binary-path": "~2.1.0", 899 | "is-glob": "~4.0.1", 900 | "normalize-path": "~3.0.0", 901 | "readdirp": "~3.6.0" 902 | }, 903 | "dependencies": { 904 | "glob-parent": { 905 | "version": "5.1.2", 906 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 907 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 908 | "dev": true, 909 | "requires": { 910 | "is-glob": "^4.0.1" 911 | } 912 | } 913 | } 914 | }, 915 | "color-name": { 916 | "version": "1.1.4", 917 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 918 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 919 | "dev": true 920 | }, 921 | "cssesc": { 922 | "version": "3.0.0", 923 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", 924 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", 925 | "dev": true 926 | }, 927 | "defined": { 928 | "version": "1.0.1", 929 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", 930 | "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", 931 | "dev": true 932 | }, 933 | "detective": { 934 | "version": "5.2.1", 935 | "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", 936 | "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", 937 | "dev": true, 938 | "requires": { 939 | "acorn-node": "^1.8.2", 940 | "defined": "^1.0.0", 941 | "minimist": "^1.2.6" 942 | } 943 | }, 944 | "didyoumean": { 945 | "version": "1.2.2", 946 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", 947 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", 948 | "dev": true 949 | }, 950 | "dlv": { 951 | "version": "1.1.3", 952 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", 953 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", 954 | "dev": true 955 | }, 956 | "fast-glob": { 957 | "version": "3.2.12", 958 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", 959 | "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", 960 | "dev": true, 961 | "requires": { 962 | "@nodelib/fs.stat": "^2.0.2", 963 | "@nodelib/fs.walk": "^1.2.3", 964 | "glob-parent": "^5.1.2", 965 | "merge2": "^1.3.0", 966 | "micromatch": "^4.0.4" 967 | }, 968 | "dependencies": { 969 | "glob-parent": { 970 | "version": "5.1.2", 971 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 972 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 973 | "dev": true, 974 | "requires": { 975 | "is-glob": "^4.0.1" 976 | } 977 | } 978 | } 979 | }, 980 | "fastq": { 981 | "version": "1.15.0", 982 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", 983 | "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", 984 | "dev": true, 985 | "requires": { 986 | "reusify": "^1.0.4" 987 | } 988 | }, 989 | "fill-range": { 990 | "version": "7.0.1", 991 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 992 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 993 | "dev": true, 994 | "requires": { 995 | "to-regex-range": "^5.0.1" 996 | } 997 | }, 998 | "fsevents": { 999 | "version": "2.3.2", 1000 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1001 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1002 | "dev": true, 1003 | "optional": true 1004 | }, 1005 | "function-bind": { 1006 | "version": "1.1.1", 1007 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1008 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1009 | "dev": true 1010 | }, 1011 | "glob-parent": { 1012 | "version": "6.0.2", 1013 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1014 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1015 | "dev": true, 1016 | "requires": { 1017 | "is-glob": "^4.0.3" 1018 | } 1019 | }, 1020 | "has": { 1021 | "version": "1.0.3", 1022 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1023 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1024 | "dev": true, 1025 | "requires": { 1026 | "function-bind": "^1.1.1" 1027 | } 1028 | }, 1029 | "is-binary-path": { 1030 | "version": "2.1.0", 1031 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1032 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1033 | "dev": true, 1034 | "requires": { 1035 | "binary-extensions": "^2.0.0" 1036 | } 1037 | }, 1038 | "is-core-module": { 1039 | "version": "2.11.0", 1040 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", 1041 | "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", 1042 | "dev": true, 1043 | "requires": { 1044 | "has": "^1.0.3" 1045 | } 1046 | }, 1047 | "is-extglob": { 1048 | "version": "2.1.1", 1049 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1050 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1051 | "dev": true 1052 | }, 1053 | "is-glob": { 1054 | "version": "4.0.3", 1055 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1056 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1057 | "dev": true, 1058 | "requires": { 1059 | "is-extglob": "^2.1.1" 1060 | } 1061 | }, 1062 | "is-number": { 1063 | "version": "7.0.0", 1064 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1065 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1066 | "dev": true 1067 | }, 1068 | "lilconfig": { 1069 | "version": "2.0.6", 1070 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", 1071 | "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", 1072 | "dev": true 1073 | }, 1074 | "merge2": { 1075 | "version": "1.4.1", 1076 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1077 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1078 | "dev": true 1079 | }, 1080 | "micromatch": { 1081 | "version": "4.0.5", 1082 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1083 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1084 | "dev": true, 1085 | "requires": { 1086 | "braces": "^3.0.2", 1087 | "picomatch": "^2.3.1" 1088 | } 1089 | }, 1090 | "minimist": { 1091 | "version": "1.2.7", 1092 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", 1093 | "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", 1094 | "dev": true 1095 | }, 1096 | "nanoid": { 1097 | "version": "3.3.4", 1098 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", 1099 | "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", 1100 | "dev": true 1101 | }, 1102 | "normalize-path": { 1103 | "version": "3.0.0", 1104 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1105 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1106 | "dev": true 1107 | }, 1108 | "object-hash": { 1109 | "version": "3.0.0", 1110 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", 1111 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", 1112 | "dev": true 1113 | }, 1114 | "path-parse": { 1115 | "version": "1.0.7", 1116 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1117 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1118 | "dev": true 1119 | }, 1120 | "picocolors": { 1121 | "version": "1.0.0", 1122 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1123 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1124 | "dev": true 1125 | }, 1126 | "picomatch": { 1127 | "version": "2.3.1", 1128 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1129 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1130 | "dev": true 1131 | }, 1132 | "pify": { 1133 | "version": "2.3.0", 1134 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1135 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 1136 | "dev": true 1137 | }, 1138 | "postcss": { 1139 | "version": "8.4.21", 1140 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", 1141 | "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", 1142 | "dev": true, 1143 | "requires": { 1144 | "nanoid": "^3.3.4", 1145 | "picocolors": "^1.0.0", 1146 | "source-map-js": "^1.0.2" 1147 | } 1148 | }, 1149 | "postcss-import": { 1150 | "version": "14.1.0", 1151 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", 1152 | "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", 1153 | "dev": true, 1154 | "requires": { 1155 | "postcss-value-parser": "^4.0.0", 1156 | "read-cache": "^1.0.0", 1157 | "resolve": "^1.1.7" 1158 | } 1159 | }, 1160 | "postcss-js": { 1161 | "version": "4.0.0", 1162 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", 1163 | "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", 1164 | "dev": true, 1165 | "requires": { 1166 | "camelcase-css": "^2.0.1" 1167 | } 1168 | }, 1169 | "postcss-load-config": { 1170 | "version": "3.1.4", 1171 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", 1172 | "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", 1173 | "dev": true, 1174 | "requires": { 1175 | "lilconfig": "^2.0.5", 1176 | "yaml": "^1.10.2" 1177 | } 1178 | }, 1179 | "postcss-nested": { 1180 | "version": "6.0.0", 1181 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", 1182 | "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", 1183 | "dev": true, 1184 | "requires": { 1185 | "postcss-selector-parser": "^6.0.10" 1186 | } 1187 | }, 1188 | "postcss-selector-parser": { 1189 | "version": "6.0.11", 1190 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", 1191 | "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", 1192 | "dev": true, 1193 | "requires": { 1194 | "cssesc": "^3.0.0", 1195 | "util-deprecate": "^1.0.2" 1196 | } 1197 | }, 1198 | "postcss-value-parser": { 1199 | "version": "4.2.0", 1200 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", 1201 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", 1202 | "dev": true 1203 | }, 1204 | "queue-microtask": { 1205 | "version": "1.2.3", 1206 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1207 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1208 | "dev": true 1209 | }, 1210 | "quick-lru": { 1211 | "version": "5.1.1", 1212 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 1213 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 1214 | "dev": true 1215 | }, 1216 | "read-cache": { 1217 | "version": "1.0.0", 1218 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", 1219 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", 1220 | "dev": true, 1221 | "requires": { 1222 | "pify": "^2.3.0" 1223 | } 1224 | }, 1225 | "readdirp": { 1226 | "version": "3.6.0", 1227 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1228 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1229 | "dev": true, 1230 | "requires": { 1231 | "picomatch": "^2.2.1" 1232 | } 1233 | }, 1234 | "resolve": { 1235 | "version": "1.22.1", 1236 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1237 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1238 | "dev": true, 1239 | "requires": { 1240 | "is-core-module": "^2.9.0", 1241 | "path-parse": "^1.0.7", 1242 | "supports-preserve-symlinks-flag": "^1.0.0" 1243 | } 1244 | }, 1245 | "reusify": { 1246 | "version": "1.0.4", 1247 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1248 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1249 | "dev": true 1250 | }, 1251 | "run-parallel": { 1252 | "version": "1.2.0", 1253 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1254 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1255 | "dev": true, 1256 | "requires": { 1257 | "queue-microtask": "^1.2.2" 1258 | } 1259 | }, 1260 | "source-map-js": { 1261 | "version": "1.0.2", 1262 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1263 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1264 | "dev": true 1265 | }, 1266 | "supports-preserve-symlinks-flag": { 1267 | "version": "1.0.0", 1268 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1269 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1270 | "dev": true 1271 | }, 1272 | "tailwindcss": { 1273 | "version": "3.2.4", 1274 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz", 1275 | "integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==", 1276 | "dev": true, 1277 | "requires": { 1278 | "arg": "^5.0.2", 1279 | "chokidar": "^3.5.3", 1280 | "color-name": "^1.1.4", 1281 | "detective": "^5.2.1", 1282 | "didyoumean": "^1.2.2", 1283 | "dlv": "^1.1.3", 1284 | "fast-glob": "^3.2.12", 1285 | "glob-parent": "^6.0.2", 1286 | "is-glob": "^4.0.3", 1287 | "lilconfig": "^2.0.6", 1288 | "micromatch": "^4.0.5", 1289 | "normalize-path": "^3.0.0", 1290 | "object-hash": "^3.0.0", 1291 | "picocolors": "^1.0.0", 1292 | "postcss": "^8.4.18", 1293 | "postcss-import": "^14.1.0", 1294 | "postcss-js": "^4.0.0", 1295 | "postcss-load-config": "^3.1.4", 1296 | "postcss-nested": "6.0.0", 1297 | "postcss-selector-parser": "^6.0.10", 1298 | "postcss-value-parser": "^4.2.0", 1299 | "quick-lru": "^5.1.1", 1300 | "resolve": "^1.22.1" 1301 | } 1302 | }, 1303 | "to-regex-range": { 1304 | "version": "5.0.1", 1305 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1306 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1307 | "dev": true, 1308 | "requires": { 1309 | "is-number": "^7.0.0" 1310 | } 1311 | }, 1312 | "util-deprecate": { 1313 | "version": "1.0.2", 1314 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1315 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 1316 | "dev": true 1317 | }, 1318 | "xtend": { 1319 | "version": "4.0.2", 1320 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1321 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 1322 | "dev": true 1323 | }, 1324 | "yaml": { 1325 | "version": "1.10.2", 1326 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 1327 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 1328 | "dev": true 1329 | } 1330 | } 1331 | } 1332 | -------------------------------------------------------------------------------- /assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "tailwindcss": "^3.2.4" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /assets/row.html: -------------------------------------------------------------------------------- 1 |
4 |
5 | {{ formatTime .CreatedAt }} 6 |
7 | 8 |
{{ .Origin }}:{{ .Owner }}
9 | 10 |
{{ .Body }}
11 |
12 | -------------------------------------------------------------------------------- /assets/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /assets/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./*.{html,js}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /cmd/tevents/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "flag" 6 | "log" 7 | "tevents" 8 | 9 | tdb "tevents/db" 10 | 11 | _ "modernc.org/sqlite" 12 | "tailscale.com/tsnet" 13 | ) 14 | 15 | var ( 16 | dburl = flag.String("dburl", "db.sqlite", "database url") 17 | hostname = flag.String("hostname", "tevents", "hostname for the tailnet") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | 23 | db, err := sql.Open("sqlite", *dburl) 24 | if err != nil { 25 | log.Fatal("cant connect to db", err) 26 | } 27 | 28 | if err := tdb.SetupSchema(db); err != nil { 29 | log.Fatal("cant setup schema", err) 30 | } 31 | 32 | ts := &tsnet.Server{ 33 | Hostname: *hostname, 34 | } 35 | 36 | defer ts.Close() 37 | 38 | ln, err := ts.Listen("tcp", ":80") 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | defer ln.Close() 44 | 45 | lc, err := ts.LocalClient() 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | s := tevents.NewServer(":8080", db, ln, lc) 51 | s.EventService = tdb.NewEventService(db) 52 | 53 | if err := s.Start(); err != nil { 54 | log.Fatal("http server failed:", err) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "database/sql" 5 | _ "embed" 6 | "strings" 7 | "tevents" 8 | ) 9 | 10 | //go:embed schema.sql 11 | var DBSchema string 12 | 13 | func SetupSchema(db *sql.DB) error { 14 | statements := strings.Split(DBSchema, ";") 15 | 16 | for _, stmt := range statements { 17 | if _, err := db.Exec(stmt); err != nil { 18 | return err 19 | } 20 | } 21 | 22 | return nil 23 | } 24 | 25 | type EventService struct { 26 | db *sql.DB 27 | } 28 | 29 | var _ tevents.EventService = (*EventService)(nil) 30 | 31 | func NewEventService(db *sql.DB) *EventService { 32 | return &EventService{db} 33 | } 34 | 35 | func (es *EventService) Insert(origin, event_type, body, owner string) error { 36 | _, err := es.db.Exec(`INSERT INTO events 37 | (origin, event_type, body, owner) 38 | VALUES (?, ?, ?, ?)`, origin, event_type, body, owner) 39 | return err 40 | } 41 | 42 | func (es *EventService) Find(event_type string) ([]*tevents.Event, error) { 43 | var events []*tevents.Event 44 | 45 | sql := `SELECT origin, event_type, body, owner, created_at FROM events WHERE event_type = ? ORDER BY created_at DESC` 46 | 47 | rows, err := es.db.Query(sql, event_type) 48 | if err != nil { 49 | return nil, err 50 | } 51 | for rows.Next() { 52 | var e tevents.Event 53 | err := rows.Scan(&e.Origin, &e.EventType, &e.Body, &e.Owner, &e.CreatedAt) 54 | if err != nil { 55 | return nil, err 56 | } 57 | events = append(events, &e) 58 | } 59 | return events, rows.Err() 60 | } 61 | 62 | func (es *EventService) ClearAll() error { 63 | _, err := es.db.Exec(`DELETE FROM events`) 64 | return err 65 | } 66 | -------------------------------------------------------------------------------- /db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS events ( 2 | id INTEGER PRIMARY KEY AUTOINCREMENT, 3 | origin TEXT, 4 | event_type TEXT, 5 | body TEXT, 6 | owner TEXT, 7 | 8 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 9 | ); 10 | 11 | CREATE INDEX IF NOT EXISTS events_origin ON events (origin); 12 | CREATE INDEX IF NOT EXISTS events_type ON events (event_type); 13 | CREATE INDEX IF NOT EXISTS events_owner ON events (owner); 14 | -------------------------------------------------------------------------------- /event.go: -------------------------------------------------------------------------------- 1 | package tevents 2 | 3 | import "time" 4 | 5 | type Event struct { 6 | Id int 7 | Origin string 8 | EventType string 9 | Body string 10 | Owner string 11 | CreatedAt time.Time 12 | } 13 | 14 | type EventService interface { 15 | Insert(origin, event_type, body, owner string) error 16 | Find(event_type string) ([]*Event, error) 17 | ClearAll() error 18 | } 19 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module tevents 2 | 3 | go 1.19 4 | 5 | require ( 6 | modernc.org/sqlite v1.20.3 7 | tailscale.com v1.36.1 8 | ) 9 | 10 | require ( 11 | filippo.io/edwards25519 v1.0.0-rc.1 // indirect 12 | github.com/Microsoft/go-winio v0.6.0 // indirect 13 | github.com/akutz/memconn v0.1.0 // indirect 14 | github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect 15 | github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect 16 | github.com/aws/aws-sdk-go-v2/config v1.11.0 // indirect 17 | github.com/aws/aws-sdk-go-v2/credentials v1.6.4 // indirect 18 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 // indirect 19 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect 20 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect 21 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 // indirect 22 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 // indirect 23 | github.com/aws/aws-sdk-go-v2/service/ssm v1.35.0 // indirect 24 | github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 // indirect 25 | github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 // indirect 26 | github.com/aws/smithy-go v1.13.5 // indirect 27 | github.com/coreos/go-iptables v0.6.0 // indirect 28 | github.com/dustin/go-humanize v1.0.0 // indirect 29 | github.com/fxamacker/cbor/v2 v2.4.0 // indirect 30 | github.com/go-ole/go-ole v1.2.6 // indirect 31 | github.com/godbus/dbus/v5 v5.0.6 // indirect 32 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 33 | github.com/google/btree v1.0.1 // indirect 34 | github.com/google/go-cmp v0.5.9 // indirect 35 | github.com/google/uuid v1.3.0 // indirect 36 | github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect 37 | github.com/illarion/gonotify v1.0.1 // indirect 38 | github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 // indirect 39 | github.com/jmespath/go-jmespath v0.4.0 // indirect 40 | github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531 // indirect 41 | github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect 42 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 43 | github.com/klauspost/compress v1.15.4 // indirect 44 | github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect 45 | github.com/mattn/go-isatty v0.0.16 // indirect 46 | github.com/mdlayher/genetlink v1.2.0 // indirect 47 | github.com/mdlayher/netlink v1.6.0 // indirect 48 | github.com/mdlayher/sdnotify v1.0.0 // indirect 49 | github.com/mdlayher/socket v0.2.3 // indirect 50 | github.com/mitchellh/go-ps v1.0.0 // indirect 51 | github.com/pkg/errors v0.9.1 // indirect 52 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect 53 | github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect 54 | github.com/tailscale/golang-x-crypto v0.0.0-20221102133106-bc99ab8c2d17 // indirect 55 | github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect 56 | github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect 57 | github.com/tailscale/wireguard-go v0.0.0-20221219190806-4fa124729667 // indirect 58 | github.com/tcnksm/go-httpstat v0.2.0 // indirect 59 | github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect 60 | github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 // indirect 61 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect 62 | github.com/x448/float16 v0.8.4 // indirect 63 | go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect 64 | go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf // indirect 65 | golang.org/x/crypto v0.3.0 // indirect 66 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect 67 | golang.org/x/mod v0.6.0 // indirect 68 | golang.org/x/net v0.5.0 // indirect 69 | golang.org/x/sync v0.1.0 // indirect 70 | golang.org/x/sys v0.4.0 // indirect 71 | golang.org/x/term v0.4.0 // indirect 72 | golang.org/x/text v0.6.0 // indirect 73 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect 74 | golang.org/x/tools v0.2.0 // indirect 75 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect 76 | golang.zx2c4.com/wireguard/windows v0.5.3 // indirect 77 | gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 // indirect 78 | inet.af/peercred v0.0.0-20210906144145-0893ea02156a // indirect 79 | lukechampine.com/uint128 v1.2.0 // indirect 80 | modernc.org/cc/v3 v3.40.0 // indirect 81 | modernc.org/ccgo/v3 v3.16.13 // indirect 82 | modernc.org/libc v1.22.2 // indirect 83 | modernc.org/mathutil v1.5.0 // indirect 84 | modernc.org/memory v1.4.0 // indirect 85 | modernc.org/opt v0.1.3 // indirect 86 | modernc.org/strutil v1.1.3 // indirect 87 | modernc.org/token v1.0.1 // indirect 88 | nhooyr.io/websocket v1.8.7 // indirect 89 | ) 90 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= 2 | filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= 3 | filippo.io/mkcert v1.4.3 h1:axpnmtrZMM8u5Hf4N3UXxboGemMOV+Tn+e+pkHM6E3o= 4 | github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= 5 | github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= 6 | github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= 7 | github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= 8 | github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= 9 | github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= 10 | github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= 11 | github.com/aws/aws-sdk-go-v2 v1.11.2/go.mod h1:SQfA+m2ltnu1cA0soUkj4dRSsmITiVQUJvBIZjzfPyQ= 12 | github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY= 13 | github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= 14 | github.com/aws/aws-sdk-go-v2/config v1.11.0 h1:Czlld5zBB61A3/aoegA9/buZulwL9mHHfizh/Oq+Kqs= 15 | github.com/aws/aws-sdk-go-v2/config v1.11.0/go.mod h1:VrQDJGFBM5yZe+IOeenNZ/DWoErdny+k2MHEIpwDsEY= 16 | github.com/aws/aws-sdk-go-v2/credentials v1.6.4 h1:2hvbUoHufns0lDIsaK8FVCMukT1WngtZPavN+W2FkSw= 17 | github.com/aws/aws-sdk-go-v2/credentials v1.6.4/go.mod h1:tTrhvBPHyPde4pdIPSba4Nv7RYr4wP9jxXEDa1bKn/8= 18 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2 h1:KiN5TPOLrEjbGCvdTQR4t0U4T87vVwALZ5Bg3jpMqPY= 19 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.8.2/go.mod h1:dF2F6tXEOgmW5X1ZFO/EPtWrcm7XkW07KNcJUGNtt4s= 20 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.2/go.mod h1:SgKKNBIoDC/E1ZCDhhMW3yalWjwuLjMcpLzsM/QQnWo= 21 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU= 22 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= 23 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.2/go.mod h1:xT4XX6w5Sa3dhg50JrYyy3e4WPYo/+WjY/BXtqXVunU= 24 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE= 25 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= 26 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2 h1:IQup8Q6lorXeiA/rK72PeToWoWK8h7VAPgHNWdSrtgE= 27 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.2/go.mod h1:VITe/MdW6EMXPb0o0txu/fsonXbMHUU2OC2Qp7ivU4o= 28 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2 h1:CKdUNKmuilw/KNmO2Q53Av8u+ZyXMC2M9aX8Z+c/gzg= 29 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.5.2/go.mod h1:FgR1tCsn8C6+Hf+N5qkfrE4IXvUL1RgW87sunJ+5J4I= 30 | github.com/aws/aws-sdk-go-v2/service/ssm v1.35.0 h1:QWCcOeLTrjvf7UdYIadzrhNH3PI6T9jXOV64Ez5YUgg= 31 | github.com/aws/aws-sdk-go-v2/service/ssm v1.35.0/go.mod h1:Hf7wSogKP1XCJ9GgW8erZDL6IZ1NLwLN7bYdV/Gn/LI= 32 | github.com/aws/aws-sdk-go-v2/service/sso v1.6.2 h1:2IDmvSb86KT44lSg1uU4ONpzgWLOuApRl6Tg54mZ6Dk= 33 | github.com/aws/aws-sdk-go-v2/service/sso v1.6.2/go.mod h1:KnIpszaIdwI33tmc/W/GGXyn22c1USYxA/2KyvoeDY0= 34 | github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 h1:QKR7wy5e650q70PFKMfGF9sTo0rZgUevSSJ4wxmyWXk= 35 | github.com/aws/aws-sdk-go-v2/service/sts v1.11.1/go.mod h1:UV2N5HaPfdbDpkgkz4sRzWCvQswZjdO1FfqCWl0t7RA= 36 | github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= 37 | github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= 38 | github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 39 | github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao= 40 | github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= 41 | github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= 42 | github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= 43 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 44 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 46 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 47 | github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= 48 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 49 | github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= 50 | github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= 51 | github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 52 | github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= 53 | github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= 54 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 55 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 56 | github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= 57 | github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 58 | github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= 59 | github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= 60 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 61 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 62 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 63 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 64 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 65 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 66 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 67 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 68 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 69 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= 70 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 71 | github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= 72 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 73 | github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= 74 | github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 75 | github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= 76 | github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 77 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 78 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 79 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 80 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 81 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 82 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 83 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 84 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 85 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 86 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 87 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 88 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 89 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 90 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 91 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 92 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 93 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 94 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 95 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 96 | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= 97 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 98 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 99 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 100 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 101 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 102 | github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU= 103 | github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE= 104 | github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= 105 | github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio= 106 | github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE= 107 | github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg= 108 | github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE= 109 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 110 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 111 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 112 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 113 | github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 114 | github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531 h1:3HNVAxEgGca1i23Ai/8DeCmibx02jBvTHAT11INaVfU= 115 | github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 116 | github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= 117 | github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= 118 | github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= 119 | github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= 120 | github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM= 121 | github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY= 122 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 123 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 124 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 125 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 126 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 127 | github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 128 | github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ= 129 | github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 130 | github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= 131 | github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= 132 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 133 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 134 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 135 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 136 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 137 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 138 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 139 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 140 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 141 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 142 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 143 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 144 | github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= 145 | github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= 146 | github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU= 147 | github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ= 148 | github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= 149 | github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= 150 | github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= 151 | github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= 152 | github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= 153 | github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= 154 | github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= 155 | github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= 156 | github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= 157 | github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= 158 | github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= 159 | github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= 160 | github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= 161 | github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= 162 | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= 163 | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= 164 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 165 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 166 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 167 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 168 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 169 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 170 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 171 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 172 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 173 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= 174 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 175 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 176 | github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w= 177 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 178 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 179 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 180 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 181 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 182 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 183 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 184 | github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8= 185 | github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs= 186 | github.com/tailscale/golang-x-crypto v0.0.0-20221102133106-bc99ab8c2d17 h1:cSm67hIDABvL13S0n9TNoVhzYwjb24M46znbABLll18= 187 | github.com/tailscale/golang-x-crypto v0.0.0-20221102133106-bc99ab8c2d17/go.mod h1:95n9fbUCixVSI4QXLEvdKJjnYK2eUlkTx9+QwLPXFKU= 188 | github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio= 189 | github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8= 190 | github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk= 191 | github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0= 192 | github.com/tailscale/wireguard-go v0.0.0-20221219190806-4fa124729667 h1:etWp6uUwKu8NEj37K2OuMBnZ7EnVMKA7gJg5AqPFy/o= 193 | github.com/tailscale/wireguard-go v0.0.0-20221219190806-4fa124729667/go.mod h1:iiClgxBTruKI+nmzlQxbFw6c3nB/wb4Td/WCyX2berY= 194 | github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= 195 | github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= 196 | github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww= 197 | github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= 198 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 199 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 200 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 201 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 202 | github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So= 203 | github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= 204 | github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= 205 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= 206 | github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= 207 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 208 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 209 | go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= 210 | go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4= 211 | go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= 212 | go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf h1:IdwJUzqoIo5lkr2EOyKoe5qipUaEjbOKKY5+fzPBZ3A= 213 | go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf/go.mod h1:+QXzaoURFd0rGDIjDNpyIkv+F9R7EmeKorvlKRnhqgA= 214 | go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= 215 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 216 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 217 | golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= 218 | golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 219 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= 220 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 221 | golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10= 222 | golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= 223 | golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= 224 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 225 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 226 | golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 227 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 228 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 229 | golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 230 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 231 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 232 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 233 | golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 234 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 235 | golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= 236 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= 237 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 238 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 239 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 240 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 241 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 242 | golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 243 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 244 | golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 245 | golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 246 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 247 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 248 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 252 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 253 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 254 | golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 255 | golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 256 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 257 | golang.org/x/sys v0.0.0-20210301091718-77cc2087c03b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 258 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 259 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 260 | golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 261 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 262 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 263 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 264 | golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 265 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 266 | golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= 267 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 268 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 269 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 270 | golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= 271 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= 272 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 273 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 274 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 275 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 276 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 277 | golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= 278 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 279 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 280 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= 281 | golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 282 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 283 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 284 | golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= 285 | golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= 286 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 287 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 288 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= 289 | golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= 290 | golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= 291 | golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= 292 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 293 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 294 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 295 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 296 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 297 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 298 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 299 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 300 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 301 | gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY= 302 | gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0/go.mod h1:Dn5idtptoW1dIos9U6A2rpebLs/MtTwFacjKb8jLdQA= 303 | honnef.co/go/tools v0.4.0-0.dev.0.20220517111757-f4a2f64ce238 h1:8Vr1KP9OTjoKQSSeLefzibQgDV4s2ujJElKHqMi7nsA= 304 | howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= 305 | inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg= 306 | inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU= 307 | lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= 308 | lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 309 | modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= 310 | modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= 311 | modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= 312 | modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= 313 | modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= 314 | modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= 315 | modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= 316 | modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= 317 | modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= 318 | modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 319 | modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= 320 | modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= 321 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 322 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 323 | modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= 324 | modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= 325 | modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= 326 | modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= 327 | modernc.org/tcl v1.15.0 h1:oY+JeD11qVVSgVvodMJsu7Edf8tr5E/7tuhF5cNYz34= 328 | modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= 329 | modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 330 | modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE= 331 | nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= 332 | nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= 333 | software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= 334 | tailscale.com v1.36.1 h1:kLmV9EIcEEiONVPsCw7NJEuYENAz3IHDIrilyHOJAZM= 335 | tailscale.com v1.36.1/go.mod h1:jtamNns5Y+yuYjOCXvITnP3HemrkHs0gRc/ChTTIPq0= 336 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package tevents 2 | 3 | import ( 4 | "bytes" 5 | "database/sql" 6 | "embed" 7 | "fmt" 8 | "html" 9 | "html/template" 10 | "io/ioutil" 11 | "log" 12 | "net" 13 | "net/http" 14 | "strings" 15 | "time" 16 | 17 | "tailscale.com/client/tailscale" 18 | ) 19 | 20 | type Server struct { 21 | addr string 22 | server *http.Server 23 | db *sql.DB 24 | listener net.Listener 25 | tsClient *tailscale.LocalClient 26 | 27 | EventService EventService 28 | Notifier *Notifier 29 | } 30 | 31 | type TplData struct { 32 | Events []*Event 33 | EventGroups map[string][]bool 34 | Monitor bool 35 | LastHours int 36 | } 37 | 38 | //go:embed assets/* 39 | var assetsFS embed.FS 40 | 41 | const monitorHours = 48 42 | 43 | var ( 44 | indexTmpl *template.Template 45 | indexMonitorTmpl *template.Template 46 | rowTmpl *template.Template 47 | ) 48 | 49 | func init() { 50 | funcMap := template.FuncMap{ 51 | "formatTime": func(t time.Time) string { 52 | return t.String()[:19] 53 | }, 54 | } 55 | 56 | indexTmpl = parseTpl(funcMap, "assets/events.html") 57 | indexMonitorTmpl = parseTpl(funcMap, "assets/monitors.html") 58 | 59 | // special handling for partial template 60 | rowTmpl = template.Must( 61 | template.New("row.html").Funcs(funcMap).ParseFS(assetsFS, "assets/row.html")) 62 | } 63 | 64 | func NewServer(addr string, db *sql.DB, ln net.Listener, lc *tailscale.LocalClient) *Server { 65 | return &Server{ 66 | server: &http.Server{ 67 | Addr: addr, 68 | }, 69 | db: db, 70 | listener: ln, 71 | tsClient: lc, 72 | Notifier: NewNotifier(), 73 | } 74 | } 75 | 76 | func (s *Server) routes() *http.ServeMux { 77 | mux := http.NewServeMux() 78 | 79 | // display 80 | mux.HandleFunc("/", s.handleIndex) 81 | mux.HandleFunc("/monitor", s.handleIndexMonitor) 82 | 83 | // collect 84 | mux.HandleFunc("/.log", s.handleLog) 85 | mux.HandleFunc("/.monitor", s.handleMonitor) 86 | mux.HandleFunc("/.clear", s.handleClear) 87 | mux.HandleFunc("/.sse", s.handleLiveUpdates) 88 | 89 | mux.Handle("/assets/", http.FileServer(http.FS(assetsFS))) 90 | 91 | return mux 92 | } 93 | 94 | func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) { 95 | events, err := s.EventService.Find("event") 96 | if err != nil { 97 | http.Error(w, err.Error(), http.StatusInternalServerError) 98 | return 99 | } 100 | 101 | indexTmpl.ExecuteTemplate(w, "layout.html", TplData{Events: events, Monitor: false}) 102 | } 103 | 104 | func (s *Server) handleIndexMonitor(w http.ResponseWriter, r *http.Request) { 105 | events, err := s.EventService.Find("monitor") 106 | if err != nil { 107 | http.Error(w, err.Error(), http.StatusInternalServerError) 108 | return 109 | } 110 | 111 | // group entries by origin to show monitor hooks over time 112 | eventsGrouped := make(map[string][]*Event) 113 | for _, e := range events { 114 | identifier := fmt.Sprintf("%s:%s", e.Origin, e.Owner) 115 | eventsGrouped[identifier] = append(eventsGrouped[identifier], e) 116 | } 117 | 118 | // calculate hours for each event group 119 | eventsGroupedHours := make(map[string][]bool) 120 | for k, v := range eventsGrouped { 121 | eventsGroupedHours[k] = MonitorMap(time.Now(), v, monitorHours) 122 | } 123 | 124 | indexMonitorTmpl.ExecuteTemplate(w, "layout.html", TplData{ 125 | EventGroups: eventsGroupedHours, 126 | Monitor: true, 127 | LastHours: monitorHours, 128 | }) 129 | } 130 | 131 | // MonitorMap groups events by hour in reverse order 132 | func MonitorMap(now time.Time, events []*Event, lastHours int) []bool { 133 | 134 | // hours represents the last hours and if a 135 | // monitoring event occured in this hour 136 | hours := make([]bool, lastHours) 137 | 138 | for _, e := range events { 139 | diff := int(now.Sub(e.CreatedAt).Minutes() / 60) 140 | 141 | if diff >= lastHours { 142 | continue 143 | } 144 | 145 | hours[diff] = true 146 | } 147 | 148 | // reverse slice 149 | for i, j := 0, len(hours)-1; i < j; i, j = i+1, j-1 { 150 | hours[i], hours[j] = hours[j], hours[i] 151 | } 152 | 153 | return hours 154 | } 155 | 156 | func (s *Server) handleLog(w http.ResponseWriter, r *http.Request) { 157 | if r.Method != http.MethodPost { 158 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 159 | return 160 | } 161 | 162 | who, err := s.tsClient.WhoIs(r.Context(), r.RemoteAddr) 163 | if err != nil { 164 | http.Error(w, err.Error(), 500) 165 | return 166 | } 167 | owner := html.EscapeString(who.Node.ComputedName) 168 | 169 | body, err := ioutil.ReadAll(r.Body) 170 | if err != nil { 171 | http.Error(w, err.Error(), http.StatusBadRequest) 172 | return 173 | } 174 | defer r.Body.Close() 175 | 176 | origin := r.URL.Query().Get("origin") 177 | 178 | if err := s.EventService.Insert(origin, "event", string(body), owner); err != nil { 179 | http.Error(w, fmt.Sprintf("sqlite error: %v", err), http.StatusInternalServerError) 180 | return 181 | } 182 | 183 | s.Notifier.Send(Event{ 184 | Origin: origin, 185 | EventType: "event", 186 | Body: string(body), 187 | Owner: owner, 188 | CreatedAt: time.Now().UTC(), 189 | }) 190 | } 191 | 192 | func (s *Server) handleMonitor(w http.ResponseWriter, r *http.Request) { 193 | if r.Method != http.MethodPost { 194 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 195 | return 196 | } 197 | 198 | origin := r.URL.Query().Get("origin") 199 | who, err := s.tsClient.WhoIs(r.Context(), r.RemoteAddr) 200 | if err != nil { 201 | http.Error(w, err.Error(), 500) 202 | return 203 | } 204 | owner := html.EscapeString(who.Node.ComputedName) 205 | 206 | if err := s.EventService.Insert(origin, "monitor", "", owner); err != nil { 207 | http.Error(w, err.Error(), http.StatusInternalServerError) 208 | return 209 | } 210 | } 211 | 212 | func (s *Server) handleClear(w http.ResponseWriter, r *http.Request) { 213 | if r.Method != http.MethodPost { 214 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) 215 | return 216 | } 217 | 218 | if err := s.EventService.ClearAll(); err != nil { 219 | http.Error(w, err.Error(), http.StatusInternalServerError) 220 | return 221 | } 222 | 223 | http.Redirect(w, r, "/", http.StatusSeeOther) 224 | } 225 | 226 | func (s *Server) handleLiveUpdates(w http.ResponseWriter, r *http.Request) { 227 | handler := s.handleSSE() 228 | handler(w, r) 229 | } 230 | 231 | func (s *Server) handleSSE() http.HandlerFunc { 232 | return func(w http.ResponseWriter, r *http.Request) { 233 | w.Header().Set("Content-Type", "text/event-stream") 234 | w.Header().Set("Cache-Control", "no-cache") 235 | w.Header().Set("Connection", "keep-alive") 236 | w.Header().Set("Access-Control-Allow-Origin", "*") 237 | 238 | flusher, _ := w.(http.Flusher) 239 | 240 | ticker := time.NewTicker(5 * time.Second) 241 | defer ticker.Stop() 242 | 243 | notifyClient := s.Notifier.AddListener() 244 | defer s.Notifier.RemoveListener(notifyClient) 245 | 246 | for { 247 | select { 248 | case event := <-notifyClient.c: 249 | 250 | // print in the format 'data:' and remove newslines 251 | var b bytes.Buffer 252 | fmt.Fprintf(w, "data:") 253 | rowTmpl.Execute(&b, event) 254 | fmt.Fprintf(w, "%s\n\n", strings.ReplaceAll(b.String(), "\n", "")) 255 | flusher.Flush() 256 | case <-ticker.C: 257 | fmt.Fprintf(w, "keepalive: \n\n") 258 | flusher.Flush() 259 | case <-r.Context().Done(): 260 | return 261 | 262 | } 263 | } 264 | 265 | } 266 | } 267 | 268 | func (s *Server) Start() error { 269 | 270 | s.server.Handler = s.routes() 271 | 272 | log.Println("Starting server on", s.server.Addr) 273 | return s.server.Serve(s.listener) 274 | } 275 | 276 | func truncateToHour(t time.Time) time.Time { 277 | return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()) 278 | } 279 | 280 | func parseTpl(funcs template.FuncMap, file string) *template.Template { 281 | return template.Must( 282 | template.New("layout.html").Funcs(funcs).ParseFS( 283 | assetsFS, 284 | "assets/layout.html", 285 | "assets/row.html", 286 | file, 287 | )) 288 | } 289 | -------------------------------------------------------------------------------- /http_test.go: -------------------------------------------------------------------------------- 1 | package tevents 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestMonitorMap(t *testing.T) { 9 | now := time.Date(2000, 1, 1, 1, 30, 0, 0, time.UTC) 10 | events := []*Event{ 11 | {Origin: "event2", CreatedAt: now.Add(-1 * time.Hour)}, 12 | {Origin: "event3", CreatedAt: now.Add(-2 * time.Hour)}, 13 | } 14 | 15 | // get results for the last 6 hours 16 | results := MonitorMap(now, events, 6) 17 | l := len(results) 18 | 19 | if results[l-1] != false || results[l-2] != true || results[l-3] != true { 20 | t.Error("monitor map is wrong", results) 21 | } 22 | } 23 | 24 | func TestMonitorMapBounds(t *testing.T) { 25 | now := time.Date(2000, 1, 1, 1, 0, 0, 0, time.UTC) 26 | events := []*Event{ 27 | {Origin: "event1", CreatedAt: now}, 28 | {Origin: "event2", CreatedAt: now.Add(-7 * time.Hour)}, 29 | } 30 | 31 | // get results for the last 6 hours 32 | results := MonitorMap(now, events, 6) 33 | 34 | if results[len(results)-1] != true { 35 | t.Error("monitor map bound is wrong") 36 | } 37 | 38 | if results[0] != false { 39 | t.Error("monitor map bound is wrong") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /notifier.go: -------------------------------------------------------------------------------- 1 | package tevents 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type Notifier struct { 9 | InputChan chan string 10 | count int64 11 | 12 | mu sync.Locker 13 | listener map[int64]Listener 14 | } 15 | 16 | func NewNotifier() *Notifier { 17 | return &Notifier{ 18 | listener: make(map[int64]Listener, 0), 19 | mu: &sync.Mutex{}, 20 | } 21 | } 22 | 23 | type Listener struct { 24 | id int64 25 | c chan Event 26 | } 27 | 28 | func (b *Notifier) AddListener() Listener { 29 | b.mu.Lock() 30 | defer b.mu.Unlock() 31 | id := b.count 32 | b.count++ 33 | 34 | l := Listener{id: id, c: make(chan Event)} 35 | b.listener[id] = l 36 | fmt.Printf("%+v\n", b.listener) 37 | return l 38 | } 39 | 40 | func (b *Notifier) RemoveListener(l Listener) { 41 | b.mu.Lock() 42 | defer b.mu.Unlock() 43 | delete(b.listener, l.id) 44 | } 45 | 46 | func (b *Notifier) Send(e Event) { 47 | fmt.Printf("sending event to %v listeners\n", len(b.listener)) 48 | for _, l := range b.listener { 49 | select { 50 | case l.c <- e: 51 | default: 52 | // ignore failed send 53 | fmt.Println("failed to send event") 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /screenshots/events-monitors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rverton/tevents/df8ee048be47fde9185aab5fa5215380ad394ecc/screenshots/events-monitors.png --------------------------------------------------------------------------------