Skip to content

Commit

Permalink
Update from master
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonEtco committed Jan 16, 2018
0 parents commit 201a34b
Show file tree
Hide file tree
Showing 25 changed files with 1,358 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"presets": [
["env", {
"targets": {
"browsers": [
"last 2 versions",
"ios_saf >= 8",
"ie >= 10",
"chrome >= 49",
"firefox >= 49",
"> 1%"
]
},
"debug": false,
"loose": true,
"useBuiltIns": true
}],
"react"
],
"plugins": [
"transform-class-properties",
[
"transform-object-rest-spread",
{ "useBuiltIns": true }
]
]
}
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.min.js
coverage
10 changes: 10 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"parser": "babel-eslint",
"extends": ["standard", "standard-react"],
"rules": {
"jsx-quotes": 0
},
"env": {
"jest": true
}
}
5 changes: 5 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const app = require('./server')()
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log('Listening at http://localhost:' + port)
})
19 changes: 19 additions & 0 deletions keep-alive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module.exports = class KeepAlive {
constructor (callback, delay) {
this.callback = callback
this.delay = delay
}

start () {
this.id = setInterval(this.callback, this.delay)
}

stop () {
clearInterval(this.id)
}

reset () {
this.stop()
this.start()
}
}
84 changes: 84 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"name": "smee-server",
"version": "0.0.3",
"description": "",
"author": "Jason Etcovitch <[email protected]> (https://github.com/JasonEtco)",
"license": "ISC",
"repository": "https://github.com/probot/smee.git",
"scripts": {
"start": "node ./index.js",
"start-dev": "concurrently \"nodemon --ignore src/ ./index.js\" \"webpack -w\"",
"build": "webpack -p",
"test": "jest --coverage && eslint",
"postinstall": "npm run build"
},
"dependencies": {
"autoprefixer": "^7.1.6",
"babel-core": "^6.26.0",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.2",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"connect-sse": "^1.2.0",
"copy-to-clipboard": "^3.0.8",
"copy-webpack-plugin": "^4.2.0",
"crypto": "^1.0.1",
"css-loader": "^0.28.7",
"eventsource": "^1.0.5",
"express": "^4.16.2",
"express-sslify": "^1.2.0",
"extract-text-webpack-plugin": "^3.0.1",
"get-value": "^2.0.6",
"glob-all": "^3.1.0",
"helmet": "^3.9.0",
"highlight.js": "^9.12.0",
"html-webpack-plugin": "^2.30.1",
"moment": "^2.19.1",
"moment-timezone": "^0.5.14",
"node-sass": "^4.5.3",
"postcss-loader": "^2.0.8",
"primer-css": "^9.6.0",
"prop-types": "^15.6.0",
"purify-css": "^1.2.5",
"purifycss-webpack": "^0.7.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-json-view": "^1.13.2",
"react-octicons": "^0.2.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.19.0",
"webpack": "^3.8.1"
},
"devDependencies": {
"babel-jest": "^21.2.0",
"concurrently": "^3.5.0",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"eslint-config-standard": "^10.2.1",
"eslint-config-standard-react": "^5.0.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-react": "^7.5.1",
"eslint-plugin-standard": "^3.0.1",
"jest": "^21.2.1",
"nodemon": "^1.12.1",
"raf": "^3.4.0",
"react-test-renderer": "^16.2.0",
"standard": "^10.0.3",
"supertest": "^3.0.0"
},
"engines": {
"node": "8.9.1"
},
"jest": {
"setupFiles": [
"./tests/setup.js"
],
"testPathIgnorePatterns": [
"/node_modules/"
]
}
}
Binary file added public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>smee.io</title>
<link rel="shortcut icon" href="/public/favicon.png">
<link rel="stylesheet" href="/public/main.min.css">
</head>
<body>
<header class="text-white text-center p-responsive bg-blue d-flex flex-column flex-items-center flex-justify-center" style="height: 80vh">
<div class="header__anim">
<div class="header__anim__payload"></div>
<div class="header__anim__line"></div>
<div class="header__anim__circle header__anim__center"></div>
<div class="header__anim__circle header__anim__dashed-circle"></div>
</div>
<h1 class="f00-light">smee.io</h1>
<h2 class="blue-700">Webhook payload delivery service</h2>
<p class="lead text-white" style="opacity: 0.8">Receives payloads then sends them to your locally running application.</p>
<a href="/new" class="btn btn-outline btn-outline-blue">Start a new channel</a>
</header>

<main class="container-lg py-6 mt-6 p-responsive">
<p class="lead text-center col-12 col-md-8 mx-auto">If your application needs to respond to webhooks, you'll need some way to expose <strong>localhost</strong> to the internet. <strong>smee.io</strong> is a small service that uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events" target="_blank" rel="noopener noreferrer">Server-Sent Events</a> to proxy payloads from the webhook source, then transmit them to your locally running application.</p>

<div class="main__anim my-6">
<div class="main__anim__circle main__anim__left">
<span>Webhook Emitter</span>
</div>
<div class="main__anim__circle main__anim__right">
<span>localhost</span>
</div>
<div class="main__anim__line"></div>
<div class="main__anim__payload"></div>
<div class="main__anim__circle main__anim__center">
<span></span>
</div>
</div>

<p class="lead text-center col-12 col-md-8 mx-auto">Tell your webhook source to send payloads to your <strong>smee.io</strong> channel, then either use the <a href="https://npmjs.com/package/smee-cli" target="_blank" rel="noopener noreferrer">smee CLI</a> or, if you're using <a href="https://probot.github.io" target="_blank" rel="noopener noreferrer">Probot</a> to build a GitHub App, just <a href="https://probot.github.io/docs/webhooks/">set the environment variable</a>.</p>
</main>

<footer class="border-top text-center py-4">
Made with 🤖 by the <a href="https://github.com/probot" target="_blank" rel="noopener noreferrer">Probot team</a>.
</footer>
</body>
</html>
21 changes: 21 additions & 0 deletions public/webhooks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webhooks</title>
<link rel="shortcut icon" href="/public/favicon.png">
<link rel="stylesheet" href="/public/main.min.css">
</head>
<body>
<noscript>
<div class="no-script">
<h1>You must enable JavaScript to use try out todo.</h1>
<a href="https://github.com/probot/webhooks/issues" target="_blank" rel="noopener noreferrer">Need help?</a>
</div>
</noscript>
<div class="mount"></div>
<script src="/public/main.min.js"></script>
</body>
</html>
97 changes: 97 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
const sse = require('connect-sse')()
const express = require('express')
const crypto = require('crypto')
const bodyParser = require('body-parser')
const EventEmitter = require('events')
const path = require('path')

const KeepAlive = require('./keep-alive')

// Tiny logger to prevent logs in tests
const log = process.env.NODE_ENV === 'test' ? _ => _ : console.log

module.exports = () => {
const events = new EventEmitter()
const app = express()
const pubFolder = path.join(__dirname, 'public')

if (process.env.FORCE_HTTPS) {
app.use(require('helmet')())
app.use(require('express-sslify').HTTPS({ trustProtoHeader: true }))
}

app.use(bodyParser.json())
app.use('/public', express.static(pubFolder))

app.get('/', (req, res) => {
res.sendFile(path.join(pubFolder, 'index.html'))
})

app.get('/new', (req, res) => {
const protocol = req.headers['x-forwarded-proto'] || req.protocol
const host = req.headers['x-forwarded-host'] || req.get('host')
const channel = crypto
.randomBytes(12)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '~')

res.redirect(307, `${protocol}://${host}/${channel}`)
})

app.get('/:channel', (req, res, next) => {
if (req.accepts('html')) {
res.sendFile(path.join(pubFolder, 'webhooks.html'))
} else {
next()
}
}, sse, (req, res) => {
function send (data) {
res.json(data)
keepAlive.reset()
}

function close () {
events.removeListener(channel, send)
keepAlive.stop()
log('Client disconnected', channel, events.listenerCount(channel))
}

const channel = req.params.channel

// Setup interval to ping every 30 seconds to keep the connection alive
const keepAlive = new KeepAlive(() => res.json({}, 'ping'), 30 * 1000)
keepAlive.start()

// Allow CORS
res.setHeader('Access-Control-Allow-Origin', '*')

// Listen for events on this channel
events.on(channel, send)

// Clean up when the client disconnects
res.on('close', close)

res.json({}, 'ready')

log('Client connected', channel, events.listenerCount(channel))
})

app.post('/:channel', (req, res) => {
events.emit(req.params.channel, {
...req.headers,
body: req.body,
timestamp: Date.now()
})
res.status(200).end()
})

// Resend payload via the event emitter
app.post('/:channel/redeliver', (req, res) => {
events.emit(req.params.channel, req.body)
res.status(200).end()
})

return app
}
Loading

0 comments on commit 201a34b

Please sign in to comment.