This commit is contained in:
2017-02-09 17:23:47 -05:00
parent cdd4f67f49
commit 211ed3b229
10 changed files with 384 additions and 2 deletions
+3
View File
@@ -0,0 +1,3 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
Generated
+16
View File
@@ -0,0 +1,16 @@
{
"default": {},
"develop": {},
"_meta": {
"sources": [
{
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
],
"requires": {},
"hash": {
"sha256": "cb65967bdf3a7372749ce6144be4ee63f412aeeac624342fda3a2b58b1c43c15"
}
}
}
+8 -2
View File
@@ -1,2 +1,8 @@
# typy.io
just type™
# typy.io: Share text with the masses
lorem ipusm
A Kenneth Reitz Project™
------------------------
ۜ\(סּںסּَ` )/ۜ
+26
View File
@@ -0,0 +1,26 @@
// client-side js
// run by the browser each time your view template is loaded
// by default, you've got jQuery,
// add other scripts at the bottom of index.html
$(function() {
console.log('hello world :o');
$.get('/dreams', function(dreams) {
dreams.forEach(function(dream) {
$('<li></li>').text(dream).appendTo('ul#dreams');
});
});
$('form').submit(function(event) {
event.preventDefault();
dream = $('input').val();
$.post('/dreams?' + $.param({dream: dream}), function() {
$('<li></li>').text(dream).appendTo('ul#dreams');
$('input').val('');
$('input').focus();
});
});
});
+82
View File
@@ -0,0 +1,82 @@
/* styles */
/* called by your view template */
/* You might want to try something fancier: */
/* less: http://lesscss.org/ */
* {
box-sizing: border-box;
}
body {
font-family: helvetica, arial, sans-serif;
margin: 25px;
}
h1 {
font-weight: bold;
color: pink;
}
.bold {
font-weight: bold;
}
p {
max-width: 600px;
}
form {
margin-bottom: 25px;
padding: 15px;
background-color: cyan;
display: inline-block;
width: 100%;
max-width: 340px;
border-radius: 3px;
}
input {
display: block;
margin-bottom: 10px;
padding: 5px;
width: 100%;
border: 1px solid lightgrey;
border-radius: 3px;
font-size: 16px;
}
button {
font-size: 16px;
border-radius: 3px;
background-color: lightgrey;
border: 1px solid grey;
box-shadow: 2px 2px teal;
cursor: pointer;
}
button:hover {
background-color: yellow;
}
button:active {
box-shadow: none;
}
li {
margin-bottom: 5px;
}
footer {
margin-top: 50px;
padding-top: 25px;
border-top: 1px solid lightgrey;
}
footer > a {
color: #BBBBBB;
}
.nicejob {
text-decoration: line-through;
}
+55
View File
@@ -0,0 +1,55 @@
class SaferProxyFix(object):
"""This middleware can be applied to add HTTP proxy support to an
application that was not designed with HTTP proxies in mind. It
sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers.
If you have more than one proxy server in front of your app, set
num_proxy_servers accordingly
Do not use this middleware in non-proxy setups for security reasons.
get_remote_addr will raise an exception if it sees a request that
does not seem to have enough proxy servers behind it so long as
detect_misconfiguration is True.
The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in
the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and
`werkzeug.proxy_fix.orig_http_host`.
:param app: the WSGI application
"""
def __init__(self, app, num_proxy_servers=1, detect_misconfiguration=False):
self.app = app
self.num_proxy_servers = num_proxy_servers
self.detect_misconfiguration = detect_misconfiguration
def get_remote_addr(self, forwarded_for):
"""Selects the new remote addr from the given list of ips in
X-Forwarded-For. By default the last one is picked. Specify
num_proxy_servers=2 to pick the second to last one, and so on.
"""
if self.detect_misconfiguration and not forwarded_for:
raise Exception("SaferProxyFix did not detect a proxy server. Do not use this fixer if you are not behind a proxy.")
if self.detect_misconfiguration and len(forwarded_for) < self.num_proxy_servers:
raise Exception("SaferProxyFix did not detect enough proxy servers. Check your num_proxy_servers setting.")
if forwarded_for and len(forwarded_for) >= self.num_proxy_servers:
return forwarded_for[-1 * self.num_proxy_servers]
def __call__(self, environ, start_response):
getter = environ.get
forwarded_proto = getter('HTTP_X_FORWARDED_PROTO', '')
forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',')
forwarded_host = getter('HTTP_X_FORWARDED_HOST', '')
environ.update({
'werkzeug.proxy_fix.orig_wsgi_url_scheme': getter('wsgi.url_scheme'),
'werkzeug.proxy_fix.orig_remote_addr': getter('REMOTE_ADDR'),
'werkzeug.proxy_fix.orig_http_host': getter('HTTP_HOST')
})
forwarded_for = [x for x in [x.strip() for x in forwarded_for] if x]
remote_addr = self.get_remote_addr(forwarded_for)
if remote_addr is not None:
environ['REMOTE_ADDR'] = remote_addr
if forwarded_host:
environ['HTTP_HOST'] = forwarded_host
if forwarded_proto:
environ['wsgi.url_scheme'] = forwarded_proto
return self.app(environ, start_response)
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import hashlib
import bucketstore
import mistune
from decouple import config
from flask import Flask, request, render_template, jsonify, redirect, url_for, Response
from flask_caster import FlaskCaster
from flask_uuid import FlaskUUID
from raven.contrib.flask import Sentry
from saferproxyfix import SaferProxyFix
# Support for gomix's 'front-end' and 'back-end' UI.
app = Flask(__name__, static_folder='public', template_folder='views')
app.debug = config('DEBUG', default=True, cast=bool)
# app.wsgi_app = SaferProxyFix(app.wsgi_app)
# Set the app secret key from the secret environment variables.
app.secret = config('SECRET')
# Flask plugins
caster = FlaskCaster(app)
FlaskUUID(app)
sentry = Sentry(app, dsn=config('SENTRY_DSN'))
# The S3 Key/Value store.
store = bucketstore.get('typy', create=True)
store_total = len(store.list())
@app.after_request
def apply_kr_hello(response):
"""Adds some headers to all responses."""
# Made by Kenneth Reitz.
if 'MADE_BY' in os.environ:
response.headers["X-Was-Here"] = os.environ.get('MADE_BY')
# Powered by Flask.
response.headers["X-Powered-By"] = os.environ.get('POWERED_BY')
return response
@app.route('/')
def type_away(fork=None):
if fork:
doc = store[fork]
else:
doc = None
return render_template('write.html', total=store_total, seed=doc)
@app.route('/raw')
def document_list():
return jsonify({'documents': store.list()})
@app.route('/', methods=['POST'])
def put_type():
doc = request.form['document']
# Sha256 of document.
sha = hashlib.sha256(doc).hexdigest()
# Store the document.
store[sha] = doc
return redirect(url_for('get_type', hash=sha))
@app.route('/<hash>')
def get_type(hash):
doc = mistune.markdown(store[hash])
return render_template('doc.html', doc=doc, hash=hash, total=store_total)
@app.route('/<hash>/raw')
def get_raw_type(hash):
return Response(store[hash], mimetype='text/plain')
@app.route('/<hash>/fork')
def fork_type(hash):
return type_away(fork=hash)
if __name__ == '__main__':
app.run()
+6
View File
@@ -0,0 +1,6 @@
# For development use (simple logging, etc):
# pip install pipenv
# python server.py
# For production use:
gunicorn server:app
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>typy: something goes here</title>
<link id="favicon" rel="icon" href="https://gomix.com/favicon-app.ico" type="image/x-icon">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<style>
#document {
font-family: 'Lato', sans-serif;
font-size: 1.7rem;
}
</style>
</head>
<body>
<header>
<a href="/">
<button>
typy: just type™
</button>
</a>
<a href="/{{ hash }}/fork">
<button class="button-primary">Fork</button>
</a>
</header>
<hr>
<main id="document">
{{ doc|safe }}
</main>
<footer>
<hr/>
<p> Document (1 of ~{{ total }}): <code># <a href="/{{ hash }}/raw">{{ hash }}</a> </code></p>
</footer>
<script src="/public/client.js"></script>
</body>
</html>
+53
View File
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<title>typy: something goes here</title>
<link id="favicon" rel="icon" href="https://gomix.com/favicon-app.ico" type="image/x-icon">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
</head>
<body>
<header>
<a href="/">
<button>
typy: just type™
</button>
<input type="submit" class="button-primary" form="form" value="save"></input>
</a>
</header>
<main>
<textarea id="document" name="document" form="form">{% if seed %} {{ seed }} {% endif %}</textarea>
</main>
<footer>
<hr/>
<p> Document (1 of ~{{ total }}):</p>
</footer>
<form id="form" method="post" action="/"></form>
<script src="/public/client.js"></script>
<script>
var simplemde = new SimpleMDE({
autosave: {
enabled: true,
delay: 1000,
uniqueId: 'document'
},
placeholder: "Just type..."
});
simplemd.autofocus = true;
simplemde.placeholder("just type...");
var array = $('#typ').val().split(/\n|\r/);
</script>
</body>
</html>