Compare commits

..

159 Commits

Author SHA1 Message Date
AJ ONeal 156c07a099 merge in mainline commercial (use our lib/server.js) 2018-10-07 20:54:26 -06:00
AJ ONeal 4d3be39043 ignore permission files 2018-10-08 01:28:29 +00:00
AJ ONeal a4b39215ea add perms to email 2018-10-08 01:19:24 +00:00
AJ ONeal 6b619c8121 add utahjs conf 2018 links 2018-10-08 01:18:48 +00:00
AJ ONeal dad775fcf6 output emails as list as well 2018-10-08 01:14:55 +00:00
AJ ONeal dbe3088908 add changelog as notes.txt 2018-10-08 01:13:25 +00:00
AJ ONeal 285ee71486 ignore dist (binary) files 2018-10-08 01:12:49 +00:00
AJ ONeal 04e399cf37 add optify (conceptual) 2018-10-08 01:09:29 +00:00
AJ ONeal 2bbfaeda2f partial for #1: adjust font size and media query 2018-10-08 01:07:43 +00:00
AJ ONeal 7c5a451a9a update contact form 2018-09-17 05:38:23 +00:00
John Shaver f0fda13b16 Added back serviceport when taking user to their new domain. 2018-09-14 01:40:51 -07:00
John Shaver 8046018dba Removed unecessary font from login page 2018-09-14 01:40:03 -07:00
John Shaver 7f2bd0a591 Added preloading of font files. 2018-09-13 23:34:40 -07:00
John Shaver 07468b41b2 More styling tweaks and word changes. 2018-09-13 22:06:04 -07:00
John Shaver 40878066d1 fixed scrolling of debug tray button 2018-09-13 17:14:25 -07:00
John Shaver bba44656f7 more style changes 2018-09-13 17:11:57 -07:00
John Shaver 408a049da2 Removed uneccessary extra css file 2018-09-13 16:48:49 -07:00
John Shaver 137bf18c4e All but the final page 2018-09-13 16:44:38 -07:00
John Shaver 27ae2aeb48 Fixed disabled button 2018-09-13 00:39:33 -07:00
John Shaver e77468106d Added button and input styling 2018-09-12 21:32:39 -07:00
John Shaver 947971dc3d Merge branch 'commercial' of ssh://git.ppl.family:42022/ppl/commercial.telebit-relay.js into commercial 2018-09-12 09:39:14 -07:00
John Shaver cde0525f63 Started on styling for first page. 2018-09-12 09:31:34 -07:00
AJ ONeal 2b79ca6b81 Merge branch 'commercial' of https://git.ppl.family/ppl/commercial.telebit-relay.js into commercial 2018-09-12 06:08:15 +00:00
AJ ONeal f34f78cbfb simplify the sclient params 2018-09-12 06:08:02 +00:00
John Shaver 682557dc86 Added fonts to login site. 2018-09-11 10:55:18 -07:00
AJ ONeal 6e1320fe9b update telebit homepage 2018-09-11 02:39:40 +00:00
AJ ONeal 883f8a55b1 reduce pairing copy 2018-09-11 02:35:31 +00:00
AJ ONeal c2165578e1 sclient: updated docs for v1.2 2018-09-03 22:27:45 +00:00
AJ ONeal 00ad13efc7 Merge branch 'public' into commercial 2018-09-01 06:40:54 +00:00
AJ ONeal e6e8113cca separate server from relay 2018-09-01 06:40:43 +00:00
AJ ONeal 374d6ee34b Merge branch 'public' into commercial 2018-09-01 06:09:23 +00:00
AJ ONeal 0fa68eef1e don't fail vhost renewals 2018-09-01 06:03:42 +00:00
AJ ONeal 22e3730c4c update web copy 2018-09-01 05:58:44 +00:00
AJ ONeal 2fc1ac7536 Merge branch 'master' into commercial-next 2018-08-21 03:13:05 +00:00
AJ ONeal eedf9fe896 Merge branch 'master' into commercial-next 2018-08-21 03:04:56 +00:00
AJ ONeal 34214e9afb remerge with master 2018-08-21 03:04:07 +00:00
AJ ONeal 7d11f15ebe handle remote push of domains 2018-08-21 02:53:38 +00:00
AJ ONeal 739f86f1cc WIP device domain push 2018-08-20 20:12:28 +00:00
AJ ONeal e6c2282fcf merge with master (which was merged with stashed) 2018-08-20 19:49:54 +00:00
AJ ONeal ec3f626560 show devices 2018-08-20 19:38:58 +00:00
AJ ONeal 168b2edfd6 workaround logout bug 2018-08-20 17:42:59 +00:00
AJ ONeal ec65fe31cc update email copy 2018-08-19 16:33:01 +00:00
AJ ONeal bd15f45d1d Support wildcard domains, instruct on CNAME value 2018-08-19 16:25:36 +00:00
AJ ONeal 2564b750e6 dns check complete 2018-08-19 07:33:03 +00:00
John Shaver d755b94bcd More mobile tweaks. 2018-08-17 01:15:40 -07:00
John Shaver 5e0b30f69e Hopefully fixed display issues with iOS safari. 2018-08-17 00:34:09 -07:00
John Shaver 17d46bdcd5 Partially implemented mobile break points. 2018-08-15 14:18:12 -07:00
AJ ONeal 2819117f10 Merge remote-tracking branch 'commercial/commercial' into commercial-next 2018-08-14 22:36:51 +00:00
AJ ONeal 222848cdd1 update copy 2018-08-14 04:22:11 +00:00
AJ ONeal d2df0d6ab3 [admin] update copy 2018-08-14 04:16:03 +00:00
AJ ONeal aad7c51834 [style] update width to show full examples 2018-08-13 16:22:21 +00:00
AJ ONeal 616458a87a WIP placeholder for authorizations 2018-08-10 09:45:05 +00:00
AJ ONeal b60658ee81 move account db to own file 2018-08-10 09:06:16 +00:00
AJ ONeal f54c4dde7a WIP challenge domain 2018-08-10 08:48:06 +00:00
AJ ONeal 8fe1f4d82a move js to own file 2018-08-10 07:44:42 +00:00
AJ ONeal 40ab6a4bb8 merge master v0.20.x 2018-08-08 09:11:48 +00:00
AJ ONeal acf8522195 tested working pair code 2018-08-08 08:22:56 +00:00
AJ ONeal f828839b4d more obvious bugfixes 2018-08-08 02:09:17 -06:00
AJ ONeal 60ee3720e0 obvious bugfixes 2018-08-08 01:59:51 -06:00
AJ ONeal 5b7f19e7a8 add sclient docs 2018-08-08 06:28:03 +00:00
AJ ONeal 09660b0731 show domains, fix date 2018-08-05 08:11:54 +00:00
AJ ONeal 6972112782 example config file 2018-08-05 07:32:07 +00:00
AJ ONeal 004908e735 add some more info to domain list 2018-08-05 07:20:06 +00:00
AJ ONeal 4e3e155460 chimney 2018-08-05 06:09:00 +00:00
AJ ONeal 5940d4bd28 convert files to json db 2018-08-05 05:48:31 +00:00
AJ ONeal e4534d6076 add createdAt 2018-08-05 05:48:17 +00:00
AJ ONeal c3c4f8893f bunch of typo and index bugfixes 2018-08-05 04:40:00 +00:00
AJ ONeal 01c24d7eec update savefile location 2018-08-05 02:45:10 +00:00
AJ ONeal 4f2948d0f0 bugfixes 2018-08-04 10:36:27 +00:00
AJ ONeal a2b23b2509 add commercial LICENSE 2018-08-04 10:22:38 +00:00
AJ ONeal 64281e4c93 WIP implementing semi-proper accounts 2018-08-04 10:21:06 +00:00
John Shaver 1099a75509 Added more use cases to sliding text. 2018-08-02 21:53:06 -07:00
John Shaver 353329120f Updated comment on newsletter subscriptions 2018-08-02 15:26:38 -07:00
AJ ONeal c44608cbe7 Merge branch 'master' into commercial 2018-08-02 20:54:04 +00:00
AJ ONeal ae43b0859b note to be account-specific 2018-08-02 20:53:30 +00:00
AJ ONeal b386857d99 choose any of the shared domains 2018-08-02 20:48:35 +00:00
AJ ONeal d63b6cb370 professional landing page 2018-07-26 16:50:29 +00:00
AJ ONeal e611d945ed updates 2018-07-08 03:02:09 +00:00
AJ ONeal de9aab8195 Merge branch 'commercial' of https://git.ppl.family/ppl/commercial.telebit-relay.js into commercial 2018-07-08 02:57:42 +00:00
AJ ONeal 79e9728d19 better error handling 2018-07-08 02:57:33 +00:00
AJ ONeal 5aed381963 excessively log pairing states 2018-07-08 02:50:03 +00:00
AJ ONeal b5fd940429 check sendMail return code 2018-07-07 22:31:41 +00:00
AJ ONeal d12cddb1aa update urequest for form-data fix 2018-07-07 20:38:36 +00:00
AJ ONeal f5126ad8ed merge 2018-07-07 09:47:53 +00:00
AJ ONeal 36ab30c9f2 remove bluebird 2018-07-07 09:47:04 +00:00
AJ ONeal 4e459ea617 handle accounts 2018-07-07 09:45:33 +00:00
AJ ONeal c045e4c712 add login 2018-07-06 08:13:59 +00:00
AJ ONeal 16e30124e6 Merge branch 'master' into commercial 2018-06-30 23:10:33 +00:00
AJ ONeal d60b458f47 more efficient checking 2018-06-30 23:10:18 +00:00
AJ ONeal 3773abdfdb coolaj86/telebit.js#11 wildcards forward by default 2018-06-30 23:10:04 +00:00
AJ ONeal 6a1df2ee05 coolaj86/telebit.js#11 wildcards forward by default 2018-06-30 23:09:38 +00:00
AJ ONeal 4bef0187f1 Merge branch 'master' into commercial 2018-06-29 22:14:10 +00:00
AJ ONeal db4e5c4f60 don't use old socket 2018-06-29 22:14:08 +00:00
AJ ONeal 44cca52f50 Merge branch 'master' into commercial 2018-06-29 21:40:18 +00:00
AJ ONeal cda10951d8 quiet down 2018-06-29 21:40:10 +00:00
AJ ONeal 5deefa9832 grant pre-authorized tokens, duh 2018-06-29 21:39:49 +00:00
AJ ONeal 021629ea68 Merge branch 'master' into commercial 2018-06-29 20:06:08 +00:00
AJ ONeal 6b6ffd4647 name more clearly 2018-06-29 20:05:59 +00:00
AJ ONeal 57f1de5f2d regression fix: pass updated jwts with grant 2018-06-29 20:05:24 +00:00
AJ ONeal bbee698322 disable auto-https triggering for now 2018-06-29 12:07:25 +00:00
AJ ONeal cc58fcb98a Merge branch 'master' into commercial 2018-06-29 12:06:47 +00:00
AJ ONeal 5b90d5ef38 correct order of operations 2018-06-29 12:06:34 +00:00
AJ ONeal 8aecbb1f56 return to former glory 2018-06-29 12:05:29 +00:00
AJ ONeal 40d54bcdad Merge branch 'master' into commercial 2018-06-29 11:02:55 +00:00
AJ ONeal a3b8cd6799 better logging 2018-06-29 11:02:44 +00:00
AJ ONeal dc67bee735 passthru authn and await authz, better logging 2018-06-29 11:02:13 +00:00
AJ ONeal 7fd28d55a1 be more specific with your feedback, please (and fix missing email addr) 2018-06-27 20:26:45 +00:00
AJ ONeal 589e4af90b merge 2018-06-23 01:09:39 +00:00
AJ ONeal 6e60fc1750 Merge branch 'master' of ssh://git.coolaj86.com:22042/coolaj86/telebit-relay.js 2018-06-23 01:08:19 +00:00
AJ ONeal 6c2f039533 add subdomain config 2018-06-23 01:08:11 +00:00
AJ ONeal cc2823a56a Merge branch 'master' into commercial 2018-06-21 20:16:57 +00:00
AJ ONeal cc5cbea486 Merge branch 'master' into commercial 2018-06-21 20:03:36 +00:00
AJ ONeal 9cd489f575 Merge branch 'master' into commercial 2018-06-21 20:00:11 +00:00
AJ ONeal 5d065c8bd0 mark token as claimed if a socket already exists 2018-06-21 19:59:44 +00:00
AJ ONeal a9353a9a75 resolve promise on pair 2018-06-21 19:48:04 +00:00
AJ ONeal 06b894a99d remove excess logging 2018-06-21 19:38:03 +00:00
AJ ONeal d2fde5c38c better logging 2018-06-21 19:36:21 +00:00
AJ ONeal 4db9a8d53d clear error message 2018-06-21 11:19:10 +00:00
AJ ONeal 809c14f91f check status for complete and invalid 2018-06-21 11:02:53 +00:00
AJ ONeal 79648af88c check invalid and complete statuses 2018-06-21 06:23:16 +00:00
AJ ONeal 148cda8516 handle pairing request via API 2018-06-21 06:13:05 +00:00
AJ ONeal 179256a88e mo' betta' 2018-06-20 09:02:00 +00:00
AJ ONeal 95f9ca0844 Merge branch 'master' into c-accounts 2018-06-19 23:43:50 +00:00
AJ ONeal 33a565b19a add urequest 2018-06-19 23:41:41 +00:00
AJ ONeal 114cc53dd4 WIP api token for accounts 2018-06-19 23:40:58 +00:00
AJ ONeal 09b1d5939e so commercial 2018-06-15 22:50:32 +00:00
AJ ONeal 2603c322bd Merge branch 'commercial' of https://git.ppl.family/ppl/commercial.telebit-relay.js into commercial 2018-06-15 09:00:21 +00:00
AJ ONeal 96631d1369 nearly works again :) 2018-06-15 09:00:16 +00:00
AJ ONeal 8a1e2f14e0 Merge branch 'master' into commercial 2018-06-15 08:47:14 +00:00
AJ ONeal 0013930dbd updates 2018-06-15 08:45:47 +00:00
AJ ONeal 72986ccdc1 Merge branch 'master' into commercial 2018-06-15 06:13:43 +00:00
AJ ONeal ff52d1e4a8 Merge branch 'commercial' of https://git.ppl.family/ppl/commercial.telebit-relay.js into commercial 2018-06-15 06:10:15 +00:00
AJ ONeal c1c7dfba02 Merge branch 'master' into commercial 2018-06-15 06:10:06 +00:00
AJ ONeal d6f517ed2b Merge branch 'commercial' of https://git.ppl.family/ppl/commercial.telebit-relay.js into commercial 2018-06-15 05:17:11 +00:00
AJ ONeal f8ac7baa1a Merge branch 'master' into commercial 2018-06-15 05:16:58 +00:00
AJ ONeal f0df611886 fix ssh doc bug 2018-06-15 05:05:18 +00:00
AJ ONeal f8c7517b14 Merge branch 'master' into commercial 2018-06-14 21:19:39 +00:00
AJ ONeal f704a9fa19 Merge branch 'master' into commercial 2018-06-14 21:14:16 +00:00
AJ ONeal 2e36d903ae de-hard-code TELEBIT_RELAY_PATH 2018-06-14 20:51:50 +00:00
AJ ONeal 93c864d4d4 read from /dev/tty direct 2018-06-14 20:45:51 +00:00
AJ ONeal 7de7cc651d update some stuff... 2018-06-14 20:43:57 +00:00
AJ ONeal 97e5a847b8 more output 2018-06-14 10:43:25 +00:00
AJ ONeal 20f59b6af7 Merge branch 'master' into commercial 2018-06-14 10:09:58 +00:00
AJ ONeal f2c983a129 fix some sni and vhost stuff 2018-06-14 09:59:19 +00:00
AJ ONeal 06540352bf update docs, add versions and upgrade script 2018-06-14 09:57:43 +00:00
AJ ONeal 63065d6d65 Merge branch 'master' into commercial 2018-06-09 20:56:00 +00:00
AJ ONeal 5e1963d11f improve email 2018-06-09 20:46:08 +00:00
AJ ONeal 34b86bc7a3 save data better 2018-06-08 08:25:06 +00:00
AJ ONeal 08833123b2 update messaging 2018-06-08 08:23:05 +00:00
AJ ONeal 4b44632f2e add contact form 2018-06-07 08:06:09 +00:00
AJ ONeal d8e54c179d Merge branch 'master' into commercial 2018-06-07 07:47:57 +00:00
AJ ONeal a9b1c4204b update goofy error message to be useful 2018-06-07 07:35:39 +00:00
AJ ONeal 46599c864c add commercial interface 2018-06-07 05:26:15 +00:00
AJ ONeal ab35fdc40e Merge branch 'master' into commercial 2018-06-07 01:39:27 +00:00
AJ ONeal 114f298e8e Merge branch 'master' into commercial 2018-06-06 10:58:22 +00:00
AJ ONeal 2b9fadf4b4 working new account 2018-06-06 10:58:01 +00:00
AJ ONeal 2b2a0021aa Merge branch 'master' into commercial 2018-06-06 09:44:49 +00:00
AJ ONeal ee1f713bd3 Merge branch 'master' into commercial 2018-06-06 09:21:01 +00:00
AJ ONeal ad3b2e9167 working on auto-auth 2018-06-06 09:18:13 +00:00
67 changed files with 4291 additions and 335 deletions

14
.gitignore vendored
View File

@ -1,3 +1,8 @@
emails
lib/extensions/permissions.json
lib/extensions/permissions.json.bak
lib/extensions/admin/sclient/dist/
lib/extensions/admin/optify/dist/
node_modules.*
include
bin/node
@ -43,12 +48,3 @@ jspm_packages
# Optional REPL history
.node_repl_history
# Snapcraft
/parts/
/prime/
/stage/
.snapcraft
*.snap
*.tar.bz2

View File

@ -2,6 +2,10 @@
(function () {
'use strict';
var fs = require('fs');
var path = require('path');
var os = require('os');
var pkg = require('../package.json');
var argv = process.argv.slice(2);
@ -47,13 +51,7 @@ function applyConfig(config) {
} else {
state.Promise = require('bluebird');
}
state.tlsOptions = {
// Handles disconnected devices
// TODO allow user to opt-in to wildcard hosting for a better error page?
SNICallback: function (servername, cb) {
return state.greenlock.tlsOptions.SNICallback(state.config.webminDomain || state.servernames[0], cb);
}
}; // TODO just close the sockets that would use this early? or use the admin servername
state.tlsOptions = {}; // TODO just close the sockets that would use this early? or use the admin servername
state.config = config;
state.servernames = config.servernames || [];
state.secret = state.config.secret;
@ -73,54 +71,59 @@ function applyConfig(config) {
state.config.greenlock.configDir = require('os').homedir() + require('path').sep + 'acme';
}
// The domains being approved for the first time are listed in opts.domains
// Certs being renewed are listed in certs.altnames
function approveDomains(opts, certs, cb) {
if (state.debug) { console.log('[debug] approveDomains', opts.domains); }
// This is where you check your database and associated
// email addresses with domains and agreements and such
// The domains being approved for the first time are listed in opts.domains
// Certs being renewed are listed in certs.altnames
if (certs) {
opts.domains = certs.altnames;
cb(null, { options: opts, certs: certs });
return;
}
if (!state.validHosts) { state.validHosts = {}; }
if (!state.validHosts[opts.domains[0]] && state.config.vhost) {
if (state.debug) { console.log('[sni] vhost checking is turned on'); }
var vhost = state.config.vhost.replace(/:hostname/, opts.domains[0]);
require('fs').readdir(vhost, function (err, nodes) {
if (state.debug) { console.log('[sni] checking fs vhost', opts.domains[0], !err); }
if (err) { check(); return; }
if (nodes) { approve(); }
});
return;
}
function approve() {
function allow() {
state.validHosts[opts.domains[0]] = true;
opts.email = state.config.email;
opts.agreeTos = state.config.agreeTos;
opts.communityMember = state.config.communityMember || state.config.greenlock.communityMember;
opts.challenges = {
// TODO dns-01
'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' })
'http-01': require('le-challenge-fs').create({ webrootPath: path.join(os.tmpdir(), 'acme-challenges') })
};
opts.communityMember = state.config.communityMember;
cb(null, { options: opts, certs: certs });
}
function check() {
if (state.debug) { console.log('[sni] checking servername'); }
if (-1 !== state.servernames.indexOf(opts.domain) || -1 !== (state._servernames||[]).indexOf(opts.domain)) {
approve();
} else {
cb(new Error("failed the approval chain '" + opts.domains[0] + "'"));
}
function deny() {
cb(new Error("[bin/telebit-relay.js] failed the approval chain '" + opts.domains[0] + "'"));
return;
}
check();
// 1) If the host was already allowed => allow
if (!state.validHosts) { state.validHosts = {}; }
if (state.validHosts[opts.domains[0]]) {
allow();
return;
}
// 2) If the host is in the config => allow
if (state.debug) { console.log('[sni] checking servername'); }
if (-1 !== state.servernames.indexOf(opts.domain)
|| -1 !== (state._servernames||[]).indexOf(opts.domain)) {
allow();
return;
}
// 3) If dynamic vhosting is allowed
// & a vhost folder exist for this domain => allow
if (state.config.vhost) {
if (state.debug) { console.log('[sni] vhost checking is turned on'); }
var vhost = state.config.vhost.replace(/:hostname/, opts.domains[0]);
require('fs').readdir(vhost, function (err, nodes) {
if (state.debug) { console.log('[sni] checking fs vhost', opts.domains[0], !err); }
if (err) { deny(); return; }
if (nodes) { allow(); }
});
return;
}
// 4) fallback => fail
deny();
}
state.greenlock = Greenlock.create({
@ -202,7 +205,7 @@ function applyConfig(config) {
//});
}
require('fs').readFile(confpath, 'utf8', function (err, text) {
fs.readFile(confpath, 'utf8', function (err, text) {
var config;
var recase = require('recase').create({});

View File

@ -0,0 +1,27 @@
email: jon@example.com # must be valid (for certificate recovery and security alerts)
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data
webmin_domain: telebit.example.com
api_domain: api.telebit.example.com
shared_domain: telebit.example.com
shared_domains:
- telebit.example.com
servernames: # hostnames that direct to the Telebit Relay admin console
- telebit.example.com
- www.telebit.example.com
- api.telebit.example.com
vhost: /srv/www/:hostname # load secure websites at this path (uses template string, i.e. /var/www/:hostname/public)
trusted_issuers:
- oauth3.org
mailer:
url: 'https://api.mailgun.net/v3/example.com/messages'
api_key: 'key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
from: 'Telebit Wizard <wizard@example.com>'
greenlock:
version: 'draft-11'
server: 'https://acme-v02.api.letsencrypt.org/directory'
store:
strategy: le-store-certbot # certificate storage plugin
config_dir: /opt/telebit-relay/etc/acme # directory for ssl certificates
secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # generate with node -e "console.log(crypto.randomBytes(16).toString('hex'))"

View File

@ -1,17 +1,24 @@
email: 'jon@example.com' # must be valid (for certificate recovery and security alerts)
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data
email: coolaj86@gmail.com
agree_tos: true
community_member: true
telemetry: true
webmin_domain: example.com
shared_domain: xm.pl
servernames: # hostnames that direct to the Telebit Relay admin console
- telebit.example.com
- telebit.example.net
vhost: /srv/www/:hostname # load secure websites at this path (uses template string, i.e. /var/www/:hostname/public)
api_domain: example.com
shared_domain: example.com
servernames:
- www.example.com
- example.com
- api.example.com
vhost: /srv/www/:hostname
greenlock:
version: 'draft-11'
server: 'https://acme-v02.api.letsencrypt.org/directory'
store:
strategy: le-store-certbot # certificate storage plugin
config_dir: /etc/acme # directory for ssl certificates
secret: '' # generate with node -e "console.log(crypto.randomBytes(16).toString('hex'))"
strategy: le-store-certbot
config_dir: /opt/telebit-relay/etc/acme
secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
mailer:
url: 'https://api.mailgun.net/v3/EXAMPLE.COM/messages'
api_key: 'key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
from: 'Example Mailer <MALIER@EXAMPLE.COM>'
debug: true

View File

@ -0,0 +1,5 @@
servernames: [ 'telebit.cloud' ]
email: 'coolaj86@gmail.com'
agree_tos: true
community_member: false
vhost: /srv/www/:hostname

View File

@ -179,6 +179,9 @@ if [ ! -f "$TELEBIT_RELAY_PATH/etc/$my_app.yml" ]; then
sudo bash -c "echo 'email: $my_email' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'secret: $my_secret' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'servernames: [ $my_servername ]' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'webmin_domain: $my_servername' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'api_domain: $my_servername' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "echo 'shared_domain: $my_servername' >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
sudo bash -c "cat $TELEBIT_RELAY_PATH/examples/$my_app.yml.tpl >> $TELEBIT_RELAY_PATH/etc/$my_app.yml"
fi

View File

@ -1,35 +0,0 @@
'use strict';
var timeago = require('./ago.js').AGO;
function test() {
[ 1.5 * 1000 // a moment ago
, 4.5 * 1000 // moments ago
, 10 * 1000 // 10 seconds ago
, 59 * 1000 // a minute ago
, 60 * 1000 // a minute ago
, 61 * 1000 // a minute ago
, 119 * 1000 // a minute ago
, 120 * 1000 // 2 minutes ago
, 121 * 1000 // 2 minutes ago
, (60 * 60 * 1000) - 1000 // 59 minutes ago
, 1 * 60 * 60 * 1000 // an hour ago
, 1.5 * 60 * 60 * 1000 // an hour ago
, 2.5 * 60 * 60 * 1000 // 2 hours ago
, 1.5 * 24 * 60 * 60 * 1000 // a day ago
, 2.5 * 24 * 60 * 60 * 1000 // 2 days ago
, 7 * 24 * 60 * 60 * 1000 // a week ago
, 14 * 24 * 60 * 60 * 1000 // 2 weeks ago
, 27 * 24 * 60 * 60 * 1000 // 3 weeks ago
, 28 * 24 * 60 * 60 * 1000 // 4 weeks ago
, 29 * 24 * 60 * 60 * 1000 // 4 weeks ago
, 1.5 * 30 * 24 * 60 * 60 * 1000 // a month ago
, 2.5 * 30 * 24 * 60 * 60 * 1000 // 2 months ago
, (12 * 30 * 24 * 60 * 60 * 1000) + 1000 // 12 months ago
, 13 * 30 * 24 * 60 * 60 * 1000 // over a year ago
].forEach(function (d) {
console.log(d, '=', timeago(d));
});
}
test();

View File

@ -1,50 +0,0 @@
;(function (exports) {
'use strict';
exports.AGO = function timeago(ms) {
var ago = Math.floor(ms / 1000);
var part = 0;
if (ago < 2) { return "a moment ago"; }
if (ago < 5) { return "moments ago"; }
if (ago < 60) { return ago + " seconds ago"; }
if (ago < 120) { return "a minute ago"; }
if (ago < 3600) {
while (ago >= 60) { ago -= 60; part += 1; }
return part + " minutes ago";
}
if (ago < 7200) { return "an hour ago"; }
if (ago < 86400) {
while (ago >= 3600) { ago -= 3600; part += 1; }
return part + " hours ago";
}
if (ago < 172800) { return "a day ago"; }
if (ago < 604800) {
while (ago >= 172800) { ago -= 172800; part += 1; }
return part + " days ago";
}
if (ago < 1209600) { return "a week ago"; }
if (ago < 2592000) {
while (ago >= 604800) { ago -= 604800; part += 1; }
return part + " weeks ago";
}
if (ago < 5184000) { return "a month ago"; }
if (ago < 31536001) {
while (ago >= 2592000) { ago -= 2592000; part += 1; }
return part + " months ago";
}
if (ago < 315360000) { // 10 years
return "more than year ago";
}
// TODO never
return "";
};
}('undefined' !== typeof module ? module.exports : window));

View File

@ -1,7 +1,6 @@
'use strict';
var Devices = module.exports;
// TODO enumerate store's keys and device's keys for documentation
Devices.addPort = function (store, serverport, newDevice) {
// TODO make special
return Devices.add(store, serverport, newDevice, true);
@ -15,7 +14,6 @@ Devices.add = function (store, servername, newDevice, isPort) {
if (!store._domains) { store._domains = {}; }
if (!store._domains[servername]) { store._domains[servername] = []; }
store._domains[servername].push(newDevice);
Devices.touch(store, servername);
// add device
// TODO only use a device id
@ -97,6 +95,7 @@ Devices.list = function (store, servername) {
// aliases have ._primary which is the name of the original
return store._domains[servername]._primary && store._domains[store._domains[servername]._primary] || store._domains[servername];
}
// There wasn't an exact match so check any of the wildcard domains, sorted longest
// first so the one with the biggest natural match with be found first.
var deviceList = [];
@ -105,7 +104,10 @@ Devices.list = function (store, servername) {
}).sort(function (a, b) {
return b.length - a.length;
}).some(function (pattern) {
// '.example.com' = '*.example.com'.split(1)
var subPiece = pattern.slice(1);
// '.com' = 'sub.example.com'.slice(-4)
// '.example.com' = 'sub.example.com'.slice(-12)
if (subPiece === servername.slice(-subPiece.length)) {
console.log('[Devices.list] "'+servername+'" matches "'+pattern+'"');
deviceList = store._domains[pattern];
@ -128,11 +130,7 @@ Devices.active = function (store, id) {
};
*/
Devices.exist = function (store, servername) {
if (Devices.list(store, servername).length) {
Devices.touch(store, servername);
return true;
}
return false;
return !!(Devices.list(store, servername).length);
};
Devices.next = function (store, servername) {
var devices = Devices.list(store, servername);
@ -144,20 +142,5 @@ Devices.next = function (store, servername) {
device = devices[devices._index || 0];
devices._index = (devices._index || 0) + 1;
if (device) { Devices.touch(store, servername); }
return device;
};
Devices.touchDevice = function (store, device) {
// TODO use device.id (which will be pubkey thumbprint) and store._devices[id].domainsMap
Object.keys(device.domainsMap).forEach(function (servername) {
Devices.touch(store, servername);
});
};
Devices.touch = function (store, servername) {
if (!store._recency) { store._recency = {}; }
store._recency[servername] = Date.now();
};
Devices.lastSeen = function (store, servername) {
if (!store._recency) { store._recency = {}; }
return store._recency[servername] || 0;
};

3
lib/extensions/LICENSE Normal file
View File

@ -0,0 +1,3 @@
commercial
Copyright ppl 2018

View File

@ -0,0 +1 @@
_apis

View File

@ -0,0 +1 @@
oauth3.org

View File

@ -0,0 +1 @@
../assets/oauth3.org/_apis/oauth3.org

View File

@ -0,0 +1,11 @@
{ "terms_of_service": ":hostname/tos/"
, "api_host": "api.:hostname"
, "pair_request": {
"method": "POST"
, "pathname": "api/telebit.cloud/pair_request"
}
, "tunnel": {
"method": "wss"
, "pathname": ""
}
}

View File

@ -0,0 +1,90 @@
<html>
<head>
<title>Telebit Account</title>
</head>
<body>
<div class="v-app">
<div v-if="spinner" style="position: absolute; width: 100%; height: 100%; background-color: #ddd;">Loading... </div>
<div v-if="!hasAccount">
<h1>Login</h1>
<form class="js-auth-form" v-on:submit.prevent="login()">
<input class="js-auth-subject" v-model="newEmail" placeholder="email" type="email" required/>
<button class="js-auth-submit" type="submit">Login</button>
</form>
</div>
<div v-if="hasAccount">
<h1>Account</h1>
<button v-on:click.prevent.stop="logout()" type="click">Logout</button>
<!-- not yet -->
<!--form v-on:submit.prevent="challengeEmail()">
Authorize another email:
<input v-model="newEmail" placeholder="jon@example.com" type="email" required/>
<button type="submit">Next</button>
</form-->
<div v-if="claims.length">
<h3>Pending Claims</h3>
<p>If your DNS host supports ANAME records, please use those instead of CNAMEs.</p>
<p>If CNAMEs are not supported, set an A record to {{ site.deviceDomainA }}.</p>
<ol>
<li v-for="claim in claims">
<span>{{ claim.value }}</span>
<br>
<span v-if="'dns' === claim.type">CNAME <span v-if="claim.wildcard">*.</span>{{ claim.value }}: {{ site.deviceDomain }}</span>
<br>
<span v-if="'dns' === claim.type">TXT _claim-challenge.{{ claim.value }}: {{ claim.challenge }}</span>
<br>
<button v-on:click.prevent="checkDns(claim)">Check</button>
</li>
</ol>
</div>
<h3>Devices</h3>
<div v-if="!devices.length">
You can add up to 5 devices:
<pre><code>curl -sf https://get.telebit.io/ | bash</code></pre>
</div>
<div v-if="devices.length">
<ol>
<li v-for="device in devices">
<span v-if="device.id">{{ device.id }}</span> {{ device.socketId }}
<ul>
<li><form v-on:submit.stop.prevent="pushDomain(device)">
<input type="text" v-model="device.newDomain" placeholder="ex: jon.telebit.com"></input><button type="submit">Push</button>
</form></li>
<li v-for="name in device.names">{{ name }}</li>
</ul>
</li>
</ol>
</div>
<h3>Domains</h3>
<form v-on:submit.prevent="challengeDns()">
Add a custom domain:
<input v-model="newDomain" placeholder="example.com" type="text" required/>
<button type="submit">Next</button>
</form>
<div v-if="domains.length">
<ol>
<li v-for="domain in domains">
<span v-if="domain.wildcard">*.</span>{{ domain.name }} <span v-if="domain.hostname">- {{domain.hostname}} ({{domain.os}} {{domain.arch}})</span>
</li>
</ol>
</div>
<h3>Debug: Token</h3>
<pre><code v-text="token"></code></pre>
</div>
</div>
<!-- development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="assets/oauth3.org/oauth3.core.js"></script>
<script src="js/account.js"></script>
</body>
</html>

@ -0,0 +1 @@
Subproject commit 8e2e09f5823ae919c615c9c3b21114e01096b1ee

4
lib/extensions/admin/dist/index.tab vendored Normal file
View File

@ -0,0 +1,4 @@
channel version date
prod v0.15.4 2018-06-14T09:37:22Z
beta v0.15.3 2018-06-14T07:07:21Z
prod v0.12.0 2018-06-07T07:43:21Z

86
lib/extensions/admin/dist/notes.txt vendored Normal file
View File

@ -0,0 +1,86 @@
Release Notes
=============
Table of Contents
* v0.20.6 - protocol upgrade
Re: v0.20.6
===========
Saturday, Sept 29, 2018
This version is a required update. I had to make some changes to the network
protocol that were easy enough to make backwards-compatible in the client, but
not worth the effort to do so on the server.
Mac, Linux, Raspberry Pi Users:
-------------------------------
curl -fsSL https://get.telebit.io | bash
That should be quick and easy, but you may need to reboot your computer.
Windows & npm users
-------------------
npm install -g npm
Note that on Windows the upgrade will **NOT** work while Telebit is
running. `telebit restart` should kill it but, on Windows, won't actually
restart it.
This is not well tested, so please contact me (aj@ppl.family) if you have any
trouble.
Upgrading *really* old versions
---------------------
If you have a version of telebit prior to v0.18.1 (which may not even list its
version in `telebit help` yet), it'll probably be easiest to manually remove
the old telebit files first:
sudo rm -rf ~/Applications/telebit* ~/.config/telebit*
sudo rm -rf /opt/telebit* /etc/telebit* /etc/systemd/system/telebit*
You'll lose your current domain. If that's an issue, contact me and we can work
it out.
Rationale
---------
> "If it ain't broke, don't fix it" - Ancient Redneck Proverb
> "When is broke, is most right time to fix" - Ageless Chinese Adage
There's a delicate balance between the two and in my infinite wisdom I've
decided that now is the right time to fix.
There are some rather disruptive bugs in the network protocol and fixing them
means breaking most existing clients.
If you've been using telebit on a daily basis, especially with ssh, I believe
that'll you see benefit immediately and even moreso once the server is updated.
It's worth it.
Additional Notes
----------------
A number of good fixes are in here:
### `telebit help`
The in-app cli help is now correctly documented. Not everything _works_ as
documented, however. Feel free to poke around and give me feedback.
### `telebit ssh none`
Previously `telebit ssh none` behaved identically to `telebit ssh auto`.
The output correctly showed the actual behavior, but it didn't make sense.
Bascially this was happening: `telebit.ssh = telebit.ssh || 22`. So when it
it was `false` it became `true`
It was changed to this `if (!('ssh' in telebit)) { telebit.ssh = 22; }`.

12
lib/extensions/admin/dist/upgrade.js vendored Normal file
View File

@ -0,0 +1,12 @@
'use strict';
module.exports = function (opts, cb) {
var pkg = opts.package;
var root = opts.root;
//console.log('DEBUG pkg', pkg);
//console.log('DEBUG root', root);
process.nextTick(function () {
cb(null, { message: "upgrade complete" });
});
return { message: "placeholder upgrade: nothing to do yet" };
};

View File

@ -0,0 +1,357 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=900">
<title>Telebit&trade; Cloud</title>
<link href="static-site-assets/styles/main.css" rel="stylesheet">
<link href="static-site-assets/styles/vertical-slide.css" rel="stylesheet">
<link href="static-site-assets/styles/1200.css" rel="stylesheet" media="(max-width:1075px)">
<style>
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-display: block;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(/static-site-assets/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: block;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(/static-site-assets/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 400;
src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(/static-site-assets/fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="logo">Telebit</div>
<ul class="navigation-menu">
<li>
<a class="nav-link" target="_blank" href="https://git.coolaj86.com/coolaj86/telebit.js">Docs</a>
</li>
<li>
<a class="nav-link" target="_blank" href="https://www.patreon.com/coolaj86">Donate</a>
</li>
<li>
<a class="link-button nav-link" href="#download-section">Download</a>
</li>
</ul>
</div>
</header><div class="hero">
<div class="container">
<div class="spiel">
<h1>Access your devices
<br>Share your stuff
</h1>
</div>
<div aria-hidden="true" class="demo-row">
<div class="demo-container">
<div class="demo-browser">
<div class="demo-browser-header">
<div class="demo-browser-buttons">
<div></div><div></div><div></div>
</div>
<div class="demo-browser-address-bar">
<img src="static-site-assets/images/green-secure.png">
<div class="demo-browser-url">
https://jondoe.telebit.io
</div>
</div>
</div>
<div class="demo-browser-body">
Hello world!
</div>
</div>
<div class="demo-terminal">
<div class="demo-terminal-input">
telebit http 3000
</div>
<div class="demo-terminal-line">&nbsp;
</div>
<div class="demo-terminal-output">
Forwarding https://jondoe.telebit.io =&gt; localhost:3000
</div>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<div class="container quickstart-container">
<h2 class="use-it">Use it <div class="sliding-vertical">
<!-- to add more of or remove some of these, you will also need to update
./static-site-assets/styles/vertical-slide.css
to allow for the correct number of values. Formulas for calculating
new values are included in the style comments.
-->
<span class="accent-color">to test your webhooks.</span>
<span class="accent-color">to show your project to Mom.</span>
<span class="accent-color">to test your site on mobile.</span>
<span class="accent-color">to work from 127.0.0.1.</span>
<span class="accent-color">to access your raspberry pi.</span>
<span class="accent-color">to build peer-to-peer apps.</span>
</div></h2>
<h2 id="download-section">Quickstart with bash</h2>
<div class="quickstart-step">
<div class="quickstart-step-text">
<div class="quickstart-step-number">1</div>
<div class="quickstart-step-name">Install Telebit</div>
</div>
<pre class="quickstart-terminal qickstart-terminal-prompt">curl https://get.telebit.io/ | bash</pre>
</div>
<div class="quickstart-step">
<div class="quickstart-step-text">
<div class="quickstart-step-number">2</div>
<div class="quickstart-step-name">Claim your device via Email</div>
</div>
<pre class="quickstart-terminal">Hello!
Want to use 'Jon's Macbook Pro' with Telebit?
Just confirm your email address:
<u>Confirm Email Address</u></pre>
</div>
<div class="quickstart-step">
<div class="quickstart-step-text">
<div class="quickstart-step-number">3</div>
<div class="quickstart-step-name">Enjoy Anytime, Anywhere Access</div>
</div>
<pre class="quickstart-terminal"><strong>For Local Development</strong>
<code class="quickstart-input">~/telebit http 3000</code>
<code class="quickstart-output">Forwarding https://jondoe.telebit.io =&gt; localhost:3000</code>
<code class="quickstart-input">curl -fsSL https://jondoe.telebit.io/</code>
<strong>For Sharing Files</strong>
<code class="quickstart-input">~/telebit http ./project.zip</code>
<code class="quickstart-output">Serving ./project.zip as https://jondoe.telebit.io</code>
<code class="quickstart-input">curl -fsSL https://jondoe.telebit.io/</code>
<strong>For Access with SSH</strong>
<code class="quickstart-input">~/telebit ssh auto</code>
<code class="quickstart-output">Forwarding jondoe.telebit.io -p 5050 =&gt; localhost:22</code>
<code class="quickstart-output">Forwarding ssh+https (openssl proxy) =&gt; localhost:22</code>
<code class="quickstart-input">ssh -p 5050 jondoe.telebit.io</code>
<code class="quickstart-input">ssh -o ProxyCommand="<a href="sclient/">sclient</a> %h" jondoe.telebit.io</code>
<strong>For Debugging with TCP</strong>
<code class="quickstart-input">~/telebit tcp 9000</code>
<code class="quickstart-output">Forwarding jondoe.telebit.io -p 5050 =&gt; localhost:9000</code>
<code class="quickstart-input">netcat jondoe.telebit.io 5050</code></pre>
</div>
</div>
</div>
<div class="install-for">
<div class="container">
<h3>Install For</h3>
<div class="install-badges">
<a class="install-badge" target="_blank"
href="https://git.coolaj86.com/coolaj86/telebit.js#windows--nodejs">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#000" fill-rule="nonzero" d="M2
4.819l8.11-1.105.004 7.823-8.107.047L2 4.819zm8.107
7.62l.006 7.83-8.107-1.114v-6.769l8.1.053zm.983-8.87L21.844
2v9.438l-10.754.085V3.57zm10.757 8.944l-.003 9.395L11.09
20.39l-.015-7.895 10.772.018z"/>
</g>
</svg>
<span>Windows</span>
</a>
<a class="install-badge" target="_blank"
href="https://git.coolaj86.com/coolaj86/telebit.js#mac--linux">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path fill="#1A1A1A" d="M21.41 8.22c-1.667 1.179-2.5
2.597-2.5 4.254 0 1.986 1.03 3.509 3.09 4.57-.553 1.6-1.354
2.993-2.402 4.178C18.549 22.407 17.592 23 16.726 23c-.408
0-.965-.135-1.67-.404l-.34-.13c-.69-.27-1.302-.404-1.834-.404-.502
0-1.052.105-1.649.316l-.426.153-.535.218c-.422.167-.848.251-1.277.251-1.012
0-2.13-.833-3.352-2.498C3.88 18.117 3 15.518 3 12.704c0-2
.55-3.61 1.649-4.832 1.1-1.222 2.555-1.833 4.368-1.833.677
0 1.31.124
1.9.371l.404.164.426.174c.378.16.684.24.917.24.298 0
.63-.069.993-.207l.557-.218.415-.153c.663-.24 1.394-.36
2.195-.36 1.9 0 3.429.724 4.586 2.17zM16.911
1c.022.255.033.45.033.589 0 1.258-.458 2.361-1.376
3.31-.917.95-1.983 1.424-3.199 1.424a5.474 5.474 0 0
1-.055-.611c0-1.069.426-2.072 1.278-3.01.852-.938
1.838-1.487 2.96-1.647.08-.015.2-.033.36-.055z"/>
</g>
</svg>
<span>Mac</span>
</a>
<a class="install-badge" target="_blank"
href="https://git.coolaj86.com/coolaj86/telebit.js#mac--linux">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 2)">
<rect width="22" height="20" fill="#000" rx="1"/>
<path fill="#FFF" d="M6.495 3.942v1.125l-4.12
1.566V5.551l2.882-1.047-2.882-1.056V2.375l4.12
1.567zm.32 3.592h4.327v.779H6.814v-.78z"/>
</g>
</g>
</svg>
<span>Linux</span>
</a>
<a class="install-badge" target="_blank"
href="https://git.coolaj86.com/coolaj86/telebit.js#mac--linux">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<g fill="#000" fill-rule="nonzero">
<path d="M6.502 3.124c2.152 1.11 3.403 2.007 4.088
2.771-.35 1.407-2.182 1.471-2.851
1.432.137-.064.251-.14.292-.258-.168-.12-.764-.012-1.18-.246.16-.033.235-.065.31-.183-.394-.125-.817-.233-1.066-.441.134.002.26.03.435-.092-.352-.19-.727-.34-1.019-.63.182-.004.378-.001.435-.068a3.924
3.924 0 0
1-.819-.665c.255.031.363.005.424-.04-.243-.25-.552-.46-.698-.767.189.065.362.09.486-.006-.082-.186-.437-.296-.641-.733.199.02.41.044.452
0-.092-.376-.25-.588-.406-.807.426-.006 1.071.002
1.042-.034l-.263-.27c.416-.112.841.018
1.15.115.14-.11-.002-.248-.171-.39.353.048.673.129.962.241.154-.14-.1-.279-.224-.418.547.104.778.25
1.008.395.167-.16.01-.296-.103-.435.412.153.624.35.848.544.075-.102.192-.177.051-.424.293.169.513.367.676.59.18-.115.108-.273.109-.418.304.247.497.51.733.767.047-.034.089-.152.126-.338.725.704
1.75 2.476.263
3.179-1.264-1.044-2.775-1.802-4.45-2.371zM17.921
3.124c-2.152 1.11-3.403 2.007-4.089 2.771.351 1.407
2.183 1.471 2.852
1.432-.137-.064-.251-.14-.292-.258.168-.12.764-.012
1.18-.246-.16-.033-.235-.065-.31-.183.393-.125.817-.233
1.066-.441-.135.002-.26.03-.436-.092.352-.19.728-.34
1.02-.63-.182-.004-.379-.001-.436-.068.323-.2.594-.422.82-.665-.255.031-.363.005-.424-.04.243-.25.551-.46.698-.767-.189.065-.362.09-.487-.006.083-.186.438-.296.642-.733-.2.02-.41.044-.453
0
.093-.376.251-.588.407-.807-.426-.006-1.071.002-1.042-.034l.263-.27c-.416-.112-.842.018-1.15.115-.14-.11.002-.248.171-.39a4.182
4.182 0 0
0-.962.241c-.154-.14.1-.279.223-.418-.546.104-.778.25-1.008.395-.166-.16-.01-.296.103-.435-.411.153-.624.35-.847.544-.076-.102-.192-.177-.052-.424a2.149
2.149 0 0
0-.675.59c-.181-.115-.108-.273-.109-.418-.304.247-.497.51-.733.767-.048-.034-.09-.152-.126-.338-.725.704-1.75
2.476-.263 3.179 1.264-1.044 2.775-1.802
4.449-2.371zM14.818 17.45c0 1.313-1.154 2.377-2.578
2.377s-2.578-1.064-2.578-2.377c0-1.313 1.154-2.377
2.578-2.377s2.578 1.064 2.578 2.377zM10.153
10.363c1.204.426 1.773 1.922 1.27 3.343-.501 1.42-1.884
2.227-3.088
1.801-1.204-.426-1.773-1.922-1.271-3.343.502-1.42
1.885-2.227 3.09-1.801zM14.226 10.236c-1.204.426-1.773
1.922-1.27 3.343.501 1.42 1.884 2.227 3.088 1.801
1.204-.425 1.773-1.922
1.271-3.342-.502-1.42-1.885-2.227-3.089-1.802zM5.41
11.803c1.153-.309.389 4.771-.55
4.355-1.032-.83-1.364-3.262.55-4.355zM18.737
11.74c-1.154-.309-.39 4.771.549 4.354 1.032-.83
1.364-3.261-.55-4.354zM14.818 7.957c1.99-.336 3.647.847
3.58 3.005-.066.827-4.313-2.882-3.58-3.005zM9.32
7.894c-1.99-.336-3.646.846-3.58 3.004.066.828
4.313-2.881 3.58-3.004zM12.178
7.39c-1.187-.03-2.327.882-2.33 1.411-.003.643.939 1.302
2.339 1.318 1.429.01 2.34-.527
2.345-1.19.006-.752-1.3-1.55-2.354-1.539zM12.251
20.578c1.036-.045 2.425.333 2.428.836.017.488-1.26
1.59-2.497
1.569-1.28.055-2.536-1.05-2.52-1.432-.019-.56 1.56-.999
2.589-.973zM8.426 17.6c.737.888 1.073 2.449.458
2.909-.582.351-1.996.207-3-1.237-.678-1.211-.591-2.444-.115-2.806.711-.433
1.81.152 2.657 1.134zM15.929 17.318c-.798.935-1.242
2.64-.66 3.188.556.427 2.05.367
3.153-1.164.801-1.028.533-2.746.075-3.201-.68-.526-1.656.147-2.568
1.177z"/>
</g>
</g>
</svg>
<span>Raspberry Pi</span>
</a>
</div>
</div>
</div>
<div class="feature-list">
<div class="container">
<h2>Features</h2>
<div class="feature-badges">
<div class="feature-badge">
<img src="static-site-assets/images/lock.svg" />
<div>
Secure https for all tunnels
</div>
</div>
<div class="feature-badge">
<img src="static-site-assets/images/computer.svg" />
<div>
Show your work to anyone
</div>
</div>
<div class="feature-badge">
<img src="static-site-assets/images/language.svg" />
<div>
Test API Webhooks
</div>
</div>
<div class="feature-badge">
<img src="static-site-assets/images/cloud.svg" />
<div>
Test your UI in cloud browsers
</div>
</div>
</div>
</div>
</div>
<div class="donate-section">
<div class="container">
<h2>Donate and become a sponsor of a more open web</h2>
<p>We're on a mission to build a more open web. Telebit is still in it's
early days and the development is supported by generous sponsors like
you. Make a recurring or one-time donation today.
</p>
<a class="link-button" target="_blank" href="https://www.patreon.com/coolaj86">Make a donation</a>
</div>
</div>
<div class="mailing-list-form">
<div class="container">
<h2>Join our mailing list</h2>
<form class="js-inline-email-form email-signup-form" novalidate>
<div class="form-error js-inactive"></div>
<div class="success-message js-inactive">Thank you for joining!</div>
<span class="input-container email">
<div class="input-error email js-inactive"></div>
<input type="email" name="email" id="email" placeholder="Email">
</span>
<input class="link-button" type="submit" value="Join">
</form>
<ul>
<li><img src="static-site-assets/images/done.svg" />Get exclusive invites to try new features</li>
<li><img src="static-site-assets/images/done.svg" />Get updates on our progress</li>
<li><img src="static-site-assets/images/done.svg" />We'll never spam you</li>
</ul>
</div>
</div>
<footer>
<div class="container">
<div class="logo">
Telebit
</div>
<ul clss="footer-links">
<li><a href="">Privacy</a></li>
<li><a href="">Terms</a></li>
</ul>
</div>
</footer>
</body>
<script src="./static-site-assets/scripts/form-processing.js"></script>
</html>

View File

@ -0,0 +1,215 @@
/*global Vue*/
(function () {
'use strict';
var OAUTH3 = window.OAUTH3;
var oauth3 = OAUTH3.create({
host: window.location.host
, pathname: window.location.pathname.replace(/\/[^\/]*$/, '/')
});
//var $ = function () { return document.querySelector.apply(document, arguments); };
var sessionStr = localStorage.getItem('session');
var session;
if (sessionStr) {
try {
session = JSON.parse(sessionStr);
} catch(e) {
// ignore
}
}
var dnsRecords = {
"telebit.ppl.family": "178.128.3.196"
, "telebit.cloud": "46.101.97.218"
};
var vueData = {
claims: []
, domains: []
, devices: []
, newDomain: null
, newDomainWildcard: false
, newEmail: null
, hasAccount: false
, token: null
, spinner: false
, site: { deviceDomain: document.location.hostname, deviceDomainA: dnsRecords[document.location.hostname] }
};
var vueMethods = {
challengeDns: function () {
// we could do a checkbox
var wildcard = vueData.newDomainWildcard || false;
if (!/(\*\.)?[a-z0-9][a-z0-9\.\-_]\.[a-z0-9]{2,}/.test(vueData.newDomain)) {
window.alert("invalid domain name '" + vueData.newDomain + "'");
return;
}
// we can just detect by text
if ('*.' === vueData.newDomain.slice(0, 2)) {
vueData.newDomain = vueData.newDomain.slice(2);
wildcard = true;
}
return oauth3.request({
url: 'https://api.' + location.hostname + '/api/telebit.cloud/account/authorizations/new'
, method: 'POST'
, session: session
, data: { type: 'dns', value: vueData.newDomain, wildcard: wildcard }
}).then(function (resp) {
vueData.claims.unshift(resp.data.claim);
});
}
, checkDns: function (claim) {
return oauth3.request({
url: 'https://api.' + location.hostname + '/api/telebit.cloud/account/authorizations/new/:value/:challenge'
.replace(/:value/g, claim.value)
.replace(/:challenge/g, claim.challenge)
, method: 'POST'
, session: session
});
}
, challengeEmail: function () {
console.log("A new (Email) challenger!", vueData);
}
, pushDomain: function (dev) {
// TODO do some local validation too
console.log('pushDomain', dev);
vueData.spinner = true;
return oauth3.request({
url: 'https://api.' + location.hostname + '/api/telebit.cloud/devices/:dev/:name'
.replace(/:dev/g, dev.socketId)
.replace(/:name/g, dev.newDomain)
, method: 'POST'
, session: session
}).catch(function (err) {
console.error(err);
window.alert(err.toString());
}).then(function () {
dev.newDomain = '';
vueData.spinner = false;
});
}
, login: function () {
var email = vueData.newEmail;
vueMethods.logout();
onClickLogin(email);
}
, logout: function () {
sessionStorage.clear();
vueData.hasAccount = false;
// TODO OAUTH3._logoutHelper(directives, opts)
}
};
var app = new Vue({
el: '.v-app'
, data: vueData
, methods: vueMethods
});
app = null;
function listStuff(data) {
//window.alert("TODO: show authorized devices, domains, and connectivity information");
vueData.hasAccount = true;
vueData.domains = data.domains;
vueData.devices = data.devices;
vueData.claims = data.claims;
}
function loadAccount(session) {
return oauth3.request({
url: 'https://api.' + location.hostname + '/api/telebit.cloud/account'
, session: session
}).then(function (resp) {
console.info("Telebit Account:");
console.log(resp.data);
if (resp.data && resp.data.domains) {
listStuff(resp.data);
return;
}
if (1 === resp.data.accounts.length) {
listStuff(resp);
} else if (0 === resp.data.accounts.length) {
return oauth3.request({
url: 'https://api.' + location.hostname + 'api/telebit.cloud/account'
, method: 'POST'
, session: session
, body: {
email: vueData.newEmail
}
}).then(function (resp) {
listStuff(resp);
});
} if (resp.data.accounts.length > 2) {
window.alert("Multiple accounts.");
} else {
window.alert("Bad response.");
}
});
}
function onChangeProvider(providerUri) {
// example https://oauth3.org
return oauth3.setIdentityProvider(providerUri);
}
// This opens up the login window for the specified provider
//
function onClickLogin(email) {
// TODO check subject for provider viability
vueData.spinner = true;
return oauth3.authenticate({
subject: email
, scope: 'email@oauth3.org'
}).then(function (session) {
console.info('Authentication was Successful:');
console.log(session);
// You can use the PPID (or preferably a hash of it) as the login for your app
// (it securely functions as both username and password which is known only by your app)
// If you use a hash of it as an ID, you can also use the PPID itself as a decryption key
//
console.info('Secure PPID (aka subject):', session.token.sub);
return oauth3.request({
url: 'https://api.oauth3.org/api/issuer@oauth3.org/jwks/:sub/:kid.json'
.replace(/:sub/g, session.token.sub)
.replace(/:kid/g, session.token.iss)
, session: session
}).then(function (resp) {
console.info("Public Key:");
console.log(resp.data);
return oauth3.request({
url: 'https://api.oauth3.org/api/issuer@oauth3.org/acl/profile'
, session: session
}).then(function (resp) {
console.info("Inspect Token:");
console.log(resp.data);
localStorage.setItem('session', JSON.stringify(session));
loadAccount(session);
});
});
}, function (err) {
console.error('Authentication Failed:');
console.log(err);
}).then(function () {
vueData.spinner = false;
});
}
//$('body form.js-auth-form').addEventListener('submit', function onClickLoginHelper(ev) {
// ev.preventDefault();
// ev.stopPropagation();
// var email = $('.js-auth-subject').value;
// onClickLogin(email);
//});
onChangeProvider('oauth3.org');
if (session) {
vueData.token = session.access_token;
loadAccount(session);
}
}());

View File

@ -0,0 +1,65 @@
(function () {
'use strict';
document.body.hidden = false;
function formSubmit() {
// to be used for good, not evil
var msg = {
name: document.querySelector('.js-list-comment').value
, address: document.querySelector('.js-list-address').value
, list: 'telebit@ppl.family'
};
window.fetch('https://api.ppl.family/api/ppl.family/public/list', {
method: 'POST'
, cors: true
, headers: new Headers({ 'Content-Type': 'application/json' })
, body: JSON.stringify(msg)
}).then(function (resp) {
return resp.json().then(function (data) {
if (data.error) {
window.alert("Couldn't save your message. Email coolaj86@gmail.com instead.");
return;
}
document.querySelector('.js-list-form').hidden = true;
document.querySelector('.js-list-form').className += ' hidden';
document.querySelector('.js-list-thanks').hidden = false;
document.querySelector('.js-list-thanks').className = document.querySelector('.js-list-thanks').className.replace(/\s*hidden\b/, '');
}, function () {
window.alert("Couldn't save your message. Email coolaj86@gmail.com instead.");
});
}, function () {
window.alert("Didn't get your message. Bad network connection? Email coolaj86@gmail.com instead.");
});
}
document.body.addEventListener('submit', function (ev) {
if (ev.target.matches('.js-list-form')) {
ev.preventDefault();
ev.stopPropagation();
formSubmit();
return;
}
});
document.body.addEventListener('click', function (ev) {
if (ev.target.matches('.js-list-submit')) {
ev.preventDefault();
ev.stopPropagation();
formSubmit();
return;
}
/*
if (ev.target.closest('.js-navbar-toggle')) {
ev.preventDefault();
ev.stopPropagation();
if (/show/.test(document.querySelector('.js-navbar-collapse').className)) {
document.querySelector('.js-navbar-collapse').className = document.querySelector('.js-navbar-collapse').className.replace(/\s+show\b/, '');
} else {
document.querySelector('.js-navbar-collapse').className += ' show';
}
return;
}
*/
});
}());

View File

@ -0,0 +1,28 @@
<head>
<style>
body {
margin: 1em;
}
</style>
</head>
<body>
<h1>Terms of Service</h1>
<p>To be used for good, not evil.</p>
<h1>Privacy</h1>
<p>We'll keep your info to ourselves.</p>
<h1>License</h1>
<p>There are Commercial and Open Source versions of Telebit<br>(kinda like how Google has Chrome and Chromium).
</p>
<p>
The Open Source versions are available as
<li><a href="https://git.coolaj86.com/coolaj86/telebit.js" target="_blank">Telebit Remote</a> (the "client" daemon)</li>
<li><a href="https://git.coolaj86.com/coolaj86/telebit.js" target="_blank">Telebit Relay</a> (the service daemon)</li>
</p>
<h1>Trademark</h1>
<p>Telebit is a trademark of AJ ONeal
</p>
</body>

View File

@ -0,0 +1,228 @@
body {
font-family: Source Sans Pro, sans-serif;
font-size: 18px;
color: #1a1a1a;
letter-spacing: -0.022222222em;
line-height: 1.33;
margin: 0;
padding-bottom: 4em;
box-sizing: border-box;
}
p {
margin: 0;
}
h2 {
font-size: 1.777777778em;
margin: 0 0 1em 0;
}
svg {
width: 1.333333333em;
height: 1.333333333em;
fill: #1a1a1a;
}
svg.icon-computer {width: 4em;height: 4em;}
button {
width: 100%;
background-color: #1a1a1a;
border: none;
font-size: 1em;
color: white;
padding: 0.44444em;
margin: 1em 0;
}
button:disabled {
background-color: #d9d9d9;
}
input[type=text] {
font-size: 1em;
padding: 0.444444444em 0.888889em;
width: 100%;
border: solid 1px #d9d9d9;
border-radius: 2px;
box-sizing: border-box;
margin: 0.888888889em 0;
}
.container {
text-align: center;
width: 17.777777778em;
margin: auto;
}
.checkbox-array {
display: flex;
flex-direction: column;
padding: 1em 0;
}
.checkbox-array input[type=checkbox] {
opacity: 0;
position: absolute;
}
.checkbox-array input[type=checkbox] ~ .icon-checked-box {
display: none;
}
.checkbox-array input[type=checkbox] ~ .icon-unchecked-box {
display: initial;
}
.checkbox-array input[type=checkbox]:checked ~ .icon-checked-box {
display: initial;
}
.checkbox-array input[type=checkbox]:checked ~ .icon-unchecked-box {
display: none;
}
.checkbox-array input[type=checkbox]:focus ~ .icon-checked-box, .checkbox-array input[type=checkbox]:focus ~ .icon-unchecked-box {
background: #DDDDDD;
}
.checkbox-array .icon-checked-box, .checkbox-array .icon-unchecked-box {
margin-right: 0.666666667em;
}
.checkbox-array label {
display: flex;
height: 1.333333333em;
font-size: 0.833333333em;
margin: 0.4em 0;
}
h1.logo {
font-size: 1.555555556em;
margin-bottom: 1.777777778em;
}
svg.authorized-check {
fill: #63f794;
margin-right: 0.666666667em;
}
.progress .row {
display: flex;
justify-content: left;
margin: 0 0 0.6666em 0;
}
.spinner-ball {
width: 4px;
height: 4px;
border-radius: 5px;
background: #1a1a1a;
margin: 2px;
}
span.spinner {
display: flex;
align-items: center;
margin-right: 0.666666667em;
}
.important-text {
font-weight: bold;
}
.progress {
display: inline-block;
margin-bottom: 1.111177778em;
}
.debugging-info-container {
text-align: center;
position: fixed;
bottom: 0;
width: 100%;
/* overflow: hidden; */
}
.debugging-info-container pre {
word-break: break-all;
white-space: pre-wrap;
}
.debugging-info {
max-width: 65em;
margin: 0 auto;
}
span.debugging.button {
display: inline-flex;
}
span.js-debugging-button.debugging-button {
display: inline-flex;
}
.debugging-button {
display: inline-flex;
padding: 0.3em;
position: absolute;
bottom: 100%;
transform: translateX(-50%);
background: white;
border: solid #eee 1px;
border-radius: 5px 5px 0 0;
border-bottom: none;
}
.debugging-info-container.visible .debugging-button svg {
transform: rotate(180deg);
}
.debugging-button svg {transition: transform 0.3s;}
.debug-drawer {
/* position: relative; */
transform: translateY(100%);
transition: transform 0.3s;
padding: 0.1em 0;
background: white;
pointer-events: initial;
border-top: solid #eee 1px;
padding-top: 1em;
}
.debugging-info-container.visible .debug-drawer {
transform: translateY(0);
}
.debugging-info-container {
padding-top: 3em;
overflow: hidden;
pointer-events: none;
}
.spinner .spinner-ball {
animation: pulsing 2s ease infinite;
}
.spinner .spinner-ball:nth-child(2) {
animation-delay: 0.2s;
}
.spinner .spinner-ball:nth-child(3) {
animation-delay: 0.4s;
}
@keyframes pulsing {
0% {transform: scale(1);}
35% {transform: scale(1);}
60% {transform: scale(1.3);}
75% {transform: scale(1.3);}
100% {transform: scale(1);}
}
.finish-button {
margin-top: 2.222222222em;
}

View File

@ -0,0 +1,161 @@
<!DOCTYPE html>
<html>
<head>
<title>Telebit - Pair Device</title>
<link href="./css/main.css" rel="stylesheet">
<style>
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-display: block;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(/static-site-assets/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: block;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(/static-site-assets/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>
<link rel="preload" href="/static-site-assets/fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/static-site-assets/fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous">
</head>
<body>
<script>document.body.hidden = true;</script>
<!-- let's define our SVG that we will use later -->
<svg width="0" height="0" viewBox="0 0 24 24">
<defs>
<g id="svg-check">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>
</g>
<g id="svg-checked">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</g>
<g id="svg-unchecked">
<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</g>
<g id="svg-download">
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</g>
<g id="svg-computer">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/>
</g>
<g id="svg-circle-check">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</g>
<g id="svg-arrow-down">
<path d="M7.41,8.59L12,13.17l4.59-4.58L18,10l-6,6l-6-6L7.41,8.59z"/>
<path fill="none" d="M0,0h24v24H0V0z"/>
</g>
</defs>
</svg>
<div class="js-error" hidden>
<h1>Invalid Pairing Link</h1>
<div class="js-magic-link">'{{magic_link}}' isn't a valid pairing link code.
<br>Links are only valid for a limited time, so you gotta act fast.
</div>
</div>
<div class="container js-magic" hidden><form class="js-submit">
<h1 class="logo">Telebit</h1>
<svg class="icon-computer" viewBox="0 0 24 24">
<use xlink:href="#svg-computer"></use>
</svg>
<h2>Pair <span class="js-hostname">Device</span></h1>
<label><span class="important-text">Enter your device pairing code</span>
<input type="text" name="pair-code" placeholder="ex: 0000" autofocus>
</label>
<div class="checkbox-array">
<label>
<input name="telebit-agree" type="checkbox" required>
<svg class="icon-checked-box" viewBox="0 0 24 24">
<use xlink:href="#svg-checked"></use>
</svg>
<svg class="icon-unchecked-box" viewBox="0 0 24 24">
<use xlink:href="#svg-unchecked"></use>
</svg>
<div>Agree to <a target="_blank" href="/legal/">Telebit&trade; Terms of Service</a></div>
</label>
<label>
<input name="letsencrypt-agree" type="checkbox" required>
<svg class="icon-checked-box" viewBox="0 0 24 24">
<use xlink:href="#svg-checked"></use>
</svg>
<svg class="icon-unchecked-box" viewBox="0 0 24 24">
<use xlink:href="#svg-unchecked"></use>
</svg>
<div>Agree to <a target="_blank" href="https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf"> Let's Encrypt&trade; Terms of Service</a></div>
</label>
</div>
<div>
<button type="submit" disabled>Claim Device</button>
</div>
</form></div>
<div class="container js-authz" hidden>
<h1 class="logo">Telebit</h1>
<svg class="icon-computer" viewBox="0 0 24 24">
<use xlink:href="#svg-computer"></use>
</svg>
<h2>Pair <span class="js-hostname">Device</span></h1>
<div>
<div class="progress">
<div class="row">
<svg class="authorized-check" viewBox="0 0 24 24">
<use xlink:href="#svg-circle-check"></use>
</svg>
Authorized
</div>
<div class="row">
<span class="spinner">
<div class="spinner-ball ball-1"></div>
<div class="spinner-ball ball-1"></div>
<div class="spinner-ball ball-1"></div>
</span>
Waiting for device to pair
</div>
</div>
</div>
<div class="important-text">
Check the command line on your device to finish pairing.
</div>
</div>
<div class="js-debug-container debugging-info-container" hidden>
<div class="debug-drawer">
<span class="js-debug-button debugging-button">
Debugging info <svg class="debugging-arrow" viewBox="0 0 24 24">
<use xlink:href="#svg-arrow-down"></use>
</svg>
</span>
<div class="js-debug-info debugging-info">
<p><a class="js-new-href">{{js-new-href}}</a></p>
<p class="js-serviceport">xxxxx</p>
<p><small>Authorization Token:
<pre><code class="js-token">{{js-token}}</code></pre></small></p>
</div>
</div>
</div>
<div class="container js-finish" hidden>
<h1 class="logo">Telebit</h1>
<svg class="icon-computer" viewBox="0 0 24 24">
<use xlink:href="#svg-computer"></use>
</svg>
<h2>Success!</h1>
<div>
<span class="important-text js-new-domain">______</span> is paired and ready to use for accessing your device and sharing your stuff.
</div>
<button class="js-finish-button finish-button">Take Me There</button>
<script src="js/app.js"></script>
</div>
</body>
</html>

View File

@ -0,0 +1,180 @@
(function () {
'use strict';
var meta = {};
var magic;
var domainname;
var port;
function checkStatus() {
// TODO use Location or Link
window.fetch(meta.baseUrl + 'api/telebit.cloud/pair_state/' + magic, {
method: 'GET'
, cors: true
}).then(function (resp) {
return resp.json().then(function (data) {
console.log(data);
if ('invalid' === data.status) {
window.alert("something went wrong");
return;
}
if ('complete' === data.status) {
successScreen();
setTimeout(function () {
//window.document.body.innerHTML += ('<img src="https://' + domainname + '/_apis/telebit.cloud/clear.gif">');
// TODO once this is loaded (even error) Let's Encrypt is done,
// then it's time to redirect to the domain. Yay!
}, 1 * 1000);
return;
}
setTimeout(checkStatus, 2 * 1000);
}, function (err) {
console.error(err);
setTimeout(checkStatus, 2 * 1000);
});
});
}
function successScreen() {
document.querySelector('.js-authz').hidden = true;
document.querySelector('.js-finish-button').addEventListener('click', function(e) {
window.location.href='https://' + domainname + "/#/serviceport=" + port;
});
document.querySelectorAll('.js-new-domain').forEach(function(ele) {
ele.innerHTML = domainname;
});
document.querySelector('.js-finish').hidden = false;
}
function submitCode(pair) {
// TODO use Location or Link
document.querySelector('.js-magic').hidden = true;
window.fetch(meta.baseUrl + 'api/telebit.cloud/pair_code/', {
method: 'POST'
, headers: {
'Content-Type': 'application/json'
}
, body: JSON.stringify({
magic: pair.magic
, pin: pair.pin || pair.code
, agree_tos: pair.agreeTos
})
, cors: true
}).then(function (resp) {
return resp.json().then(function (data) {
// TODO check for error (i.e. bad Pair Code / PIN)
// shouldn't be pending (because we get here by being ready)
// should poll over 'ready'
console.log('Submit Code Response:');
console.log(data);
if (data.error) {
document.querySelector('.js-error').hidden = false;
return;
}
setTimeout(checkStatus, 0);
document.querySelector('.js-authz').hidden = false;
document.querySelector('.js-debug-container').hidden = false;
/*
document.querySelectorAll('.js-token-data').forEach(function ($el) {
$el.innerText = JSON.stringify(data, null, 2);
});
*/
document.querySelectorAll('.js-new-href').forEach(function ($el) {
domainname = data.domains[0];
port = data.port;
$el.href = 'https://' + data.domains[0] + '/';
$el.innerText = '🔐 https://' + data.domains[0];
});
document.querySelectorAll('.js-domainname').forEach(function ($el) {
$el.innerText = data.domains.join(',');
});
document.querySelectorAll('.js-serviceport').forEach(function ($el) {
$el.innerText = data.ports.join(',');
});
document.querySelectorAll('.js-token').forEach(function ($el) {
$el.innerText = data.jwt;
});
}, function (err) {
console.error(err);
document.querySelector('.js-error').hidden = false;
});
});
}
function init() {
magic = (window.location.hash || '').substr(2).replace(/magic=/, '');
if (!magic) {
document.querySelector('body').hidden = false;
document.querySelector('.js-error').hidden = false;
return;
}
window.fetch(meta.baseUrl + meta.pair_request.pathname + '/' + magic, {
method: 'GET'
, cors: true
}).then(function (resp) {
return resp.json().then(function (data) {
console.log('pair request data:');
console.log(data);
document.querySelector('body').hidden = false;
if (data.error) {
document.querySelector('.js-error').hidden = false;
document.querySelector('.js-magic-link').innerText = "Something went wrong. Perhaps an bad or expired link.";
return;
}
document.querySelector('.js-magic').hidden = false;
document.querySelectorAll('.js-hostname').forEach(function(ele) {
ele.innerText = data.hostname || 'Device';
});
//document.querySelector('.js-token-data').innerText = JSON.stringify(data, null, 2);
});
});
document.querySelector('.js-submit').addEventListener('submit', function (ev) {
ev.preventDefault();
var pair = {};
pair.magic = magic;
pair.code = document.querySelector('[name=pair-code]').value;
pair.agreeTos = document.querySelector('[name=letsencrypt-agree]').checked
&& document.querySelector('[name=telebit-agree]').checked;
console.log('Pair Form:');
console.log(pair);
submitCode(pair);
});
var formElements = document.querySelector('.js-submit').elements;
for(var i = 0; i < formElements.length; ++i) {
var tosCheck = document.querySelector('[name=telebit-agree]');
var leCheck = document.querySelector('[name=letsencrypt-agree]');
var pairCodeInput = document.querySelector('[name=pair-code]');
formElements[i].addEventListener('input', function(ev) {
if(tosCheck.checked && leCheck.checked && pairCodeInput.value.length) {
document.querySelector('.js-submit button').disabled = false;
} else {
document.querySelector('.js-submit button').disabled = true;
}
});
};
document.querySelector('.js-debug-button').addEventListener("click", function(e) {
document.querySelector('.js-debug-container').classList.toggle("visible");
})
}
window.fetch('https://' + location.hostname + '/_apis/telebit.cloud/index.json', {
method: 'GET'
, cors: true
}).then(function (resp) {
return resp.json().then(function (_json) {
meta = _json;
meta.baseUrl = 'https://' + meta.api_host.replace(/:hostname/g, location.hostname) + '/';
init();
});
});
}());

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>Optify - Containerize without the container</title>
</head>
<body>
<h1>Optify</h1>
<p>containerize without the container</p>
<p>Holds your hand without being so hands on.
Like brew and Docker had a baby that <em>did</em> care about versions and <em>didn't</em> care about port numbers.
Only for the masters of their domain.
Be wise and be bold.</p>
</body>
</html>

View File

@ -0,0 +1,58 @@
#!/bin/bash
#<pre><code>
set -e
set -u
# minimal os detection
my_os="$(uname -s | tr '[:upper:]' '[:lower:]')"
# https://github.com/golang/go/wiki/GoArm#supported-architectures
# minimal cpu arch detection
my_arch="$(uname -m)"
if [ "x86_64" == "$my_arch" ]; then
my_arch="amd64"
elif [ "i386" == "$my_arch" ]; then
my_arch="386"
elif [ -n "$($my_arch | grep arm)" ]; then
if [ -n "$(uname -a | grep aarch64)" ]; then
my_arch="arm64"
elif [ -n "$(uname -a | grep armv8l)" ]; then
my_arch="arm64"
elif [ -n "$(uname -a | grep armv7l)" ]; then
my_arch="armv7l"
elif [ -n "$(uname -a | grep armv6l)" ]; then
my_arch="armv6l"
else
echo "could not determine arm cpu architecture" >&2
exit 1
fi
else
echo "could not determine cpu architecture" >&2
exit 1
fi
# get optify for this cpu and arch
if [ -z "${OPTIFY_VERSION:-}" ]; then
latest_version="$(curl -fsSL https://telebit.cloud/optify/latest)"
OPTIFY_VERSION=$latest_version
echo "Installing optify-$OPTIFY_VERSION (latest)"
else
echo "Installing optify OPTIFY_VERSION=$OPTIFY_VERSION"
fi
# download to a tmp folder
#my_tmpdir="$(mktemp -d /tmp/optify.XXXXXXXX)"
my_tmpdir="$(mktemp -d -t optify.XXXXXXXX)"
my_url="https://telebit.cloud/optify/dist/${my_os}/${my_arch}/optify-${OPTIFY_VERSION}"
if [ -n "$(type -p curl || true)" ]; then
my_out="$(curl -fsSL -o "$my_tmpdir/optify-${OPTIFY_VERSION}" "$my_url" || true)"
elif [ -n "$(type -p wget || true)" ]; then
my_out="$(wget -q -c "$my_url" -O "$my_tmpdir/optify-${OPTIFY_VERSION}" || true)"
else
echo "found neither wget nor curl" >&2
exit 1
fi
# check for downloader success
chmod a+x "$my_tmpdir/optify-${OPTIFY_VERSION}"
"$my_tmpdir/optify-${OPTIFY_VERSION}" --install

View File

@ -0,0 +1 @@
v0.0.5

View File

@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<title>sclient - tls unwrapper for Windows, Mac, and Linux</title>
</head>
<body>
<h1>sclient</h1>
<p>ssl unwrapper for Windows, Mac, and Linux</p>
<p>a poor man's alternative to <code>openssl s_client</code>, <code>stunnel</code>, <code>socat</code>
for the simple use case of connecting a client application that doesn't support tls+sni
through a secure connection, https proxy, or sni multiplexer
(think <code>telnet</code>, <code>netcat</code>, <code>ssh</code>, <code>openvpn</code>, etc).</p>
<h2>Usage</h2>
<pre><code>$ sclient [flags] &lt;remote&gt; &lt;local&gt;</code></pre>
<pre><code>$ sclient example.com:443 localhost:3000</code></pre>
<h3>Flags</h3>
<ul>
<li><kbd>-k, --insecure</kbd> ignore invalid tls certificates</li>
<li><kbd>--servername &lt;string&gt;</kbd> spoof SNI
(to disable use IP as &lt;remote&gt; and do not use this option)</li>
</ul>
<h3>Arguments</h3>
<ul>
<li><kbd>&lt;remote&gt;</kbd> the servername and port of the tls-enabled server (default port is 443)</li>
<li><kbd>&lt;local&gt;</kbd> the local address and port to bind to (default bind address is 127.0.0.1 or ::1)
<ul>
<li><code>-</code> may be used to read from stdin (like netcat)</li>
<li>may be omitted when piping (see pipe example below)</li>
</ul>
</li>
</ul>
<h2>Examples</h2>
<h3>SSH</h3>
<pre><code>$ ssh -o ProxyCommand="sclient %h" jon.telebit.io</code></pre>
<p>This is useful to be able to connect to SSH even from behind a corporate packet-inspection firewall.
It can also be used to multiplex and relay multiple ssh connections through a single host.
</p>
<h3>Telnet </h3>
<pre><code>$ sclient example.com:443 localhost:3000
&gt; [listening] example.com:443 &lt;= localhost:3000</code></pre>
<pre><code>$ telnet localhost 3000</code></pre>
<h3>stdin/stdout</h3>
<pre><code>$ sclient whatever.com -
&gt; (connected to whatever.com:443 and reading from stdin)</code></pre>
Use just like netcat or telnet. A manual HTTP request, for example:
<pre><code>&gt; GET / HTTP/1.1
&gt; Host: whatever.com
&gt; Connection: close
&gt;
</code></pre>
<h3>pipe</h3>
<pre><code>$ printf "GET / HTTP/1.1\r\nHost: telebit.cloud\r\n\r\n" | sclient telebit.cloud</code></pre>
<h2>Downloads (standalone) <small>v1.2</small></h2>
<ul>
<li>Windows 7/8/10
<a href="dist/windows/amd64/sclient.exe">Download</a>
| <a href="dist/windows/386/sclient.exe">(x86)</a>
</li>
<li>macOS / OS X / Darwin
<a href="dist/darwin/amd64/sclient">Download</a>
</li>
<li>Linux Ubuntu/Arch/etc
<a href="dist/linux/amd64/sclient">Download</a>
| <a href="dist/linux/386/sclient">(386)</a>
</li>
<li>Raspberry Pi
<a href="dist/linux/armv7/sclient">Download</a>
| <a href="dist/linux/armv5/sclient">(Pi Zero)</a>
</li>
<li>Source Code
<a href="https://git.coolaj86.com/coolaj86/sclient.go">Go (golang)</a>
| <a href="https://git.coolaj86.com/coolaj86/sclient.js">node.js</a>
</li>
</ul>
<h2>Source Code</h2>
<p><code>sclient</code> is maintained simultaneously in go and node.js</p>
<ul>
<li>Go
<ul>
<li><a href="https://git.coolaj86.com/coolaj86/sclient.go">git</a></li>
<li><a href="https://git.coolaj86.com/coolaj86/sclient.go/archive/master.zip">zip</a></li>
</ul>
</li>
<br>
<li>node.js
<ul>
<li><a href="https://git.coolaj86.com/coolaj86/sclient.js">git</a></li>
<li><a href="https://git.coolaj86.com/coolaj86/sclient.js/archive/master.zip">zip</a></li>
<li><kbd>npm install -g sclient</kbd></li>
</ul>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Telebit Relay</title>
</head>
<body>
[TODO: Setup Interface]
<br>
<ul>
<li>Admin Server Name</li>
<li>Administrator Email</li>
<li>SSL ToS Agree</li>
<li>Community Member</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="170px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170 170" version="1.1" height="170px">
<path d="m150.37 130.25c-2.45 5.66-5.35 10.87-8.71 15.66-4.58 6.53-8.33 11.05-11.22 13.56-4.48 4.12-9.28 6.23-14.42 6.35-3.69 0-8.14-1.05-13.32-3.18-5.197-2.12-9.973-3.17-14.34-3.17-4.58 0-9.492 1.05-14.746 3.17-5.262 2.13-9.501 3.24-12.742 3.35-4.929 0.21-9.842-1.96-14.746-6.52-3.13-2.73-7.045-7.41-11.735-14.04-5.032-7.08-9.169-15.29-12.41-24.65-3.471-10.11-5.211-19.9-5.211-29.378 0-10.857 2.346-20.221 7.045-28.068 3.693-6.303 8.606-11.275 14.755-14.925s12.793-5.51 19.948-5.629c3.915 0 9.049 1.211 15.429 3.591 6.362 2.388 10.447 3.599 12.238 3.599 1.339 0 5.877-1.416 13.57-4.239 7.275-2.618 13.415-3.702 18.445-3.275 13.63 1.1 23.87 6.473 30.68 16.153-12.19 7.386-18.22 17.731-18.1 31.002 0.11 10.337 3.86 18.939 11.23 25.769 3.34 3.17 7.07 5.62 11.22 7.36-0.9 2.61-1.85 5.11-2.86 7.51zm-31.26-123.01c0 8.1021-2.96 15.667-8.86 22.669-7.12 8.324-15.732 13.134-25.071 12.375-0.119-0.972-0.188-1.995-0.188-3.07 0-7.778 3.386-16.102 9.399-22.908 3.002-3.446 6.82-6.3113 11.45-8.597 4.62-2.2516 8.99-3.4968 13.1-3.71 0.12 1.0831 0.17 2.1663 0.17 3.2409z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"/>
</svg>

After

Width:  |  Height:  |  Size: 302 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/>
</svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm6.93 6h-2.95c-.32-1.25-.78-2.45-1.38-3.56 1.84.63 3.37 1.91 4.33 3.56zM12 4.04c.83 1.2 1.48 2.53 1.91 3.96h-3.82c.43-1.43 1.08-2.76 1.91-3.96zM4.26 14C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2 0 .68.06 1.34.14 2H4.26zm.82 2h2.95c.32 1.25.78 2.45 1.38 3.56-1.84-.63-3.37-1.9-4.33-3.56zm2.95-8H5.08c.96-1.66 2.49-2.93 4.33-3.56C8.81 5.55 8.35 6.75 8.03 8zM12 19.96c-.83-1.2-1.48-2.53-1.91-3.96h3.82c-.43 1.43-1.08 2.76-1.91 3.96zM14.34 14H9.66c-.09-.66-.16-1.32-.16-2 0-.68.07-1.35.16-2h4.68c.09.65.16 1.32.16 2 0 .68-.07 1.34-.16 2zm.25 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95c-.96 1.65-2.49 2.93-4.33 3.56zM16.36 14c.08-.66.14-1.32.14-2 0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2h-3.38z"/>
</svg>

After

Width:  |  Height:  |  Size: 959 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<g transform="translate(1 2)">
<rect width="22" height="20" fill="#000" rx="1"/>
<path fill="#FFF" d="M6.495 3.942v1.125l-4.12 1.566V5.551l2.882-1.047-2.882-1.056V2.375l4.12 1.567zm.32 3.592h4.327v.779H6.814v-.78z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 436 B

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>
</svg>

After

Width:  |  Height:  |  Size: 375 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#1A1A1A" d="M21.41 8.22c-1.667 1.179-2.5 2.597-2.5 4.254 0 1.986 1.03 3.509 3.09 4.57-.553 1.6-1.354 2.993-2.402 4.178C18.549 22.407 17.592 23 16.726 23c-.408 0-.965-.135-1.67-.404l-.34-.13c-.69-.27-1.302-.404-1.834-.404-.502 0-1.052.105-1.649.316l-.426.153-.535.218c-.422.167-.848.251-1.277.251-1.012 0-2.13-.833-3.352-2.498C3.88 18.117 3 15.518 3 12.704c0-2 .55-3.61 1.649-4.832 1.1-1.222 2.555-1.833 4.368-1.833.677 0 1.31.124 1.9.371l.404.164.426.174c.378.16.684.24.917.24.298 0 .63-.069.993-.207l.557-.218.415-.153c.663-.24 1.394-.36 2.195-.36 1.9 0 3.429.724 4.586 2.17zM16.911 1c.022.255.033.45.033.589 0 1.258-.458 2.361-1.376 3.31-.917.95-1.983 1.424-3.199 1.424a5.474 5.474 0 0 1-.055-.611c0-1.069.426-2.072 1.278-3.01.852-.938 1.838-1.487 2.96-1.647.08-.015.2-.033.36-.055z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 982 B

View File

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<g fill="#000" fill-rule="nonzero">
<path d="M6.502 3.124c2.152 1.11 3.403 2.007 4.088 2.771-.35 1.407-2.182 1.471-2.851 1.432.137-.064.251-.14.292-.258-.168-.12-.764-.012-1.18-.246.16-.033.235-.065.31-.183-.394-.125-.817-.233-1.066-.441.134.002.26.03.435-.092-.352-.19-.727-.34-1.019-.63.182-.004.378-.001.435-.068a3.924 3.924 0 0 1-.819-.665c.255.031.363.005.424-.04-.243-.25-.552-.46-.698-.767.189.065.362.09.486-.006-.082-.186-.437-.296-.641-.733.199.02.41.044.452 0-.092-.376-.25-.588-.406-.807.426-.006 1.071.002 1.042-.034l-.263-.27c.416-.112.841.018 1.15.115.14-.11-.002-.248-.171-.39.353.048.673.129.962.241.154-.14-.1-.279-.224-.418.547.104.778.25 1.008.395.167-.16.01-.296-.103-.435.412.153.624.35.848.544.075-.102.192-.177.051-.424.293.169.513.367.676.59.18-.115.108-.273.109-.418.304.247.497.51.733.767.047-.034.089-.152.126-.338.725.704 1.75 2.476.263 3.179-1.264-1.044-2.775-1.802-4.45-2.371zM17.921 3.124c-2.152 1.11-3.403 2.007-4.089 2.771.351 1.407 2.183 1.471 2.852 1.432-.137-.064-.251-.14-.292-.258.168-.12.764-.012 1.18-.246-.16-.033-.235-.065-.31-.183.393-.125.817-.233 1.066-.441-.135.002-.26.03-.436-.092.352-.19.728-.34 1.02-.63-.182-.004-.379-.001-.436-.068.323-.2.594-.422.82-.665-.255.031-.363.005-.424-.04.243-.25.551-.46.698-.767-.189.065-.362.09-.487-.006.083-.186.438-.296.642-.733-.2.02-.41.044-.453 0 .093-.376.251-.588.407-.807-.426-.006-1.071.002-1.042-.034l.263-.27c-.416-.112-.842.018-1.15.115-.14-.11.002-.248.171-.39a4.182 4.182 0 0 0-.962.241c-.154-.14.1-.279.223-.418-.546.104-.778.25-1.008.395-.166-.16-.01-.296.103-.435-.411.153-.624.35-.847.544-.076-.102-.192-.177-.052-.424a2.149 2.149 0 0 0-.675.59c-.181-.115-.108-.273-.109-.418-.304.247-.497.51-.733.767-.048-.034-.09-.152-.126-.338-.725.704-1.75 2.476-.263 3.179 1.264-1.044 2.775-1.802 4.449-2.371zM14.818 17.45c0 1.313-1.154 2.377-2.578 2.377s-2.578-1.064-2.578-2.377c0-1.313 1.154-2.377 2.578-2.377s2.578 1.064 2.578 2.377zM10.153 10.363c1.204.426 1.773 1.922 1.27 3.343-.501 1.42-1.884 2.227-3.088 1.801-1.204-.426-1.773-1.922-1.271-3.343.502-1.42 1.885-2.227 3.09-1.801zM14.226 10.236c-1.204.426-1.773 1.922-1.27 3.343.501 1.42 1.884 2.227 3.088 1.801 1.204-.425 1.773-1.922 1.271-3.342-.502-1.42-1.885-2.227-3.089-1.802zM5.41 11.803c1.153-.309.389 4.771-.55 4.355-1.032-.83-1.364-3.262.55-4.355zM18.737 11.74c-1.154-.309-.39 4.771.549 4.354 1.032-.83 1.364-3.261-.55-4.354zM14.818 7.957c1.99-.336 3.647.847 3.58 3.005-.066.827-4.313-2.882-3.58-3.005zM9.32 7.894c-1.99-.336-3.646.846-3.58 3.004.066.828 4.313-2.881 3.58-3.004zM12.178 7.39c-1.187-.03-2.327.882-2.33 1.411-.003.643.939 1.302 2.339 1.318 1.429.01 2.34-.527 2.345-1.19.006-.752-1.3-1.55-2.354-1.539zM12.251 20.578c1.036-.045 2.425.333 2.428.836.017.488-1.26 1.59-2.497 1.569-1.28.055-2.536-1.05-2.52-1.432-.019-.56 1.56-.999 2.589-.973zM8.426 17.6c.737.888 1.073 2.449.458 2.909-.582.351-1.996.207-3-1.237-.678-1.211-.591-2.444-.115-2.806.711-.433 1.81.152 2.657 1.134zM15.929 17.318c-.798.935-1.242 2.64-.66 3.188.556.427 2.05.367 3.153-1.164.801-1.028.533-2.746.075-3.201-.68-.526-1.656.147-2.568 1.177z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1 @@
<svg aria-labelledby="simpleicons-raspberrypi-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title id="simpleicons-raspberrypi-icon">Raspberry Pi icon</title><path d="M16.111 17.338c-.857.989-1.334 2.79-.709 3.371.596.449 2.201.391 3.385-1.23.86-1.08.569-2.893.081-3.372-.73-.555-1.778.164-2.757 1.243v-.012zm-8.057.3c-.908-1.04-2.088-1.658-2.851-1.199-.51.382-.605 1.685.123 2.967 1.078 1.524 2.596 1.679 3.221 1.307.659-.488.3-2.137-.493-3.075zm4.105 3.145c-1.103-.026-2.798.439-2.776 1.032-.018.403 1.331 1.572 2.705 1.513 1.326.03 2.699-1.139 2.682-1.649-.004-.523-1.498-.927-2.607-.884l-.004-.012zm-.075-13.944c-1.275-.032-2.502.933-2.502 1.493-.004.68 1.008 1.376 2.51 1.394 1.543.01 2.518-.559 2.532-1.26.016-.794-1.394-1.639-2.518-1.627h-.022zm-3.071.532c-2.135-.345-3.913.9-3.842 3.192.07.884 4.63-3.041 3.843-3.177l-.001-.015zm9.749 3.251c.071-2.277-1.709-3.521-3.844-3.176-.787.135 3.772 4.061 3.844 3.176zm.364.824c-1.239-.329-.42 5.049.588 4.615 1.109-.869 1.466-3.446-.588-4.6v-.015zM4.228 16.121c1.007.45 1.827-4.929.589-4.6-2.053 1.153-1.698 3.73-.589 4.615v-.015zm9.415-5.948c-1.146.75-1.354 2.428-.461 3.746.891 1.318 2.543 1.813 3.691 1.078 1.146-.733 1.353-2.412.462-3.746-.892-1.333-2.545-1.813-3.692-1.063v-.015zm-3.096.135c-1.146-.734-2.799-.254-3.689 1.064-.892 1.334-.686 3.012.461 3.761s2.799.269 3.691-1.064c.885-1.318.675-2.997-.465-3.745l.002-.016zm4.369 7.162c-.009-1.393-1.252-2.518-2.781-2.502-1.527.016-2.761 1.139-2.754 2.532v.029c.01 1.394 1.254 2.517 2.783 2.502 1.527 0 2.756-1.138 2.742-2.517v-.029l.01-.015zm3.209-15.133c-2.307 1.184-3.652 2.128-4.389 2.938.377 1.498 2.344 1.558 3.063 1.512-.147-.06-.271-.149-.315-.269.18-.12.821-.016 1.268-.255-.171-.03-.252-.061-.329-.195.419-.135.875-.24 1.141-.465-.143 0-.278.03-.467-.09.377-.194.778-.359 1.095-.658-.196 0-.406 0-.466-.075.346-.21.635-.435.877-.704-.272.045-.39.016-.454-.03.261-.255.593-.479.749-.81-.203.076-.391.09-.522 0 .091-.194.47-.314.69-.779-.215.03-.441.046-.486 0 .098-.389.269-.613.435-.854-.457 0-1.15 0-1.117-.029l.283-.285c-.448-.12-.904.015-1.236.12-.149-.105 0-.255.185-.405-.39.061-.733.135-1.034.256-.164-.15.105-.285.24-.436-.599.12-.839.27-1.094.42-.18-.165-.015-.314.104-.449-.449.164-.674.374-.914.568-.09-.104-.209-.179-.06-.449-.314.18-.554.39-.734.629-.194-.134-.119-.299-.119-.449-.33.27-.54.54-.794.811-.061-.031-.105-.15-.135-.346-.779.75-1.889 2.623-.285 3.356 1.349-1.094 2.981-1.903 4.779-2.503l.041-.075zm-12.259 0c1.798.6 3.419 1.408 4.777 2.518 1.596-.75.493-2.623-.282-3.356-.041.194-.085.329-.135.359-.255-.27-.462-.54-.788-.81 0 .15.077.33-.117.45-.175-.239-.41-.45-.725-.63.149.256.025.33-.056.449-.24-.225-.465-.434-.899-.599.12.149.3.3.12.465-.239-.149-.494-.3-1.078-.42.135.149.404.3.238.45-.315-.122-.66-.212-1.035-.258.181.15.342.289.192.405-.345-.12-.806-.255-1.255-.135l.284.284c.03.037-.659.03-1.121.035.165.225.337.449.435.854-.045.045-.27.016-.483 0 .225.449.599.57.688.765-.135.096-.314.075-.523 0 .164.314.494.539.748.81-.074.044-.18.074-.464.037.239.26.524.494.869.704-.06.07-.271.069-.479.075.314.304.719.464 1.094.663-.195.136-.33.105-.465.105.255.225.72.329 1.139.464-.09.135-.164.165-.344.195.449.254 1.078.135 1.258.27-.045.119-.164.209-.314.27.719.045 2.697-.015 3.072-1.514-.736-.807-2.084-1.752-4.391-2.921l.04.016zM7.6.103c.236-.007.436.135.652.201.529-.17.65.063.91.159.577-.12.752.141 1.029.419l.322-.009c.869.507 1.305 1.536 1.457 2.065.152-.529.584-1.559 1.457-2.065l.321.007c.277-.283.453-.539 1.029-.418.261-.105.38-.33.911-.166.33-.104.62-.375 1.057-.045.368-.149.726-.195 1.045.09.495-.06.653.061.774.21.108 0 .809-.104 1.132.36.81-.09 1.064.464.774.988.165.255.337.494-.05.975.15.269.062.553-.27.913.091.374-.074.63-.374.839.06.51-.48.81-.629.914-.061.3-.181.584-.795.734-.089.449-.464.523-.824.614 1.185.675 2.188 1.558 2.188 3.731l.181.299c1.349.809 2.562 3.402.674 5.514-.119.659-.329 1.124-.511 1.648-.269 2.113-2.082 3.101-2.561 3.221-.689.525-1.438 1.02-2.442 1.363-.942.961-1.976 1.336-2.994 1.336h-.092c-1.033 0-2.063-.375-3.012-1.335-1.007-.344-1.754-.838-2.447-1.363-.479-.12-2.283-1.107-2.562-3.221-.187-.524-.394-1.004-.518-1.662-1.894-2.113-.681-4.705.666-5.515l.172-.3c0-2.172 1.005-3.057 2.188-3.73-.359-.09-.72-.165-.823-.615-.615-.15-.735-.434-.795-.734-.15-.105-.689-.404-.629-.928-.3-.211-.465-.465-.375-.854-.314-.346-.404-.645-.27-.915-.39-.479-.209-.733-.045-.974C3.236 1.329 3.491.76 4.3.85 4.614.385 5.32.491 5.423.491c.121-.15.285-.285.779-.225.314-.285.675-.24 1.049-.102.151-.12.286-.164.406-.164L7.6.103z"/></svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with Inkscape (http://www.inkscape.org/) by Marsupilami -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="766"
height="768"
viewBox="-2.61977004 -2.61977004 92.56520808 92.83416708"
id="svg8375">
<defs
id="defs8377" />
<path
d="M 0,12.40183 35.68737,7.5416 35.70297,41.96435 0.03321,42.16748 z m 35.67037,33.52906 0.0277,34.45332 -35.66989,-4.9041 -0.002,-29.77972 z M 39.99644,6.90595 87.31462,0 l 0,41.527 -47.31818,0.37565 z M 87.32567,46.25471 87.31457,87.59463 39.9964,80.91625 39.9301,46.17767 z"
id="path13" />
</svg>
<!-- version: 20110311, original size: 87.325668 87.594627, border: 3% -->

After

Width:  |  Height:  |  Size: 861 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="#000" fill-rule="nonzero" d="M2 4.819l8.11-1.105.004 7.823-8.107.047L2 4.819zm8.107 7.62l.006 7.83-8.107-1.114v-6.769l8.1.053zm.983-8.87L21.844 2v9.438l-10.754.085V3.57zm10.757 8.944l-.003 9.395L11.09 20.39l-.015-7.895 10.772.018z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@ -0,0 +1,149 @@
(function(){
'use strict';
function validateFormData(data) {
var errors = {}
if(!data.address) {
errors.email = "Please enter an email address.";
} else if(data.address.length > 244) {
errors.email = "Email is too long.<br>" +
"If your email address is really this long, we apologize. <br>" +
"Please email us directly (hello@ppl.family) so we can adjust our form.";
} else if(!/.+@.+\..+/.test(data.address)) {
errors.email = "Please enter a valid email address.";
}
if(data.comment && data.comment.length > 102400) {
errors.name = "Name is too long. <br>Please use a shorter version of your name.";
}
if(Object.keys(errors).length) {
return errors;
}
return false;
}
function enableForm(form) {
var elements = form.elements;
for(var i = 0; i < elements.length; ++i) {
elements[i].removeAttribute("disabled");
}
}
function disableForm(form) {
var elements = form.elements;
for(var i = 0; i < elements.length; ++i) {
elements[i].setAttribute("disabled", "");
}
}
function enableEmailForms() {
enableForm(document.querySelector(".js-inline-email-form"));
}
function disableEmailForms() {
disableForm(document.querySelector(".js-inline-email-form"));
}
function displaySuccess(form) {
var successEle = form.querySelector(".success-message");
if(successEle) {
successEle.classList.remove("js-inactive");
}
}
function hideSuccess(form){
var successEle = form.querySelector(".success-message");
if(successEle) {
successEle.classList.add("js-inactive");
}
}
function displayErrors(form, errors) {
errors = errors || {};
form.querySelectorAll(".input-error").forEach(function(ele) {
ele.classList.add("js-inactive");
});
form.querySelector(".form-error").classList.add("js-inactive");
Object.keys(errors).forEach(function(key) {
var errorEle;
if(key === "_form" && errors[key]) {
errorEle = form.querySelector(".form-error");
} else if(errors[key]) {
var query = "." + key + ".input-error";
errorEle = form.querySelector(query);
}
if(!errorEle) return;
errorEle.innerHTML = errors[key];
errorEle.classList.remove("js-inactive");
});
}
function submitFormData(form) {
hideSuccess(form);
var data = new FormData(form);
var msg = {
address: data.get("email")
, comment: 'telebit: ' + (data.get("name") || '')
};
var errors = validateFormData(msg);
displayErrors(form, errors);
if(errors) {
console.warn("Form validation failed: ", errors);
return Promise.resolve();
}
disableEmailForms();
return window.fetch('https://api.ppl.family/api/ppl.family/public/list', {
method: 'POST'
, cors: true
, headers: new Headers({ 'Content-Type': 'application/json' })
, body: JSON.stringify(msg)
}).then(function (resp) {
return resp.json();
}).then(function (data) {
enableEmailForms();
if (data.error) {
console.error("Error submitting form: ", data.error);
err = {
"_form": "Couldn't save email. <br>Try again or email hello@ppl.family directly."
};
return displayErrors(form, errors);
}
displaySuccess(form);
console.log("Successfully subscribed!");
form.reset();
}, function (err) {
enableEmailForms();
console.error("Error sending form data to server: ", err);
displayErrors(form, {
"_form": "Unable to send the info to the server.<br>" +
"Please try again or email hello@ppl.family directly."
});
});
}
document.body.addEventListener('submit', function (ev) {
console.log("Caught event!");
function eleMatchesString(ele, selector) {
return ele.matches ? ele.matches(selector) : ele.msMatchesSelector(selector);
}
var form = ev.target;
if (!eleMatchesString(form, '.js-inline-email-form')) {
return;
}
ev.preventDefault();
ev.stopPropagation();
submitFormData(form);
return;
});
})();

View File

@ -0,0 +1,47 @@
.quickstart-step-text {
align-items: center;
justify-content: center;
margin: 0 0 1.5em;
}
.quickstart-step {
flex-direction: column;
justify-content: center;
align-items: center;
}
.quickstart-terminal {
flex: 0 0;
}
.container.quickstart-container {
padding: 0;
}
@media (max-width: 900px) {
.donate-section p {
margin: 1.77777778em 10%;
font-size: 1.6em;
}
.quickstart-terminal {
width: 100%;
box-sizing: border-box;
font-size: .95em;
}
h2 {
font-size: 1.9em;
}
.quickstart-step-name {
font-size: 1.2em;
}
h3 {
font-size: 1.5em;
}
}

View File

@ -0,0 +1,433 @@
body{
font-family: Source Sans Pro, sans-serrif;
font-size: 17px;
line-height: 1.3333;
margin: 0;
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
a {
text-decoration: none;
color: inherit;
}
header {
background-color: #1a1a1a;
color: white;
}
.hero {
background-color: #1a1a1a;
color: white;
}
a:hover, u:hover {
color: #ddd;
}
.mailing-list-form ul, footer ul, header ul {
list-style-type: none;
padding: 0;
}
.container {
width: 788px;
margin: auto;
}
header > .container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.77778em 0;
}
footer .logo, header .logo {
font-size: 1.55556em;
font-weight: 900;
}
header .navigation-menu {
display: flex;
margin: initial;
align-items: center;
}
header .navigation-menu li {
margin-left: 1.77778em;
}
.hero .container {
padding-top: 0.44444em;
display: flex;
flex-direction: column;
align-items: center;
}
h1 {
font-size: 2.22222em;
margin: initial;
}
.spiel {
max-width: 60%;
text-align: center;
}
.link-button {
border: solid 1px white;
padding: 0.444444em 0.8888889em;
border-radius: 0.1111111em;
display: inline-block;
background-color: #1a1a1a;
color: #ffffff;
font-size: 1em;
font-family: inherit;
}
.hero-download {
margin: 1.33333333em;
}
a.link-button.wide {
padding: 0.44444em 1.7777776em;
}
.demo-container {
margin-top: 1em;
position: relative;
height: 236px;
width: 644px;
overflow: hidden;
}
.demo-browser {
position: absolute;
bottom: 0;
right: 0;
width: 544px;
height: 236px;
background-color: #ffffff;
border-radius: 4px 4px 0 0;
}
.demo-browser-buttons > div {
width: 11px;
height: 11px;
border: solid 1px #a6a6a6;
border-radius: 6px;
display: inline-block;
margin-left: 8px;
}
.demo-browser-header {
background-color: #ededed;
display: flex;
align-items: center;
border-radius: 4px 4px 0 0;
}
.demo-browser-buttons {
margin: 4px;
}
.demo-browser-address-bar {
color: #1a1a1a;
border: solid 1px #d9d9d9;
border-radius: 2px;
background-color: #ffffff;
flex: 1;
margin-left: 20px;
font-size: 0.83333em;
margin-right: 56px;
margin: 8px 56px 8px 20px;
padding: 5px;
display: flex;
justify-content: left;
align-items: center;
}
.demo-browser-address-bar img {
height: 17px;
float: left;
margin-left: 6px;
margin-right: 8px;
}
.demo-browser-body {
font-size: 32px;
background-color: #ffffff;
color: #bebebe;
padding: 12px;
}
.demo-terminal {
/*width: 418px;*/
width: 512px;
position: absolute;
font-size: 15px;
background-color: #f7f7f7;
font-weight: normal;
color: #1a1a1a;
padding: 24px 24px 20px;
bottom: 0;
font-family: monospace;
line-height: 1.35;
-webkit-box-shadow: -5px 5px 34px 7px rgba(17,17,17,0.18);
-moz-box-shadow: -5px 5px 34px 7px rgba(17,17,17,0.18);
box-shadow: -5px 5px 26px 10px rgba(17, 17, 17, 0.2);
}
.demo-terminal-line:before {
content: " ";
}
.demo-terminal-input:before {
content: "$";
}
.demo-terminal-output {
padding-left: 1em;
text-indent: -1em;
}
.demo-terminal-output:before {
content: ">";
}
h2 {text-align: center;font-size: 1.77778em;margin: 0 0 1.25em 0;}
body {}
.donate-section {
background-color: #f7f7f7;
padding: 1.777778em 0;
}
.use-it {
text-align: left;
text-indent: 4.3em;
margin: 1.75em 0;
}
.accent-color {
color: rgb(0,0,0,0.4);
}
.quickstart-step-number {
border-radius: 1em;
height: 1.583333333em;
width: 1.5833333333em;
font-weight: bold;
display: inline-flex;
align-items: center;
justify-content: space-around;
background-color: #f8f8f8;
margin-right: 0.5em;
flex-shrink: 0;
}
.quickstart-step {
font-size: 1.33333em;
display: flex;
flex-wrap: wrap;
margin-bottom: 2em;
justify-content: center;
}
.quickstart-step-text {
min-width: 9.583336em;
margin-right: 1.3333333em;
flex: 1 1;
display: flex;
}
.quickstart-terminal {
flex: 0 0 36.7em;
background-color: #f7f7f7;
font-family: monospace;
font-size: 0.8em;
width: 36.7em;
line-height: 1.33;
margin: 0;
padding: 0.8em 1em 0.8em 2em;
}
.quickstart-line:before {
content: " ";
}
.quickstart-input:before {
content: "$ ";
}
.quickstart-output:before {
content: "> ";
}
h3 {
text-align: center;
font-size: 1em;
}
.install-badges {
display: flex;
justify-content: space-between;
margin: auto;
}
.install-badge {
width: 9.9444444em;
display: flex;
align-items: center;
background-color: #f8f8f8;
}
.install-badge img, .install-badge svg {
width: 1.3333333em;
margin: 0.888888889em;
}
.feature.badge {
width: 9.888888889em;
}
.feature-badge img {
margin: auto;
display: block;
width: 1.333333333em;
}
.feature-badge {
width: 9.8888889em;
text-align: center;
}
.feature-badge div {
margin-top: 0.555555556em;
}
.feature-badges {
display: flex;
justify-content: space-between;
}
.donate-section p {
margin: 1.7777778em 7.555555556em;
text-align: center;
}
.feature-list {
margin: 4em 0;
}
.donate-section h2 {
margin: 0 0 0.88888889em 0;
}
.donate-section a.link-button {
border: none;
width: 11.1111111em;
padding-left: 0;
padding-right: 0;
}
.donate-section .container {
text-align: center;
}
input {
font-size: 1em;
padding: 0.44444444em;
margin: 0;
font-family: inherit;
border: solid 1px #d9d9d9;
}
.mailing-list-form .link-button {
border: none;
margin-left: 0.88889em;
width: 9em;
padding-left: 0;
padding-right: 0;
}
.mailing-list-form form {
text-align: center;
}
.mailing-list-form {
background-color: #d9d9d9;
padding: 1.77777778em 0;
}
.mailing-list-form li img {
width: 1.111111111em;
margin-right: 0.4444444em;
vertical-align: middle;
}
footer .container {
display: flex;
justify-content: space-between;
align-items: center;
}
footer {
background-color: #b3b3b3;
color: white;
padding: 1.444444444em 0;
}
footer li {
display: inline;
margin-left: 2.2222em;
font-size: 0.833333333em;
}
footer ul {
margin: 0;
}
.js-inactive {
display: none;
}
s {}
.mailing-list-form ul {
display: inline-block;
}
.mailing-list-form .container {
text-align: center;
}
.mailing-list-form li {
text-align: left;
}
a {}
.quickstart-terminal.qickstart-terminal-prompt:before {
content: "$ ";
}
.install-badge:hover {
cursor: pointer;
}
.install-badge:hover path {
fill: #ababab;
}
input[type="submit"] {
appearance: none;
-webkit-appearance: none;
}
.quickstart-container {
max-width: 1025px;
width: auto;
padding: 0px 3.111111111em;
}
.quickstart-step-name {
display: inline-block;
}

View File

@ -0,0 +1,84 @@
/*
interval: time between spans appearing
transitionTime: the time it takes for the span to finish "sliding" in to place.
transitionTime should be <= interval/2;
n: total number of spans sliding in and out
n should be > 1
nth: the value in "nth-child())"
*/
.sliding-vertical{
display: inline;
text-indent: 8px;
}
.sliding-vertical span{
animation: topToBottom 15s linear infinite 0s;/* interval * n */
-ms-animation: topToBottom 15s linear infinite 0s;/* interval * n */
-webkit-animation: topToBottom 15s linear infinite 0s;/* interval * n */
opacity: 0;
overflow: hidden;
position: absolute;
}
.sliding-vertical span:nth-child(2){
animation-delay: 2.5s;/* (nth - 1) * interval */
-ms-animation-delay: 2.5s;/* (nth - 1) * interval */
-webkit-animation-delay: 2.5s;/* (nth - 1) * interval */
}
.sliding-vertical span:nth-child(3){
animation-delay: 5s;
-ms-animation-delay: 5s;
-webkit-animation-delay: 5s;
}
.sliding-vertical span:nth-child(4){
animation-delay: 7.5s;
-ms-animation-delay: 7.5s;
-webkit-animation-delay: 7.5s;
}
.sliding-vertical span:nth-child(5){
animation-delay: 10s;
-ms-animation-delay: 10s;
-webkit-animation-delay: 10s;
}
.sliding-vertical span:nth-child(6){
animation-delay: 12.5s;
-ms-animation-delay: 12.5s;
-webkit-animation-delay: 12.5s;
}
/*
.sliding-vertical span:nth-child(7){
animation-delay: 15s;
-ms-animation-delay: 15s;
-webkit-animation-delay: 15s;
}
*/
/*topToBottom Animation*/
@keyframes topToBottom{
0% { opacity: 0; transform: translateY(-50px); }
6.667% { opacity: 1; transform: translateY(0px); }/* transitionTime/(interval*n) */
16.667% { opacity: 1; transform: translateY(0px); } /* 1/n */
23.333% { opacity: 0; transform: translateY(50px); } /* (interval + transitionTime)/(n*interval) */
}
@-moz-keyframes topToBottom{
0% { opacity: 0; -moz-transform: translateY(-50px); }
6.667% { opacity: 1; -moz-transform: translateY(0px); }
16.667% { opacity: 1; -moz-transform: translateY(0px); }
23.333% { opacity: 0; -moz-transform: translateY(50px); }
}
@-webkit-keyframes topToBottom{
0% { opacity: 0; -webkit-transform: translateY(-50px); }
6.667% { opacity: 1; -webkit-transform: translateY(0px); }
16.667% { opacity: 1; -webkit-transform: translateY(0px); }
23.333% { opacity: 0; -webkit-transform: translateY(50px); }
}
@-ms-keyframes topToBottom{
0% { opacity: 0; -ms-transform: translateY(-50px); }
6.667% { opacity: 1; -ms-transform: translateY(0px); }
16.667% { opacity: 1; -ms-transform: translateY(0px); }
23.333% { opacity: 0; -ms-transform: translateY(50px); }
}
.install-for {
margin-top: 3.1111111113em;
}

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="refresh" content="0;url=https://docs.google.com/presentation/d/e/2PACX-1vRQ1YyZcTDKYINLvUe8OdaDn_mIoCc0v8XSK-rgI3-b8EldgqpwbZEGmPn7J9pN1vnEJ1-pOcl_T-QP/pub">
<style>
body, html {
height: 100%;
margin: 0%;
padding: 0%;
background-color: black;
}
.bg {
background-image: url("http://www.tshirtvortex.net/wp-content/uploads/thenamesrex.jpg");
height: 100%;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
a {
color: white;
}
</style>
</head>
<body>
<div class="bg">
<center>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<h1><a href="https://docs.google.com/presentation/d/e/2PACX-1vRQ1YyZcTDKYINLvUe8OdaDn_mIoCc0v8XSK-rgI3-b8EldgqpwbZEGmPn7J9pN1vnEJ1-pOcl_T-QP/pub?start=false&loop=false&delayms=3000">Access Ability (look ma, no cloud) [slides]</a></h1>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,47 @@
'use strict';
var fs = require('fs');
var path = require('path');
var basedir = path.join(__dirname, 'emails');
var files = fs.readdirSync(basedir)
var emails = {};
files.forEach(function (fname) {
var fpath = path.join(basedir, fname);
var data;
var email;
var iat;
var mdata;
if (!/\.data$/.test(fname)) {
return;
}
data = JSON.parse(fs.readFileSync(fpath));
email = fname.replace('\.' + data.domains.join('') + '\.data', '');
mdata = JSON.parse(fs.readFileSync(path.join(basedir, email)));
if (data.iat) {
iat = new Date(data.iat * 1000).toISOString();
}
if (!emails[email]) {
emails[email] = {
domains: []
, ports: []
, nodes: [ { createdAt: iat, scheme: 'mailto', type: 'email', name: email } ]
, jtis: []
};
}
emails[email].jtis.push(data.id);
data.domains.forEach(function (d) {
emails[email].domains.push({ createdAt: iat, name: d, wildcard: true, hostname: mdata.hostname
, os: mdata.os_type, arch: mdata.os_arch });
});
data.ports.forEach(function (p) {
emails[email].ports.push({ createdAt: iat, number: p, hostname: mdata.hostname
, os: mdata.os_type, arch: mdata.os_arch });
});
});
console.log('');
console.log('[\n' + Object.keys(emails).map(function (k) { return JSON.stringify(emails[k]); }).join(',\n') + '\n]');
console.log('');
console.log('');
console.log(Object.keys(emails).join(', '));
console.log('');

315
lib/extensions/db.js Normal file
View File

@ -0,0 +1,315 @@
'use strict';
var PromiseA;
try {
PromiseA = require('bluebird');
} catch(e) {
PromiseA = global.Promise;
}
var path = require('path');
var sfs = require('safe-replace');
var DB = module.exports = {};
DB._savefile = path.join(__dirname, 'permissions.json');
DB._load = function () {
try {
DB._perms = require(DB._savefile);
} catch(e) {
try {
DB._perms = require(DB._savefile + '.bak');
} catch(e) {
DB._perms = [];
}
}
DB._byDomain = {};
DB._byPort = {};
DB._byEmail = {};
DB._byPpid = {};
DB._byId = {};
DB._grants = {};
DB._grantsMap = {};
DB._authz = {};
DB._perms.forEach(function (acc) {
if ('authz' === acc.type) {
DB._authz[acc.id] = acc;
return;
}
if (acc.id) {
// if account has an id
DB._byId[acc.id] = acc;
if (!DB._grants[acc.id]) {
DB._grantsMap[acc.id] = {};
DB._grants[acc.id] = [];
}
acc.domains.forEach(function (d) {
DB._grants[d.name + '|id|' + acc.id] = true;
if (!DB._grantsMap[acc.id][d.name]) {
DB._grantsMap[acc.id][d.name] = d;
DB._grants[acc.id].push(d);
}
});
acc.ports.forEach(function (p) {
DB._grants[p.number + '|id|' + acc.id] = true;
if (!DB._grantsMap[acc.id][p.number]) {
DB._grantsMap[acc.id][p.number] = p;
DB._grants[acc.id].push(p);
}
});
} else if (acc.nodes[0] && 'email' === acc.nodes[0].type) {
// if primary (first) node is email
//console.log("XXXX email", acc.nodes[0].name);
if (!DB._byEmail[acc.nodes[0].name]) {
DB._byEmail[acc.nodes[0].name] = {
account: acc
, node: acc.nodes[0]
};
}
}
// map domains to all nodes that have permission
// (which permission could be granted by more than one account)
acc.nodes.forEach(function (node) {
if ('mailto' === node.scheme || 'email' === node.type) {
if (!DB._grants[node.name]) {
DB._grantsMap[node.name] = {};
DB._grants[node.name] = [];
}
acc.domains.forEach(function (d) {
DB._grants[d.name + '|' + (node.scheme||node.type) + '|' + node.name] = true;
if (!DB._grantsMap[node.name][d.name]) {
DB._grantsMap[node.name][d.name] = d;
DB._grants[node.name].push(d);
}
});
acc.ports.forEach(function (p) {
DB._grants[p.number + '|' + (node.scheme||node.type) + '|' + node.name] = true;
if (!DB._grantsMap[node.name][p.number]) {
DB._grantsMap[node.name][p.number] = p;
DB._grants[node.name].push(p);
}
});
}
});
// TODO this also should be maps/arrays (... or just normal database)
acc.domains.forEach(function (domain) {
if (DB._byDomain[domain.name]) {
console.warn("duplicate domain '" + domain.name + "'");
console.warn("::existing account '" + acc.nodes.map(function (node) { return node.name; }) + "'");
console.warn("::new account '" + DB._byDomain[domain.name].account.nodes.map(function (node) { return node.name; }) + "'");
}
DB._byDomain[domain.name] = {
account: acc
, domain: domain
};
});
acc.ports.forEach(function (port) {
if (DB._byPort[port.number]) {
console.warn("duplicate port '" + port.number + "'");
console.warn("::existing account '" + acc.nodes.map(function (node) { return node.name; }) + "'");
console.warn("::new account '" + DB._byPort[port.number].account.nodes.map(function (node) { return node.name; }) + "'");
}
DB._byPort[port.number] = {
account: acc
, port: port
};
});
});
};
DB._load();
DB.accounts = {};
DB.accounts.get = function (obj) {
return PromiseA.resolve().then(function () {
//console.log('XXXX obj.name', DB._byEmail[obj.name]);
return DB._byId[obj.name] || (DB._byEmail[obj.name] || {}).account || null;
});
};
DB.accounts.add = function (obj) {
return PromiseA.resolve().then(function () {
if (obj.id) {
// TODO more checks
DB._perms.push(obj);
} else if ('email' === obj.nodes[0].type || obj.email) {
obj.email = undefined;
DB._perms.push(obj);
}
});
};
DB.authorizations = {};
DB.authorizations.create = function (acc, claim) {
if (!acc.id || !claim.type || !claim.value) { throw new Error("requires account id"); }
var crypto = require('crypto');
var authz = DB._authz[acc.id];
if (!authz) {
authz = {
id: acc.id
, type: 'authz'
, claims: []
};
DB._authz[acc.id] = authz;
DB._perms.push(authz);
}
// TODO check for unique type:value pairing in claims
claim.challenge = crypto.randomBytes(16).toString('hex');
claim.createdAt = Date.now();
claim.verifiedAt = 0;
authz.claims.push(claim);
DB.save();
return JSON.parse(JSON.stringify(claim));
};
DB.authorizations.check = function (acc, claim) {
var authz = DB._authz[acc.id];
var vclaim = null;
if (!authz) {
return vclaim;
}
authz.claims.some(function (c) {
console.log('authz.check', c);
if (claim.challenge) {
if (c.challenge === claim.challenge) {
vclaim = JSON.parse(JSON.stringify(c));
return true;
}
} else if (claim.value === c.value) {
vclaim = JSON.parse(JSON.stringify(c));
return true;
}
});
return vclaim;
};
DB.authorizations.checkAll = function (acc) {
var authz = DB._authz[acc.id];
if (!authz) {
return [];
}
return authz.claims.map(function (claim) {
return JSON.parse(JSON.stringify(claim));
});
};
DB.authorizations.verify = function (acc, claim) {
var scmp = require('scmp');
var authz = DB._authz[acc.id];
var vclaim;
if (!authz) { return false; }
authz.claims.some(function (c) {
if (scmp(c.challenge, claim.challenge)) {
vclaim = c;
c.verifiedAt = Date.now();
return true;
}
});
if (vclaim) {
DB.save();
return true;
}
return false;
};
DB.domains = {};
DB.domains.available = function (name) {
return PromiseA.resolve().then(function () {
return !DB._byDomain[name];
});
};
DB.domains._add = function (acc, opts) {
// TODO verifications to change ownership of a domain
return PromiseA.resolve().then(function () {
var err;
//var acc = DB._byId[aid];
var domain = {
name: (opts.domain || opts.name)
, hostname: opts.hostname
, os: opts.os
, createdAt: new Date().toISOString()
, wildcard: opts.wildcard
};
var pdomain;
var parts = (opts.domain || domain.name).split('.').map(function (el, i, arr) {
return arr.slice(i).join('.');
}).reverse();
parts.shift();
parts.pop();
if (parts.some(function (part) {
if (DB._byDomain[part] && DB._byDomain[part].wildcard) {
pdomain = part;
return true;
}
})) {
err = new Error("'" + domain.name + "' exists as '" + pdomain + "' and therefore requires an admin to review and approve");
err.code = "E_REQ_ADMIN";
throw err;
}
if (DB._byDomain[domain.name]) {
if (acc !== DB._byDomain[domain.name].account) {
throw new Error("domain '" + domain.name + "' exists");
}
// happily ignore non-change
return;
}
DB._byDomain[domain.name] = {
account: acc
, domain: domain
};
acc.domains.push(domain);
DB.save();
});
};
DB.ports = {};
DB.ports.available = function (number) {
return PromiseA.resolve().then(function () {
return !DB._byPort[number];
});
};
DB.ports._add = function (acc, opts) {
return PromiseA.resolve().then(function () {
//var acc = DB._byId[aid];
var port = {
number: opts.port || opts.number
, hostname: opts.hostname
, os: opts.os
, createdAt: new Date().toISOString()
};
if (DB._byPort[port.number]) {
// TODO verifications
throw new Error("port '" + port.number + "' exists");
}
DB._byPort[port.number] = {
account: acc
, port: port
};
acc.ports.push(port);
});
};
DB._save = function () {
return sfs.writeFileAsync(DB._savefile, JSON.stringify(DB._perms));
};
DB._saveToken = null;
DB._savePromises = [];
DB._savePromise = PromiseA.resolve();
DB.save = function () {
clearTimeout(DB._saveToken);
return new PromiseA(function (resolve, reject) {
function doSave() {
DB._savePromise = DB._savePromise.then(function () {
return DB._save().then(function (yep) {
DB._savePromises.forEach(function (p) {
p.resolve(yep);
});
DB._savePromises.length = 1;
}, function (err) {
DB._savePromises.forEach(function (p) {
p.reject(err);
});
DB._savePromises.length = 1;
});
});
return DB._savePromise;
}
DB._saveToken = setTimeout(doSave, 2500);
DB._savePromises.push({ resolve: resolve, reject: reject });
});
};

View File

1232
lib/extensions/index.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
{
"name": "telebit.commercial",
"version": "1.0.0",
"private": true,
"description": "Commercial node.js APIs for Telebit",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"jwk-to-pem": "^2.0.0",
"oauth3.js": "^1.2.5",
"scmp": "^1.0.2"
}
}

View File

@ -0,0 +1,12 @@
'use strict';
var perms = require('./permissions.json');
var emails = {};
perms.forEach(function (p) {
p.nodes.forEach(function (n) {
if ('email' === n.type) {
emails[n.name] = true;
}
});
});
console.log(Object.keys(emails).join(', '));

View File

@ -10,7 +10,7 @@ function noSniCallback(tag) {
var err = new Error("[noSniCallback] no handler set for '" + tag + "':'" + servername + "'");
console.error(err.message);
cb(new Error(err));
};
}
}
module.exports.create = function (state) {
@ -72,44 +72,38 @@ module.exports.create = function (state) {
state.tlsInvalidSniServer.on('tlsClientError', function () {
console.error('tlsClientError InvalidSniServer');
});
state.createHttpInvalid = function (opts) {
return http.createServer(function (req, res) {
if (!opts.servername) {
res.statusCode = 422;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end(
"3. An inexplicable temporal shift of the quantum realm... that makes me feel uncomfortable.\n\n"
+ "[ERROR] No SNI header was sent. I can only think of two possible explanations for this:\n"
+ "\t1. You really love Windows XP and you just won't let go of Internet Explorer 6\n"
+ "\t2. You're writing a bot and you forgot to set the servername parameter\n"
);
return;
}
// TODO use req.headers.host instead of servername (since domain fronting is disabled anyway)
res.statusCode = 502;
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(
"<h1>Oops!</h1>"
+ "<p>It looks like '" + encodeURIComponent(opts.servername) + "' isn't connected right now.</p>"
+ "<p><small>Last seen: " + opts.ago + "</small></p>"
+ "<p><small>Error: 502 Bad Gateway</small></p>"
);
});
};
state.httpsInvalid = function (opts, socket) {
state.httpsInvalid = function (servername, socket) {
// none of these methods work:
// httpsServer.emit('connection', socket); // this didn't work
// tlsServer.emit('connection', socket); // this didn't work either
//console.log('chunkLen', firstChunk.byteLength);
console.log('[httpsInvalid] servername', opts.servername);
console.log('[httpsInvalid] servername', servername);
//state.tlsInvalidSniServer.emit('connection', wrapSocket(socket));
var tlsInvalidSniServer = tls.createServer(state.tlsOptions, function (tlsSocket) {
console.log('[tlsInvalid] tls connection');
// We create an entire http server object because it's difficult to figure out
// how to access the original tlsSocket to get the servername
state.createHttpInvalid(opts).emit('connection', tlsSocket);
// things get a little messed up here
var httpInvalidSniServer = http.createServer(function (req, res) {
if (!servername) {
res.statusCode = 422;
res.end(
"3. An inexplicable temporal shift of the quantum realm... that makes me feel uncomfortable.\n\n"
+ "[ERROR] No SNI header was sent. I can only think of two possible explanations for this:\n"
+ "\t1. You really love Windows XP and you just won't let go of Internet Explorer 6\n"
+ "\t2. You're writing a bot and you forgot to set the servername parameter\n"
);
return;
}
res.end(
"You came in hot looking for '" + servername + "' and, granted, the IP address for that domain"
+ " must be pointing here (or else how could you be here?), nevertheless either it's not registered"
+ " in the internal system at all (which Seth says isn't even a thing) or there is no device"
+ " connected on the south side of the network which has informed me that it's ready to have traffic"
+ " for that domain forwarded to it (sorry I didn't check that deeply to determine which).\n\n"
+ "Either way, you're doing strange things that make me feel uncomfortable... Please don't touch me there any more.");
});
httpInvalidSniServer.emit('connection', tlsSocket);
});
tlsInvalidSniServer.on('tlsClientError', function () {
console.error('tlsClientError InvalidSniServer httpsInvalid');

View File

@ -68,6 +68,7 @@ module.exports.create = function (state) {
});
if (initToken) {
console.log('[wss.onConnection] token provided in http headers');
return Server.addToken(state, srv, initToken).then(function () {
Server.init(state, srv);
}).catch(function (err) {

View File

@ -172,7 +172,6 @@ var Server = {
, _initSocketHandlers: function (state, srv) {
function refreshTimeout() {
srv.lastActivity = Date.now();
Devices.touchDevice(state.deviceLists, srv);
}
function checkTimeout() {

View File

@ -2,16 +2,6 @@
var sni = require('sni');
var pipeWs = require('./pipe-ws.js');
var ago = require('./ago.js').AGO;
var up = Date.now();
function fromUptime(ms) {
if (ms) {
return ago(Date.now() - ms);
} else {
return "Not seen since relay restarted, " + ago(Date.now() - up);
}
}
module.exports.createTcpConnectionHandler = function (state) {
var Devices = state.Devices;
@ -37,16 +27,6 @@ module.exports.createTcpConnectionHandler = function (state) {
var str;
var m;
if (!firstChunk) {
try {
conn.end();
} catch(e) {
console.error("[lib/unwrap-tls.js] Error:", e);
conn.destroy();
}
return;
}
//conn.pause();
conn.unshift(firstChunk);
@ -58,15 +38,8 @@ module.exports.createTcpConnectionHandler = function (state) {
// defer after return (instead of being in many places)
function deferData(fn) {
if ('httpsInvalid' === fn) {
state[fn]({
servername: servername
, ago: fromUptime(Devices.lastSeen(state.deviceLists, servername))
}, conn);
} else if (fn) {
if (fn) {
state[fn](servername, conn);
} else {
console.error("[SANITY ERROR] '" + fn + "' doesn't have a state handler");
}
/*
process.nextTick(function () {
@ -75,81 +48,33 @@ module.exports.createTcpConnectionHandler = function (state) {
*/
}
var httpOutcomes = {
missingServername: function () {
console.log("[debug] [http] missing servername");
// TODO use a more specific error page
deferData('handleInsecureHttp');
}
, requiresSetup: function () {
console.log("[debug] [http] requires setup");
// TODO Insecure connections for setup will not work on secure domains (i.e. .app)
state.httpSetupServer.emit('connection', conn);
}
, isInternal: function () {
console.log("[debug] [http] is known internally (admin)");
if (/well-known/.test(str)) {
deferData('handleHttp');
} else {
deferData('handleInsecureHttp');
}
}
, isVhost: function () {
console.log("[debug] [http] is vhost (normal server)");
if (/well-known/.test(str)) {
deferData('handleHttp');
} else {
deferData('handleInsecureHttp');
}
}
, assumeExternal: function () {
console.log("[debug] [http] assume external");
var service = 'http';
function tryTls() {
var vhost;
if (!Devices.exist(state.deviceLists, servername)) {
// It would be better to just re-read the host header rather
// than creating a whole server object, but this is a "rare"
// case and I'm feeling lazy right now.
console.log("[debug] [http] no device connected");
state.createHttpInvalid({
servername: servername
, ago: fromUptime(Devices.lastSeen(state.deviceLists, servername))
}).emit('connection', conn);
return;
}
// TODO make https redirect configurable on a per-domain basis
// /^\/\.well-known\/acme-challenge\//.test(str)
if (/well-known/.test(str)) {
// HTTP
console.log("[debug] [http] passthru");
pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport);
return;
} else {
console.log("[debug] [http] redirect to https");
deferData('handleInsecureHttp');
}
}
};
var tlsOutcomes = {
missingServername: function () {
if (!servername) {
if (state.debug) { console.log("No SNI was given, so there's nothing we can do here"); }
deferData('httpsInvalid');
return;
}
, requiresSetup: function () {
if (!state.servernames.length) {
console.info("[Setup] https => admin => setup => (needs bogus tls certs to start?)");
deferData('httpsSetupServer');
return;
}
, isInternal: function () {
if (-1 !== state.servernames.indexOf(servername)) {
if (state.debug) { console.log("[Admin]", servername); }
deferData('httpsTunnel');
return;
}
, isVhost: function (vhost) {
if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); }
deferData('httpsVhost');
if (state.config.nowww && /^www\./i.test(servername)) {
console.log("TODO: use www bare redirect");
}
, assumeExternal: function () {
var nextDevice = Devices.next(state.deviceLists, servername);
function run() {
var nextDevice = Devices.next(state.deviceLists, servername);
if (!nextDevice) {
if (state.debug) { console.log("No devices match the given servername"); }
deferData('httpsInvalid');
@ -157,33 +82,27 @@ module.exports.createTcpConnectionHandler = function (state) {
}
if (state.debug) { console.log("pipeWs(servername, service, deviceLists['" + servername + "'], socket)"); }
deferData();
pipeWs(servername, service, nextDevice, conn, serviceport);
}
};
function handleConnection(outcomes) {
var vhost;
// No routing information available
if (!servername) { outcomes.missingServername(); return; }
// Server needs to be set up
if (!state.servernames.length) { outcomes.requiresSetup(); return; }
// This is one of the admin domains
if (-1 !== state.servernames.indexOf(servername)) { outcomes.isInternal(); return; }
// TODO don't run an fs check if we already know this is working elsewhere
//if (!state.validHosts) { state.validHosts = {}; }
if (state.config.vhost) {
vhost = state.config.vhost.replace(/:hostname/, servername);
vhost = state.config.vhost.replace(/:hostname/, (servername||'reallydoesntexist'));
if (state.debug) { console.log("[tcp] [vhost]", state.config.vhost, "=>", vhost); }
//state.httpsVhost(servername, conn);
//return;
require('fs').readdir(vhost, function (err, nodes) {
if (state.debug && err) { console.log("VHOST error", err); }
if (err || !nodes) { outcomes.assumeExternal(); return; }
outcomes.isVhost(vhost);
if (err || !nodes) { run(); return; }
//if (nodes) { deferData('httpsVhost'); return; }
deferData('httpsVhost');
});
return;
}
outcomes.assumeExternal();
run();
}
// https://github.com/mscdex/httpolyglot/issues/3#issuecomment-173680155
@ -192,19 +111,40 @@ module.exports.createTcpConnectionHandler = function (state) {
service = 'https';
servername = (sni(firstChunk)||'').toLowerCase().trim();
if (state.debug) { console.log("[tcp] tls hello from '" + servername + "'"); }
handleConnection(tlsOutcomes);
tryTls();
return;
}
if (firstChunk[0] > 32 && firstChunk[0] < 127) {
// (probably) HTTP
str = firstChunk.toString();
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
servername = (m && m[1].toLowerCase() || '').split(':')[0];
if (state.debug) { console.log("[tcp] http hostname '" + servername + "'"); }
if (/HTTP\//i.test(str)) {
handleConnection(httpOutcomes);
if (!state.servernames.length) {
console.info("[tcp] No admin servername. Entering setup mode.");
deferData();
state.httpSetupServer.emit('connection', conn);
return;
}
service = 'http';
// TODO make https redirect configurable
// /^\/\.well-known\/acme-challenge\//.test(str)
if (/well-known/.test(str)) {
// HTTP
if (Devices.exist(state.deviceLists, servername)) {
deferData();
pipeWs(servername, service, Devices.next(state.deviceLists, servername), conn, serviceport);
return;
}
deferData('handleHttp');
return;
}
// redirect to https
deferData('handleInsecureHttp');
return;
}
}

View File

@ -37,20 +37,32 @@
},
"homepage": "https://git.coolaj86.com/coolaj86/telebit-relay.js",
"dependencies": {
"bluebird": "^3.5.1",
"@coolaj86/urequest": "^1.3.5",
"body-parser": "^1.18.3",
"cluster-store": "^2.0.8",
"connect-cors": "^0.5.6",
"escape-html": "^1.0.3",
"express": "^4.16.3",
"finalhandler": "^1.1.1",
"greenlock": "^2.2.4",
"human-readable-ids": "^1.0.4",
"js-yaml": "^3.11.0",
"jsonwebtoken": "^8.3.0",
"jwk-to-pem": "^2.0.0",
"mkdirp": "^0.5.1",
"nowww": "^1.2.1",
"proxy-packer": "^2.0.0",
"recase": "^1.0.4",
"redirect-https": "^1.1.5",
"request": "^2.87.0",
"safe-replace": "^1.0.3",
"serve-static": "^1.13.2",
"sni": "^1.0.0",
"ws": "^5.1.1"
},
"trulyOptionalDependencies": {
"bluebird": "^3.5.1"
},
"engineStrict": true,
"engines": {
"node": "10.2.1"

View File

@ -1,24 +0,0 @@
name: telebit-relay
version: '0.20.0'
summary: Because friends don't let friends localhost
description: |
A server that works in combination with Telebit Remote
to allow you to serve http and https from any computer,
anywhere through a secure tunnel.
grade: stable
confinement: strict
apps:
telebit-relay:
command: telebit-relay --config $SNAP_COMMON/config.yml
plugs: [network, network-bind]
daemon: simple
parts:
telebit-relay:
plugin: nodejs
node-engine: 10.13.0
source: .
override-build: |
snapcraftctl build