From 94c93b837d733c8f0819c1fdbc5ff2cdc3a85431 Mon Sep 17 00:00:00 2001 From: "systemli[bot]" Date: Sun, 1 Oct 2023 04:33:28 +0000 Subject: [PATCH] Deployed 94c160e with MkDocs version: 1.4.2 --- .nojekyll | 0 404.html | 431 ++ api/index.html | 480 ++ api/swagger.yaml | 170 + assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.51d95adb.min.js | 29 + assets/javascripts/bundle.51d95adb.min.js.map | 8 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.e5c33ebb.min.js | 42 + .../workers/search.e5c33ebb.min.js.map | 8 + assets/stylesheets/main.558e4712.min.css | 1 + assets/stylesheets/main.558e4712.min.css.map | 1 + assets/stylesheets/palette.2505c338.min.css | 1 + .../stylesheets/palette.2505c338.min.css.map | 1 + assets/ticker-api.service | 17 + configuration/index.html | 505 ++ docs.go | 252 + index.html | 550 ++ quick-install-all/index.html | 938 +++ quick-install/index.html | 747 ++ search/search_index.json | 1 + sitemap.xml | 28 + sitemap.xml.gz | Bin 0 -> 198 bytes swagger.json | 228 + swagger.yaml | 170 + 52 files changed, 11803 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 api/index.html create mode 100644 api/swagger.yaml create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.51d95adb.min.js create mode 100644 assets/javascripts/bundle.51d95adb.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js.map create mode 100644 assets/stylesheets/main.558e4712.min.css create mode 100644 assets/stylesheets/main.558e4712.min.css.map create mode 100644 assets/stylesheets/palette.2505c338.min.css create mode 100644 assets/stylesheets/palette.2505c338.min.css.map create mode 100644 assets/ticker-api.service create mode 100644 configuration/index.html create mode 100644 docs.go create mode 100644 index.html create mode 100644 quick-install-all/index.html create mode 100644 quick-install/index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 swagger.json create mode 100644 swagger.yaml diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..1e8fc0ee --- /dev/null +++ b/404.html @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + + + + + + Systemli Ticker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/api/index.html b/api/index.html new file mode 100644 index 00000000..f9598b06 --- /dev/null +++ b/api/index.html @@ -0,0 +1,480 @@ + + + + + + + + + + + + + + + + + + + + + + + + API Specification - Systemli Ticker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

API Specification

+
+

Important

+

The Specification is work in progress at the moment. Inaccuracies or errors are possible.

+
+

+
+
+ + + + + + + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/api/swagger.yaml b/api/swagger.yaml new file mode 100644 index 00000000..381c74d9 --- /dev/null +++ b/api/swagger.yaml @@ -0,0 +1,170 @@ +basePath: /v1 +definitions: + response.Error: + properties: + code: + $ref: '#/definitions/response.ErrorCode' + message: + $ref: '#/definitions/response.ErrorMessage' + type: object + response.ErrorCode: + enum: + - 1000 + - 1001 + - 1002 + - 1003 + type: integer + x-enum-varnames: + - CodeDefault + - CodeNotFound + - CodeBadCredentials + - CodeInsufficientPermissions + response.ErrorMessage: + enum: + - insufficient permissions + - unauthorized + - user identifier not found + - ticker identifier not found + - message identifier not found + - message not found + - files identifier not found + - upload limit exceeded + - user not found + - ticker not found + - setting not found + - messages couldn't fetched + - invalid form values + - failed to save + - uploads not found + - unable to connect to mastodon + - could not authenticate password + type: string + x-enum-varnames: + - InsufficientPermissions + - Unauthorized + - UserIdentifierMissing + - TickerIdentifierMissing + - MessageIdentierMissing + - MessageNotFound + - FilesIdentifierMissing + - TooMuchFiles + - UserNotFound + - TickerNotFound + - SettingNotFound + - MessageFetchError + - FormError + - StorageError + - UploadsNotFound + - MastodonError + - PasswordError + response.Response: + properties: + data: + additionalProperties: + type: string + type: object + error: + $ref: '#/definitions/response.Error' + status: + $ref: '#/definitions/response.Status' + type: object + response.Status: + enum: + - success + - error + type: string + x-enum-varnames: + - StatusSuccess + - StatusError +host: localhost:8080 +info: + contact: + email: admin@systemli.org + name: Systemli Admin Team + url: https://www.systemli.org/en/contact/ + description: Service to distribute short messages in support of events, demonstrations, + or other time-sensitive events. + license: + name: GPLv3 + url: https://www.gnu.org/licenses/gpl-3.0.html + title: Ticker API + version: "1.0" +paths: + /init: + get: + consumes: + - application/json + description: |- + The first request for retrieving information about the ticker. It is mandatory that the browser sends + the origin as a header. This can be overwritten with a query parameter. + parameters: + - description: Origin from the ticker, e.g. demoticker.org + in: query + name: origin + type: string + - description: Origin from the ticker, e.g. http://demoticker.org + in: header + name: origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Retrieves the initial ticker configuration + tags: + - public + /timeline: + get: + consumes: + - application/json + description: |- + Endpoint to retrieve the messages from a ticker. The endpoint has a pagination to fetch newer or older + messages. It is mandatory that the browser sends the origin as a header. This can be overwritten with + a query parameter. + parameters: + - description: Origin from the ticker, e.g. demoticker.org + in: query + name: origin + type: string + - description: Origin from the ticker, e.g. http://demoticker.org + in: header + name: origin + type: string + - description: 'Limit for fetched messages, default: 10' + in: query + name: limit + type: integer + - description: ID of the message we look for older entries + in: query + name: before + type: integer + - description: ID of the message we look for newer entries + in: query + name: after + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.Response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.Response' + summary: Fetch the messages for a ticker. + tags: + - public +swagger: "2.0" diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 00000000..b20ec683 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Configuration

+
# listen binds ticker to specific address and port
+listen: "localhost:8080"
+# log_level sets log level for logrus
+log_level: "error"
+# log_format sets log format for logrus (default: json)
+log_format: "json"
+# initiator is the email for the first admin user (see password in logs)
+initiator: "admin@systemli.org"
+# database is the path to the bolt file
+database: "ticker.db"
+# secret used for JSON Web Tokens
+secret: "slorp-panfil-becall-dorp-hashab-incus-biter-lyra-pelage-sarraf-drunk"
+# telegram configuration
+telegram_bot_token: ""
+# listen port for prometheus metrics exporter
+metrics_listen: ":8181"
+# path where to store the uploaded files
+upload_path: "/path/to/uploads"
+# base url for uploaded assets
+upload_url: "http://localhost:8080"
+
+
+

Note

+

We use viper. That means you can use any of the supported file formats. Env variables +will overwrite existing config file values. Note that every env variable MUST be prefixed by: TICKER_. +E.g. TICKER_DATABASE.

+
+

The following env vars can be used:

+
    +
  • TICKER_DATABASE
  • +
  • TICKER_LISTEN
  • +
  • TICKER_LOG_LEVEL
  • +
  • TICKER_INITIATOR
  • +
  • TICKER_SECRET
  • +
  • TICKER_TELEGRAM_BOT_TOKEN
  • +
  • TICKER_METRICS_LISTEN
  • +
  • TICKER_UPLOAD_PATH
  • +
  • TICKER_UPLOAD_URL
  • +
+ + + + + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/docs.go b/docs.go new file mode 100644 index 00000000..11aef0ae --- /dev/null +++ b/docs.go @@ -0,0 +1,252 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "Systemli Admin Team", + "url": "https://www.systemli.org/en/contact/", + "email": "admin@systemli.org" + }, + "license": { + "name": "GPLv3", + "url": "https://www.gnu.org/licenses/gpl-3.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/init": { + "get": { + "description": "The first request for retrieving information about the ticker. It is mandatory that the browser sends\nthe origin as a header. This can be overwritten with a query parameter.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "public" + ], + "summary": "Retrieves the initial ticker configuration", + "parameters": [ + { + "type": "string", + "description": "Origin from the ticker, e.g. demoticker.org", + "name": "origin", + "in": "query" + }, + { + "type": "string", + "description": "Origin from the ticker, e.g. http://demoticker.org", + "name": "origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + }, + "/timeline": { + "get": { + "description": "Endpoint to retrieve the messages from a ticker. The endpoint has a pagination to fetch newer or older\nmessages. It is mandatory that the browser sends the origin as a header. This can be overwritten with\na query parameter.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "public" + ], + "summary": "Fetch the messages for a ticker.", + "parameters": [ + { + "type": "string", + "description": "Origin from the ticker, e.g. demoticker.org", + "name": "origin", + "in": "query" + }, + { + "type": "string", + "description": "Origin from the ticker, e.g. http://demoticker.org", + "name": "origin", + "in": "header" + }, + { + "type": "integer", + "description": "Limit for fetched messages, default: 10", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "description": "ID of the message we look for older entries", + "name": "before", + "in": "query" + }, + { + "type": "integer", + "description": "ID of the message we look for newer entries", + "name": "after", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.Response" + } + } + } + } + } + }, + "definitions": { + "response.Error": { + "type": "object", + "properties": { + "code": { + "$ref": "#/definitions/response.ErrorCode" + }, + "message": { + "$ref": "#/definitions/response.ErrorMessage" + } + } + }, + "response.ErrorCode": { + "type": "integer", + "enum": [ + 1000, + 1001, + 1002, + 1003 + ], + "x-enum-varnames": [ + "CodeDefault", + "CodeNotFound", + "CodeBadCredentials", + "CodeInsufficientPermissions" + ] + }, + "response.ErrorMessage": { + "type": "string", + "enum": [ + "insufficient permissions", + "unauthorized", + "user identifier not found", + "ticker identifier not found", + "message identifier not found", + "message not found", + "files identifier not found", + "upload limit exceeded", + "user not found", + "ticker not found", + "setting not found", + "messages couldn't fetched", + "invalid form values", + "failed to save", + "uploads not found", + "unable to connect to mastodon", + "could not authenticate password" + ], + "x-enum-varnames": [ + "InsufficientPermissions", + "Unauthorized", + "UserIdentifierMissing", + "TickerIdentifierMissing", + "MessageIdentierMissing", + "MessageNotFound", + "FilesIdentifierMissing", + "TooMuchFiles", + "UserNotFound", + "TickerNotFound", + "SettingNotFound", + "MessageFetchError", + "FormError", + "StorageError", + "UploadsNotFound", + "MastodonError", + "PasswordError" + ] + }, + "response.Response": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "error": { + "$ref": "#/definitions/response.Error" + }, + "status": { + "$ref": "#/definitions/response.Status" + } + } + }, + "response.Status": { + "type": "string", + "enum": [ + "success", + "error" + ], + "x-enum-varnames": [ + "StatusSuccess", + "StatusError" + ] + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:8080", + BasePath: "/v1", + Schemes: []string{}, + Title: "Ticker API", + Description: "Service to distribute short messages in support of events, demonstrations, or other time-sensitive events.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/index.html b/index.html new file mode 100644 index 00000000..fc3785de --- /dev/null +++ b/index.html @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + + + + Systemli Ticker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Systemli Ticker

+

Service to distribute short messages in support of events, demonstrations, or other time-sensitive events.

+

This repository contains the API and storage for +the Systemli Ticker Project.

+
+

Requirements

+

The project is written in Go. You should be familiar with the structure and organisation of the code. If not, there are +some good guides.

+
+

First run

+
    +
  • +

    make sure you have pulled the repository +

    git clone git@github.com:systemli/ticker.git
    +

    +
  • +
  • +

    start the ticker +

    cp config.yml.dist config.yml
    +go run .
    +

    +
  • +
+

Now you have a running ticker API!

+

Testing

+
go test ./...
+
+ + + + + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/quick-install-all/index.html b/quick-install-all/index.html new file mode 100644 index 00000000..9b35861e --- /dev/null +++ b/quick-install-all/index.html @@ -0,0 +1,938 @@ + + + + + + + + + + + + + + + + + + + + + + + + Full Installation - Systemli Ticker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Full Installation

+

This was tested with an Ubuntu 20.04 LTS server.

+

Results may differ on other releases or distributions!

+

Requirements

+
    +
  • git
  • +
  • go
  • +
  • nodejs
  • +
  • yarn
  • +
  • nginx
  • +
  • certificate (certbot & python3-certbot-nginx to use free Let's Encrypt Certs)
  • +
  • git
  • +
  • Public IPv4
  • +
  • Public IPv6 (Please!)
  • +
+

Getting Go:

+

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

+

Instead use: + golang.org install guide

+

Please be also aware, that it's best practice to build your version of "ticker" not on the production machine. In order to keep the hurdle as low as possible, we will build the app on the system we're going to run it. +To enhance security maybe you want to remove go afterwards.

+

Getting NodeJS:

+

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

+

Instead use: + nodesource/distributions

+

Install ticker

+

Build from source:

+

As mentioned above, this isn't best practice. +You can also build it from source on your dedicated build server, your own pc at home, etc. Then just scp it over to the production Server afterwards. +1. cd /var/www/ +The directory where we gonna install all the things +2. git clone https://github.com/systemli/ticker +Clone the repository onto your disk +3. cd ticker +Go into the just cloned repository +4. go build -o build/ticker +Build the application +5. Go to "Configuration, Service and Stuff"

+

Downloading a release from GitHub:

+
    +
  1. Go to https://github.com/systemli/ticker/releases
  2. +
  3. Pick the latest release and download it via wget https://github.com/systemli/ticker/releases/download/<version>/ticker-<version>-<architecture>
  4. +
  5. mv ticker-<version>-<architecture> /var/www/ticker/ticker
  6. +
  7. chmod +x /var/www/ticker/ticker
  8. +
  9. Go to "Configuration, Service and Stuff"
  10. +
+

Configuration, Service and Stuff

+
    +
  1. vim config.yml +Fill your config file with the following content: +
    # listen binds ticker to specific address and port
    +listen: "localhost:8080"
    +# log_level sets log level for logrus
    +log_level: "error"
    +# initiator is the email for the first admin user (see password in logs)
    +initiator: "<your e-mail>"
    +# database is the path to the bolt file
    +database: "ticker.db"
    +# secret used for JSON Web Tokens
    +secret: "<your special little secret> (make it LOOOONG!)"
    +# listen port for prometheus metrics exporter
    +metrics_listen: ":8181"
    +# path where to store the uploaded files
    +upload_path: "uploads"
    +# base url for uploaded assets
    +upload_url: "https://api.domain.tld"
    +
  2. +
  3. Create a systemd Task (see docs/ticker-api.service for reference)
  4. +
  5. systemctl enable ticker-api.service
  6. +
  7. systemctl start ticker-api.service
  8. +
  9. If you enter systemctl status ticker-api.service you'll see the generated admin password. Please change it immediately!
  10. +
  11. Done. \o/ You now have a fully functional ticker API.
  12. +
+

Exposing

+

vim /etc/nginx/sites-available/ticker-api

+
server {
+    listen 80;
+    listen [::]:80;
+    server_name api.domain.tld;
+    location / {
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_pass http://127.0.0.1:8080;
+    }
+}
+
+

Create a symlink to enable this config: +ln -s /etc/nginx/sites-available/ticker-api /etc/nginx/sites-enabled/

+

Now run nginx -t to check if the config is correct.

+

If your output looks like this: +

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
+nginx: configuration file /etc/nginx/nginx.conf test is successful
+
+then you can proceed. Otherwise: look for the error or ask someone to help.

+

Run certbot --nginx --redirect -d api.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

+

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

+

It should generate a certificate after answering a few questions like a email address, etc.

+

done. This domain is now serving a ticker API. :)

+

Install ticker-admin

+
    +
  1. cd /var/www/ +The directory where we gonna install all the things
  2. +
  3. git clone https://github.com/systemli/ticker-admin +Clone the repository onto your disk
  4. +
  5. cd ticker-admin +Go into the just cloned repository
  6. +
  7. yarn +Install the dependencies
  8. +
  9. +

    vim .env +Fill your .env file with the following content: +

    REACT_APP_API_URL=https://api.domain.tld/v1
    +
    +Change api.domain.tld to the URL you chose at ticker API server creation

    +
  10. +
  11. +

    yarn build +Build the application

    +
  12. +
  13. chown www-data:www-data dist/ -R +Sets the owner for the freshly created dist repository to your nginx user
  14. +
+

Exposing

+

vim /etc/nginx/sites-available/ticker-admin

+
server {
+    listen 80;
+    listen [::]:80;
+    server_name admin.domain.tld;
+    root /var/www/ticker-admin/dist;
+    index index.html;
+    location / {
+        try_files $uri $uri/ =404;
+    }
+}
+
+

Create a symlink to enable this config: +ln -s /etc/nginx/sites-available/ticker-admin /etc/nginx/sites-enabled/

+

Now run nginx -t to check if the config is correct.

+

If your output looks like this: +

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
+nginx: configuration file /etc/nginx/nginx.conf test is successful
+
+then you can proceed. Otherwise: look for the error or ask someone to help.

+

Run certbot --nginx --redirect -d admin.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

+

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

+

It should generate a certificate after answering a few questions like a email address, etc.

+

done. This domain is now serving a ticker frontend. :)

+

You need to create the ticker in ticker-admin in order to see something!

+

Install ticker-frontend

+
    +
  1. cd /var/www/ +The directory where we gonna install all the things
  2. +
  3. git clone https://github.com/systemli/ticker-frontend +Clone the repository onto your disk
  4. +
  5. cd ticker-frontend +Go into the just cloned repository
  6. +
  7. git checkout d03982f3059d6335a9e9ec0abcb71813ccbafef7 +Checkout this branch. Hopefully, this won't be necessary in the future, but right now, this seems to be the the last working commit (for me)
  8. +
  9. yarn +Install the dependencies
  10. +
  11. +

    vim .env +Fill your .env file with the following content: +

    REACT_APP_API_URL=https://api.domain.tld/v1
    +
    +Change api.domain.tld to the URL you chose at ticker API server creation

    +
  12. +
  13. +

    yarn build +Build the application

    +
  14. +
  15. chown www-data:www-data dist/ -R +Sets the owner for the freshly created dist repository to your nginx user
  16. +
+

Exposing

+

vim /etc/nginx/sites-available/ticker-frontend

+

The following config is for a single domain only! For wildcard configs, just replace the sub.domain.tld with a *.domain.tld. You need to validate your domain via DNS challenge or use another provider then Let's Encrypt!

+
server {
+    listen 80;
+    listen [::]:80;
+    server_name sub.domain.tld;
+    root /var/www/ticker-frontend/dist;
+    index index.html;
+    location / {
+        try_files $uri $uri/ =404;
+    }
+}
+
+

Create a symlink to enable this config: +ln -s /etc/nginx/sites-available/ticker-frontend /etc/nginx/sites-enabled/

+

Now run nginx -t to check if the config is correct.

+

If your output looks like this: +

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
+nginx: configuration file /etc/nginx/nginx.conf test is successful
+
+then you can proceed. Otherwise: look for the error or ask someone to help.

+

Run certbot --nginx --redirect -d sub.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

+

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

+

It should generate a certificate after answering a few questions like a email address, etc.

+

done. This domain is now serving a ticker frontend. :)

+

You need to create the ticker in ticker-admin in order to see something!

+

First touch

+
    +
  • Go to https://admin.domain.tld and log in with the credentials provided at your first start of the ticker api.
  • +
  • Change the provided credentials (and use a password manager)
  • +
  • Create a new ticker for with the domain sub.domain.tld
  • +
  • Create test content
  • +
  • Try to open https://sub.domain.tld
  • +
  • ...
  • +
  • profit?
  • +
+ + + + + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/quick-install/index.html b/quick-install/index.html new file mode 100644 index 00000000..fa8677a0 --- /dev/null +++ b/quick-install/index.html @@ -0,0 +1,747 @@ + + + + + + + + + + + + + + + + + + + + + + Installation - Systemli Ticker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Installation

+

This is tested under Ubuntu 20.04 LTS

+

We're assuming, that the ticker api will be available under the api.domain.tld domain. Please change accordingly.

+

This should be considered a QUICK INSTALL GUIDE! Some best practices may differ.

+

Requirements:

+
    +
  • nginx
  • +
  • certificate (certbot & python3-certbot-nginx to use free Let's Encrypt Certs)
  • +
  • git
  • +
  • go
  • +
  • Public IPv4
  • +
  • Public IPv6 (Please!)
  • +
+

Getting Go:

+

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

+

Instead use: + golang.org install guide

+

Please be also aware, that it's best practice to build your version of "ticker" not on the production machine. In order to keep the hurdle as low as possible, we will build the app on the system we're going to run it. +To enhance security maybe you want to remove go afterwards.

+

Installation

+

Build from source:

+

As mentioned above, this isn't best practice. +You can also build it from source on your dedicated build server, your own pc at home, etc. Then just scp it over to the production Server afterwards.

+
    +
  1. cd /var/www/ +The directory where we gonna install all the things
  2. +
  3. git clone https://github.com/systemli/ticker +Clone the repository onto your disk
  4. +
  5. cd ticker +Go into the just cloned repository
  6. +
  7. go build -o build/ticker +Build the application
  8. +
  9. Go to "Configuration, Service and Stuff"
  10. +
+

Downloading a release from GitHub:

+
    +
  1. Go to https://github.com/systemli/ticker/releases
  2. +
  3. Pick the latest release and download it via wget https://github.com/systemli/ticker/releases/download/<version>/ticker-<version>-<architecture>
  4. +
  5. mv ticker-<version>-<architecture> /var/www/ticker/ticker
  6. +
  7. chmod +x /var/www/ticker/ticker
  8. +
  9. Go to "Configuration, Service and Stuff"
  10. +
+

Configuration, Service and Stuff

+
    +
  1. vim config.yml +Fill your config file with the following content: +
    # listen binds ticker to specific address and port
    +listen: "localhost:8080"
    +# log_level sets log level for logrus
    +log_level: "error"
    +# initiator is the email for the first admin user (see password in logs)
    +initiator: "<your e-mail>"
    +# database is the path to the bolt file
    +database: "ticker.db"
    +# secret used for JSON Web Tokens
    +secret: "<your special little secret> (make it LOOOONG!)"
    +# listen port for prometheus metrics exporter
    +metrics_listen: ":8181"
    +# path where to store the uploaded files
    +upload_path: "uploads"
    +# base url for uploaded assets
    +upload_url: "https://api.domain.tld"
    +
  2. +
  3. Create a systemd Task (see docs/ticker-api.service for reference)
  4. +
  5. systemctl enable ticker-api.service
  6. +
  7. systemctl start ticker-api.service
  8. +
  9. If you enter systemctl status ticker-api.service you'll see the generated admin password. Please change it immediately!
  10. +
  11. Done. \o/ You now have a fully functional ticker API.
  12. +
+

Exposing

+

In order to expose your ticker API to the users and not only yourself on the server, you'll need some sort of reverse proxy. +The following config expects you to use nginx, but apache2, caddy, etc. works just fine too.

+

vim /etc/nginx/sites-available/ticker-api

+

This config is only for use with cerbot! Please create a secure SSL config if you won't let certbot do the job!

+
server {
+    listen 80;
+    listen [::]:80;
+    server_name api.domain.tld;
+    location / {
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_pass http://127.0.0.1:8080;
+    }
+}
+
+

This is an example config for using TLS/SSL without certbot: +

server {
+    server_name api.domain.tld;
+    location / {
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_pass http://127.0.0.1:8080;
+    }
+
+    listen [::]:443 ssl ipv6only=on;
+    listen 443 ssl;
+    ssl_certificate /etc/ssl/api.domain.tld-fullchain.pem;
+    ssl_certificate_key /etc/ssl/api.domain.tld-privkey.pem;
+    ssl_session_cache shared:le_nginx_SSL:10m;
+    ssl_session_timeout 1440m;
+    ssl_session_tickets off;
+
+    ssl_protocols TLSv1.2 TLSv1.3;
+    ssl_prefer_server_ciphers off;
+
+    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA";
+
+}
+
+
+server {
+    return 301 https://$host$request_uri;
+
+    listen 80;
+    listen [::]:80;
+    server_name api.domain.tld;
+
+}
+

+

Create a symlink to enable this config: +ln -s /etc/nginx/sites-available/ticker-api /etc/nginx/sites-enabled/

+

Now run nginx -t to check if the config is correct.

+

If your output looks like this: +

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
+nginx: configuration file /etc/nginx/nginx.conf test is successful
+
+then you can proceed. Otherwise: look for the error or ask someone to help.

+

Run certbot --nginx --redirect -d api.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

+

It should generate a certificate after answering a few questions like a email address, etc.

+

done. This domain is now serving a ticker API. :)

+ + + + + + +
+
+ + +
+ +
+ +
+ + +
+ +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 00000000..25e7a05d --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Systemli Ticker","text":"

Service to distribute short messages in support of events, demonstrations, or other time-sensitive events.

This repository contains the API and storage for the Systemli Ticker Project.

Requirements

The project is written in Go. You should be familiar with the structure and organisation of the code. If not, there are some good guides.

"},{"location":"#first-run","title":"First run","text":"
  • make sure you have pulled the repository

    git clone git@github.com:systemli/ticker.git\n

  • start the ticker

    cp config.yml.dist config.yml\ngo run .\n

Now you have a running ticker API!

"},{"location":"#testing","title":"Testing","text":"
go test ./...\n
"},{"location":"api/","title":"API Specification","text":"

Important

The Specification is work in progress at the moment. Inaccuracies or errors are possible.

const ui = SwaggerUIBundle({ url: 'swagger.yaml', dom_id: '#swagger-ui', })"},{"location":"configuration/","title":"Configuration","text":"
# listen binds ticker to specific address and port\nlisten: \"localhost:8080\"\n# log_level sets log level for logrus\nlog_level: \"error\"\n# log_format sets log format for logrus (default: json)\nlog_format: \"json\"\n# initiator is the email for the first admin user (see password in logs)\ninitiator: \"admin@systemli.org\"\n# database is the path to the bolt file\ndatabase: \"ticker.db\"\n# secret used for JSON Web Tokens\nsecret: \"slorp-panfil-becall-dorp-hashab-incus-biter-lyra-pelage-sarraf-drunk\"\n# telegram configuration\ntelegram_bot_token: \"\"\n# listen port for prometheus metrics exporter\nmetrics_listen: \":8181\"\n# path where to store the uploaded files\nupload_path: \"/path/to/uploads\"\n# base url for uploaded assets\nupload_url: \"http://localhost:8080\"\n

Note

We use viper. That means you can use any of the supported file formats. Env variables will overwrite existing config file values. Note that every env variable MUST be prefixed by: TICKER_. E.g. TICKER_DATABASE.

The following env vars can be used:

  • TICKER_DATABASE
  • TICKER_LISTEN
  • TICKER_LOG_LEVEL
  • TICKER_INITIATOR
  • TICKER_SECRET
  • TICKER_TELEGRAM_BOT_TOKEN
  • TICKER_METRICS_LISTEN
  • TICKER_UPLOAD_PATH
  • TICKER_UPLOAD_URL
"},{"location":"quick-install-all/","title":"Full Installation","text":"

This was tested with an Ubuntu 20.04 LTS server.

Results may differ on other releases or distributions!

"},{"location":"quick-install-all/#requirements","title":"Requirements","text":"
  • git
  • go
  • nodejs
  • yarn
  • nginx
  • certificate (certbot & python3-certbot-nginx to use free Let's Encrypt Certs)
  • git
  • Public IPv4
  • Public IPv6 (Please!)
"},{"location":"quick-install-all/#getting-go","title":"Getting Go:","text":"

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

Instead use: golang.org install guide

Please be also aware, that it's best practice to build your version of \"ticker\" not on the production machine. In order to keep the hurdle as low as possible, we will build the app on the system we're going to run it. To enhance security maybe you want to remove go afterwards.

"},{"location":"quick-install-all/#getting-nodejs","title":"Getting NodeJS:","text":"

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

Instead use: nodesource/distributions

"},{"location":"quick-install-all/#install-ticker","title":"Install ticker","text":""},{"location":"quick-install-all/#build-from-source","title":"Build from source:","text":"

As mentioned above, this isn't best practice. You can also build it from source on your dedicated build server, your own pc at home, etc. Then just scp it over to the production Server afterwards. 1. cd /var/www/ The directory where we gonna install all the things 2. git clone https://github.com/systemli/ticker Clone the repository onto your disk 3. cd ticker Go into the just cloned repository 4. go build -o build/ticker Build the application 5. Go to \"Configuration, Service and Stuff\"

"},{"location":"quick-install-all/#downloading-a-release-from-github","title":"Downloading a release from GitHub:","text":"
  1. Go to https://github.com/systemli/ticker/releases
  2. Pick the latest release and download it via wget https://github.com/systemli/ticker/releases/download/<version>/ticker-<version>-<architecture>
  3. mv ticker-<version>-<architecture> /var/www/ticker/ticker
  4. chmod +x /var/www/ticker/ticker
  5. Go to \"Configuration, Service and Stuff\"
"},{"location":"quick-install-all/#configuration-service-and-stuff","title":"Configuration, Service and Stuff","text":"
  1. vim config.yml Fill your config file with the following content:
    # listen binds ticker to specific address and port\nlisten: \"localhost:8080\"\n# log_level sets log level for logrus\nlog_level: \"error\"\n# initiator is the email for the first admin user (see password in logs)\ninitiator: \"<your e-mail>\"\n# database is the path to the bolt file\ndatabase: \"ticker.db\"\n# secret used for JSON Web Tokens\nsecret: \"<your special little secret> (make it LOOOONG!)\"\n# listen port for prometheus metrics exporter\nmetrics_listen: \":8181\"\n# path where to store the uploaded files\nupload_path: \"uploads\"\n# base url for uploaded assets\nupload_url: \"https://api.domain.tld\"\n
  2. Create a systemd Task (see docs/ticker-api.service for reference)
  3. systemctl enable ticker-api.service
  4. systemctl start ticker-api.service
  5. If you enter systemctl status ticker-api.service you'll see the generated admin password. Please change it immediately!
  6. Done. \\o/ You now have a fully functional ticker API.
"},{"location":"quick-install-all/#exposing","title":"Exposing","text":"

vim /etc/nginx/sites-available/ticker-api

server {\n    listen 80;\n    listen [::]:80;\n    server_name api.domain.tld;\n    location / {\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_pass http://127.0.0.1:8080;\n    }\n}\n

Create a symlink to enable this config: ln -s /etc/nginx/sites-available/ticker-api /etc/nginx/sites-enabled/

Now run nginx -t to check if the config is correct.

If your output looks like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\n
then you can proceed. Otherwise: look for the error or ask someone to help.

Run certbot --nginx --redirect -d api.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

It should generate a certificate after answering a few questions like a email address, etc.

done. This domain is now serving a ticker API. :)

"},{"location":"quick-install-all/#install-ticker-admin","title":"Install ticker-admin","text":"
  1. cd /var/www/ The directory where we gonna install all the things
  2. git clone https://github.com/systemli/ticker-admin Clone the repository onto your disk
  3. cd ticker-admin Go into the just cloned repository
  4. yarn Install the dependencies
  5. vim .env Fill your .env file with the following content:

    REACT_APP_API_URL=https://api.domain.tld/v1\n
    Change api.domain.tld to the URL you chose at ticker API server creation

  6. yarn build Build the application

  7. chown www-data:www-data dist/ -R Sets the owner for the freshly created dist repository to your nginx user
"},{"location":"quick-install-all/#exposing_1","title":"Exposing","text":"

vim /etc/nginx/sites-available/ticker-admin

server {\n    listen 80;\n    listen [::]:80;\n    server_name admin.domain.tld;\n    root /var/www/ticker-admin/dist;\n    index index.html;\n    location / {\n        try_files $uri $uri/ =404;\n    }\n}\n

Create a symlink to enable this config: ln -s /etc/nginx/sites-available/ticker-admin /etc/nginx/sites-enabled/

Now run nginx -t to check if the config is correct.

If your output looks like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\n
then you can proceed. Otherwise: look for the error or ask someone to help.

Run certbot --nginx --redirect -d admin.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

It should generate a certificate after answering a few questions like a email address, etc.

done. This domain is now serving a ticker frontend. :)

You need to create the ticker in ticker-admin in order to see something!

"},{"location":"quick-install-all/#install-ticker-frontend","title":"Install ticker-frontend","text":"
  1. cd /var/www/ The directory where we gonna install all the things
  2. git clone https://github.com/systemli/ticker-frontend Clone the repository onto your disk
  3. cd ticker-frontend Go into the just cloned repository
  4. git checkout d03982f3059d6335a9e9ec0abcb71813ccbafef7 Checkout this branch. Hopefully, this won't be necessary in the future, but right now, this seems to be the the last working commit (for me)
  5. yarn Install the dependencies
  6. vim .env Fill your .env file with the following content:

    REACT_APP_API_URL=https://api.domain.tld/v1\n
    Change api.domain.tld to the URL you chose at ticker API server creation

  7. yarn build Build the application

  8. chown www-data:www-data dist/ -R Sets the owner for the freshly created dist repository to your nginx user
"},{"location":"quick-install-all/#exposing_2","title":"Exposing","text":"

vim /etc/nginx/sites-available/ticker-frontend

The following config is for a single domain only! For wildcard configs, just replace the sub.domain.tld with a *.domain.tld. You need to validate your domain via DNS challenge or use another provider then Let's Encrypt!

server {\n    listen 80;\n    listen [::]:80;\n    server_name sub.domain.tld;\n    root /var/www/ticker-frontend/dist;\n    index index.html;\n    location / {\n        try_files $uri $uri/ =404;\n    }\n}\n

Create a symlink to enable this config: ln -s /etc/nginx/sites-available/ticker-frontend /etc/nginx/sites-enabled/

Now run nginx -t to check if the config is correct.

If your output looks like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\n
then you can proceed. Otherwise: look for the error or ask someone to help.

Run certbot --nginx --redirect -d sub.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

If you don't want to use certbot for your installation, please keep in mind, that nontheless you'll need an TLS cert for running this in production and need to configure the nginx accordingly by yourself!

It should generate a certificate after answering a few questions like a email address, etc.

done. This domain is now serving a ticker frontend. :)

You need to create the ticker in ticker-admin in order to see something!

"},{"location":"quick-install-all/#first-touch","title":"First touch","text":"
  • Go to https://admin.domain.tld and log in with the credentials provided at your first start of the ticker api.
  • Change the provided credentials (and use a password manager)
  • Create a new ticker for with the domain sub.domain.tld
  • Create test content
  • Try to open https://sub.domain.tld
  • ...
  • profit?
"},{"location":"quick-install/","title":"Installation","text":"

This is tested under Ubuntu 20.04 LTS

We're assuming, that the ticker api will be available under the api.domain.tld domain. Please change accordingly.

This should be considered a QUICK INSTALL GUIDE! Some best practices may differ.

"},{"location":"quick-install/#requirements","title":"Requirements:","text":"
  • nginx
  • certificate (certbot & python3-certbot-nginx to use free Let's Encrypt Certs)
  • git
  • go
  • Public IPv4
  • Public IPv6 (Please!)
"},{"location":"quick-install/#getting-go","title":"Getting Go:","text":"

Don't use the shipped version of your system, if you're working on a Debian based OS (Ubuntu, etc)

Instead use: golang.org install guide

Please be also aware, that it's best practice to build your version of \"ticker\" not on the production machine. In order to keep the hurdle as low as possible, we will build the app on the system we're going to run it. To enhance security maybe you want to remove go afterwards.

"},{"location":"quick-install/#installation_1","title":"Installation","text":""},{"location":"quick-install/#build-from-source","title":"Build from source:","text":"

As mentioned above, this isn't best practice. You can also build it from source on your dedicated build server, your own pc at home, etc. Then just scp it over to the production Server afterwards.

  1. cd /var/www/ The directory where we gonna install all the things
  2. git clone https://github.com/systemli/ticker Clone the repository onto your disk
  3. cd ticker Go into the just cloned repository
  4. go build -o build/ticker Build the application
  5. Go to \"Configuration, Service and Stuff\"
"},{"location":"quick-install/#downloading-a-release-from-github","title":"Downloading a release from GitHub:","text":"
  1. Go to https://github.com/systemli/ticker/releases
  2. Pick the latest release and download it via wget https://github.com/systemli/ticker/releases/download/<version>/ticker-<version>-<architecture>
  3. mv ticker-<version>-<architecture> /var/www/ticker/ticker
  4. chmod +x /var/www/ticker/ticker
  5. Go to \"Configuration, Service and Stuff\"
"},{"location":"quick-install/#configuration-service-and-stuff","title":"Configuration, Service and Stuff","text":"
  1. vim config.yml Fill your config file with the following content:
    # listen binds ticker to specific address and port\nlisten: \"localhost:8080\"\n# log_level sets log level for logrus\nlog_level: \"error\"\n# initiator is the email for the first admin user (see password in logs)\ninitiator: \"<your e-mail>\"\n# database is the path to the bolt file\ndatabase: \"ticker.db\"\n# secret used for JSON Web Tokens\nsecret: \"<your special little secret> (make it LOOOONG!)\"\n# listen port for prometheus metrics exporter\nmetrics_listen: \":8181\"\n# path where to store the uploaded files\nupload_path: \"uploads\"\n# base url for uploaded assets\nupload_url: \"https://api.domain.tld\"\n
  2. Create a systemd Task (see docs/ticker-api.service for reference)
  3. systemctl enable ticker-api.service
  4. systemctl start ticker-api.service
  5. If you enter systemctl status ticker-api.service you'll see the generated admin password. Please change it immediately!
  6. Done. \\o/ You now have a fully functional ticker API.
"},{"location":"quick-install/#exposing","title":"Exposing","text":"

In order to expose your ticker API to the users and not only yourself on the server, you'll need some sort of reverse proxy. The following config expects you to use nginx, but apache2, caddy, etc. works just fine too.

vim /etc/nginx/sites-available/ticker-api

This config is only for use with cerbot! Please create a secure SSL config if you won't let certbot do the job!

server {\n    listen 80;\n    listen [::]:80;\n    server_name api.domain.tld;\n    location / {\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_pass http://127.0.0.1:8080;\n    }\n}\n

This is an example config for using TLS/SSL without certbot:

server {\n    server_name api.domain.tld;\n    location / {\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_pass http://127.0.0.1:8080;\n    }\n\n    listen [::]:443 ssl ipv6only=on;\n    listen 443 ssl;\n    ssl_certificate /etc/ssl/api.domain.tld-fullchain.pem;\n    ssl_certificate_key /etc/ssl/api.domain.tld-privkey.pem;\n    ssl_session_cache shared:le_nginx_SSL:10m;\n    ssl_session_timeout 1440m;\n    ssl_session_tickets off;\n\n    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_prefer_server_ciphers off;\n\n    ssl_ciphers \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA\";\n\n}\n\n\nserver {\n    return 301 https://$host$request_uri;\n\n    listen 80;\n    listen [::]:80;\n    server_name api.domain.tld;\n\n}\n

Create a symlink to enable this config: ln -s /etc/nginx/sites-available/ticker-api /etc/nginx/sites-enabled/

Now run nginx -t to check if the config is correct.

If your output looks like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\nnginx: configuration file /etc/nginx/nginx.conf test is successful\n
then you can proceed. Otherwise: look for the error or ask someone to help.

Run certbot --nginx --redirect -d api.domain.tld to get a free SSL certificate. Please keep in mind, that you need to point the A & AAAA Records to your machine!

It should generate a certificate after answering a few questions like a email address, etc.

done. This domain is now serving a ticker API. :)

"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 00000000..543ad52a --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,28 @@ + + + + None + 2023-10-01 + daily + + + None + 2023-10-01 + daily + + + None + 2023-10-01 + daily + + + None + 2023-10-01 + daily + + + None + 2023-10-01 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..06ba54f45f167233b0ea4dbcf019a0a7a681e964 GIT binary patch literal 198 zcmV;%06G63iwFpq_84UX|8r?{Wo=<_E_iKh0PT`Z4#FT1h4(!LVPBwiqlVI*OILaT zLa`;GP#Ls(d+AS1@1V1pH*a3%+YGN?y+uzQz<5{Tf-sD17%J;Yy8Gi%| zI-(SIP~k4d;YjH?js#59y&Qn}&{9xc8i1}QW#oWGhEh!j7Bv^QUWv1Jn$t;b7ZPIc z8&R@ymlrH&MT)H6CZ;;+T73)pE*fd