chore: add peerfinder assets
This commit is contained in:
parent
6569c58e37
commit
12716ae972
119
app/gql/graphiql/playground.go
Normal file
119
app/gql/graphiql/playground.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package graphiql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{{.title}}</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphiql {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/react@17.0.2/umd/react.production.min.js"
|
||||||
|
integrity="{{.reactSRI}}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.production.min.js"
|
||||||
|
integrity="{{.reactDOMSRI}}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/graphiql@{{.version}}/graphiql.min.css"
|
||||||
|
x-integrity="{{.cssSRI}}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="graphiql">Loading...</div>
|
||||||
|
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/graphiql@{{.version}}/graphiql.min.js"
|
||||||
|
x-integrity="{{.jsSRI}}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{{- if .endpointIsAbsolute}}
|
||||||
|
const url = {{.endpoint}};
|
||||||
|
const subscriptionUrl = {{.subscriptionEndpoint}};
|
||||||
|
{{- else}}
|
||||||
|
const url = location.protocol + '//' + location.host + {{.endpoint}};
|
||||||
|
const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const subscriptionUrl = wsProto + '//' + location.host + {{.endpoint}};
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
const fetcher = GraphiQL.createFetcher({ url, subscriptionUrl });
|
||||||
|
ReactDOM.render(
|
||||||
|
React.createElement(GraphiQL, {
|
||||||
|
fetcher: fetcher,
|
||||||
|
isHeadersEditorEnabled: true,
|
||||||
|
shouldPersistHeaders: true
|
||||||
|
}),
|
||||||
|
document.getElementById('graphiql'),
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`))
|
||||||
|
|
||||||
|
// Handler responsible for setting up the playground
|
||||||
|
func Handler(title string, endpoint string) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "text/html; charset=UTF-8")
|
||||||
|
err := page.Execute(w, map[string]interface{}{
|
||||||
|
"title": title,
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"endpointIsAbsolute": endpointHasScheme(endpoint),
|
||||||
|
"subscriptionEndpoint": getSubscriptionEndpoint(endpoint),
|
||||||
|
"version": "2.0.10",
|
||||||
|
"cssSRI": "sha256-gQryfbGYeYFxnJYnfPStPYFt0+uv8RP8Dm++eh00G9c=",
|
||||||
|
"jsSRI": "sha256-qQ6pw7LwTLC+GfzN+cJsYXfVWRKH9O5o7+5H96gTJhQ=",
|
||||||
|
"reactSRI": "sha256-Ipu/TQ50iCCVZBUsZyNJfxrDk0E2yhaEIz0vqI+kFG8=",
|
||||||
|
"reactDOMSRI": "sha256-nbMykgB6tsOFJ7OdVmPpdqMFVk4ZsqWocT6issAPUF0=",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpointHasScheme checks if the endpoint has a scheme.
|
||||||
|
func endpointHasScheme(endpoint string) bool {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
return err == nil && u.Scheme != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSubscriptionEndpoint returns the subscription endpoint for the given
|
||||||
|
// endpoint if it is parsable as a URL, or an empty string.
|
||||||
|
func getSubscriptionEndpoint(endpoint string) string {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "https":
|
||||||
|
u.Scheme = "wss"
|
||||||
|
default:
|
||||||
|
u.Scheme = "ws"
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.String()
|
||||||
|
}
|
6
app/peerfinder/assets/bootstrap.min.css
vendored
Normal file
6
app/peerfinder/assets/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
58
app/peerfinder/assets/peerfinder.css
Normal file
58
app/peerfinder/assets/peerfinder.css
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/* Space out content a bit */
|
||||||
|
body {
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Everything but the jumbotron gets side spacing for mobile first views */
|
||||||
|
.header,
|
||||||
|
.footer {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom page header */
|
||||||
|
.header {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
/* Make the masthead heading the same height as the navigation */
|
||||||
|
.header h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom page footer */
|
||||||
|
.footer {
|
||||||
|
padding-top: 19px;
|
||||||
|
color: #777;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Customize container */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
max-width: 730px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.container-narrow > hr {
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Responsive: Portrait tablets and up */
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
/* Remove the padding we set earlier */
|
||||||
|
.header,
|
||||||
|
.footer {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
/* Space out the masthead */
|
||||||
|
.header {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-primary a { color: white; font-weight:bold }
|
21
app/peerfinder/layouts/form.tpl
Normal file
21
app/peerfinder/layouts/form.tpl
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >Peer Name</label><div class='col-sm-10'><input class="form-control" type=text name=peer_name value="{$o.peer_name|default:''|escape}"/></div></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >IRC Nick</label><div class='col-sm-10'><input class="form-control" type=text name=peer_nick value="{$o.peer_nick|default:''|escape}"/></div></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >Note</label><div class='col-sm-10'><input class="form-control" type=text name=peer_note value="{$o.peer_note|default:''|escape}"/></div></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >Country</label><div class='col-sm-2'><input class="form-control" type=text name=peer_country maxlength=3 value="{$o.peer_country|default:''|escape}"/></div></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >VPN Types</label><div class='col-sm-10'><select class="form-control" size=12 multiple name="peer_type[]">
|
||||||
|
<option {$types['openvpn']|default:''} value="openvpn">openvpn</option>
|
||||||
|
<option {$types['gre/ipsec']|default:''} value="gre/ipsec">gre/ipsec</option>
|
||||||
|
<option {$types['gre/plain']|default:''} value="gre/plain">gre/plain</option>
|
||||||
|
<option {$types['fastd']|default:''} value="fastd">fastd</option>
|
||||||
|
<option {$types['tinc']|default:''} value="tinc">tinc</option>
|
||||||
|
<option {$types['zerotier']|default:''} value="zerotier">zerotier</option>
|
||||||
|
<option {$types['wireguard']|default:''} value="wireguard">wireguard</option>
|
||||||
|
<option {$types['pptp']|default:''} value="pptp">pptp</option>
|
||||||
|
<option {$types['l2tp']|default:''} value="l2tp">l2tp</option>
|
||||||
|
<option {$types['other']|default:''} value="other">other</option>
|
||||||
|
</select></div></div>
|
||||||
|
<div class="form-group"><label class="col-sm-2 control-label" >Address Family</label><div class='col-sm-10'>
|
||||||
|
<label><input type="radio" name="peer_family" value="1" {$fam[0]|default:''} /> ipv4 </label>
|
||||||
|
<label><input type="radio" name="peer_family" value="2" {$fam[1]|default:''} /> ipv6 </label>
|
||||||
|
<label><input type="radio" name="peer_family" value="3" {$fam[2]|default:''} /> both </label>
|
||||||
|
</div></div>
|
41
app/peerfinder/layouts/main.tpl
Normal file
41
app/peerfinder/layouts/main.tpl
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{{define "main"}}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
{{template "meta" .}}
|
||||||
|
<title>DN42 PingFinder</title>
|
||||||
|
|
||||||
|
<link href="/peers/assets/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||||
|
<link href="/peers/assets/peerfinder.css" rel="stylesheet" integrity="sha384-ZsT4S9156eA60lsB4aOfffKowiaZ0NG7gIQfgIfGoxT6FxFocYH39kZgYaeZCvql" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="header clearfix">
|
||||||
|
<nav>
|
||||||
|
<ul class="nav nav-pills pull-right">
|
||||||
|
<li role="presentation"><a href="/peers">Home</a></li>
|
||||||
|
<li role="presentation"><a href="/peers/status">Status</a></li>
|
||||||
|
<li role="presentation"><a href="//util.sour.is/peer">Sign up/Manage</a></li>
|
||||||
|
<li role="presentation"><a href="//git.dn42.us/dn42/pingfinder/src/master/clients">Scripts</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<h3 class="text-muted">DN42 PeerFinder</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=container>
|
||||||
|
{{template "content" .}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=container>
|
||||||
|
<h2>JSON Output</h2>
|
||||||
|
<pre style="background:#222; color:#ddd; height: 20em; font-size: 65%">{$o|json_encode:128|escape}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
43
app/peerfinder/pages/home.tpl
Normal file
43
app/peerfinder/pages/home.tpl
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{{template "main" .}}
|
||||||
|
|
||||||
|
{{define "meta"}}
|
||||||
|
<meta http-equiv="refresh" content="30">
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "content"}}
|
||||||
|
<h2>What is this?</h2>
|
||||||
|
|
||||||
|
<p>This tool allows you to find "good" peerings
|
||||||
|
for <a href="https://dn42.net">dn42</a>, by measuring the latency from
|
||||||
|
various points in the network towards you.</p>
|
||||||
|
|
||||||
|
<p>If you don't know what dn42 is,
|
||||||
|
read <a href="https://dn42.net/Home">the website</a> and in particular
|
||||||
|
the <a href="https://dn42.net/Getting-started-with-dn42">Getting Started
|
||||||
|
guide</a>.</p>
|
||||||
|
|
||||||
|
<h2>How does it work?</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<ol>
|
||||||
|
<li>You enter your (Internet) IP address</li>
|
||||||
|
<li>Various routers participating in dn42 will ping you over the Internet</li>
|
||||||
|
<li>After a short while, you get back all the latency results</li>
|
||||||
|
<li>You can then peer with people close to you (low latency)</li>
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form class="form-inline" method="POST" action="/peers/req">
|
||||||
|
<label>Ping IP Address [Check Hidden?]:</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input class="form-control" type="text" name="req_ip" placeholder="{$rq->remote_ip}">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<input type="checkbox" name="req_hidden" value=1 aria-label="Hidden?">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-default" type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p>If you mark your measurement as hidden, it will not be displayed on the
|
||||||
|
page below. Note that the IP addresses of the target will be shown alongside the result.</p>
|
||||||
|
{{end}}
|
121
pkg/es/driver/resolve-links/resolve-links.go
Normal file
121
pkg/es/driver/resolve-links/resolve-links.go
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package resolvelinks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/sour-is/ev/internal/lg"
|
||||||
|
"github.com/sour-is/ev/pkg/es"
|
||||||
|
"github.com/sour-is/ev/pkg/es/driver"
|
||||||
|
"github.com/sour-is/ev/pkg/es/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resolvelinks struct {
|
||||||
|
up driver.Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *resolvelinks {
|
||||||
|
return &resolvelinks{}
|
||||||
|
}
|
||||||
|
func (r *resolvelinks) Apply(es *es.EventStore) {
|
||||||
|
r.up = es.Driver
|
||||||
|
es.Driver = r
|
||||||
|
}
|
||||||
|
func (r *resolvelinks) Unwrap() driver.Driver {
|
||||||
|
return r.up
|
||||||
|
}
|
||||||
|
func (r *resolvelinks) Open(ctx context.Context, dsn string) (driver.Driver, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
return r.up.Open(ctx, dsn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resolvelinks) EventLog(ctx context.Context, streamID string) (driver.EventLog, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
l, err := r.up.EventLog(ctx, streamID)
|
||||||
|
return &wrapper{l, r}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrapper struct {
|
||||||
|
up driver.EventLog
|
||||||
|
resolvelinks *resolvelinks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) Read(ctx context.Context, after int64, count int64) (event.Events, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
events, err := w.up.Read(ctx, after, count)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, e := range events {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *event.EventPtr:
|
||||||
|
d, err := w.resolvelinks.EventLog(ctx, e.StreamID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lis, err := d.ReadN(ctx, e.Pos)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
events[i] = lis.First()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) ReadN(ctx context.Context, index ...uint64) (event.Events, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
events, err := w.up.ReadN(ctx, index...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, e := range events {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *event.EventPtr:
|
||||||
|
d, err := w.resolvelinks.EventLog(ctx, e.StreamID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lis, err := d.ReadN(ctx, e.Pos)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
events[i] = lis.First()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) Append(ctx context.Context, events event.Events, version uint64) (uint64, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
return w.up.Append(ctx, events, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) FirstIndex(ctx context.Context) (uint64, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
return w.up.FirstIndex(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) LastIndex(ctx context.Context) (uint64, error) {
|
||||||
|
ctx, span := lg.Span(ctx)
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
return w.up.LastIndex(ctx)
|
||||||
|
}
|
|
@ -137,7 +137,13 @@ func (m Meta) Created() time.Time {
|
||||||
func (m Meta) GetEventID() string { return m.EventID.String() }
|
func (m Meta) GetEventID() string { return m.EventID.String() }
|
||||||
|
|
||||||
func Init(ctx context.Context) error {
|
func Init(ctx context.Context) error {
|
||||||
return Register(ctx, NilEvent, &EventPtr{})
|
if err := Register(ctx, NilEvent, &EventPtr{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := RegisterName(ctx, "event.eventPtr", &EventPtr{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type nilEvent struct{}
|
type nilEvent struct{}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user