Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f02ee7b
Showing
59 changed files
with
2,938 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.py[cod] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2013-2014 Matthias Eisen | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Newman | ||
|
||
Form mailing for static websites | ||
|
||
## Prerequisites | ||
|
||
- A Google App Engine account | ||
- A Mailgun Account | ||
|
||
## Installation | ||
|
||
1. Create a new App Engine app | ||
2. Open src/app.yaml and enter your app id | ||
3. Open src/config.py and edit it according to the comments | ||
4. Deploy your app to App Engine | ||
|
||
## Contact | ||
|
||
me@matthiaseisen.com | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import config | ||
import random | ||
from google.appengine.ext import ndb | ||
from base_handler import BaseHandler | ||
from google.appengine.api import users | ||
|
||
|
||
class Account(ndb.Model): | ||
api_key_quota = ndb.IntegerProperty(default=config.default_api_key_quota) | ||
number_of_api_keys = ndb.IntegerProperty(default=0) | ||
email = ndb.StringProperty() | ||
created = ndb.DateTimeProperty(auto_now_add=True) | ||
updated = ndb.DateTimeProperty(auto_now=True) | ||
|
||
@classmethod | ||
def get_by_api_key(cls, api_key): | ||
result = cls.get_by_id(api_key) | ||
if result: | ||
return result | ||
return cls.query(cls.api_key == api_key).get() | ||
|
||
|
||
class ApiKey(ndb.Model): | ||
email = ndb.StringProperty() | ||
label = ndb.StringProperty() | ||
user_id = ndb.StringProperty() | ||
created = ndb.DateTimeProperty(auto_now_add=True) | ||
updated = ndb.DateTimeProperty(auto_now=True) | ||
|
||
@staticmethod | ||
def random_key(length): | ||
chars = 'abcdefghijklmnopqrstuvwxyz0123456789' | ||
return ''.join(random.choice(chars) for x in xrange(length)) | ||
|
||
@classmethod | ||
def exists(cls, api_key): | ||
return cls.get_by_id(api_key) and True | ||
|
||
@classmethod | ||
def unused_api_key(cls): | ||
new_key = cls.random_key(32) | ||
while cls.exists(new_key): | ||
new_key = cls.random_key(32) | ||
return new_key | ||
|
||
|
||
class AccountHandler(BaseHandler): | ||
def get(self): | ||
user = self.user() | ||
if user: | ||
account = Account.get_by_id(user.user_id(), parent=self.root.key) | ||
if not account: | ||
account = Account( | ||
id=user.user_id(), | ||
parent=self.root.key, | ||
email=user.email(), | ||
) | ||
api_key = ApiKey( | ||
id=ApiKey.unused_api_key(), | ||
parent=self.root.key, | ||
user_id=user.user_id(), | ||
email='mail@example.com', | ||
label='Default' | ||
) | ||
account.number_of_api_keys += 1 | ||
ndb.put_multi([api_key, account]) | ||
api_keys = [{ | ||
'key': api_key.key.id(), | ||
'email': api_key.email, | ||
'label': api_key.label, | ||
}] | ||
else: | ||
api_keys = [ | ||
{ | ||
'key': k.key.id(), | ||
'email': k.email or '', | ||
'label': k.label or '', | ||
} for k in ApiKey.query( | ||
ApiKey.user_id == user.user_id(), | ||
ancestor=self.root.key | ||
) | ||
] | ||
self.context.update({ | ||
'api_key_quota': account.api_key_quota, | ||
'number_of_api_keys': account.number_of_api_keys, | ||
'api_keys': api_keys, | ||
'email': account.email or '', | ||
'active_page': 'account', | ||
}) | ||
return self.render('account.html') | ||
return self.redirect(self.uri_for('login', _full=True)) | ||
|
||
|
||
class ApiKeyHandler(BaseHandler): | ||
|
||
def post(self): | ||
user = self.user() | ||
if not user: | ||
return self.error(401) | ||
try: | ||
label = self.request.get('label').strip() | ||
except AttributeError: | ||
label = None | ||
try: | ||
recipient = self.request.get('recipient').strip() | ||
except AttributeError: | ||
recipient = None | ||
api_key = self.request.get('api-key').strip() | ||
api_key = ( | ||
api_key and ndb.Key( | ||
'PseudoParent', 'root', | ||
'ApiKey', api_key | ||
).get() | ||
) or None | ||
if api_key: | ||
api_key.email = recipient | ||
api_key.label = label | ||
api_key.put() | ||
else: | ||
account = Account.get_by_id(user.user_id(), parent=self.root.key) | ||
if account and account.number_of_api_keys < account.api_key_quota: | ||
api_key = ApiKey( | ||
id=ApiKey.unused_api_key(), | ||
parent=self.root.key, | ||
user_id=user.user_id(), | ||
email=recipient, | ||
label=label | ||
) | ||
account.number_of_api_keys += 1 | ||
ndb.put_multi([api_key, account]) | ||
else: | ||
return self.error(400) | ||
return self.redirect(self.uri_for('account')) | ||
|
||
|
||
class LoginHandler(BaseHandler): | ||
def get(self): | ||
if self.user(): | ||
return self.redirect(self.uri_for('account')) | ||
else: | ||
return self.redirect( | ||
users.create_login_url(self.uri_for('account', _full=True)) | ||
) | ||
|
||
|
||
class LogoutHandler(BaseHandler): | ||
def get(self): | ||
return self.redirect( | ||
users.create_logout_url(self.uri_for('index', _full=True)) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# ---------------------- | ||
application: your-app-id | ||
# ---------------------- | ||
version: 1-0-1 | ||
runtime: python27 | ||
api_version: 1 | ||
threadsafe: true | ||
|
||
handlers: | ||
|
||
- url: /favicon.ico | ||
static_files: static/img/favicon.ico | ||
upload: static/img/favicon.ico | ||
|
||
- url: /robots.txt | ||
static_files: static/robots.txt | ||
upload: static/robots.txt | ||
|
||
- url: /static | ||
static_dir: static | ||
|
||
- url: /.* | ||
script: main.app | ||
|
||
libraries: | ||
- name: webapp2 | ||
version: latest | ||
- name: jinja2 | ||
version: latest | ||
|
||
inbound_services: | ||
|
||
- warmup |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import os | ||
import webapp2 | ||
import jinja2 | ||
from google.appengine.api import users | ||
from google.appengine.ext import ndb | ||
import config | ||
|
||
|
||
class PseudoParent(ndb.Model): | ||
timestamp = ndb.DateTimeProperty(auto_now_add=True) | ||
|
||
|
||
class BaseHandler(webapp2.RequestHandler): | ||
|
||
def __init__(self, request, response): | ||
self.context = { | ||
'static_url': config.static_url, | ||
'meta_title': config.meta_title, | ||
'meta_keywords': config.meta_keywords, | ||
'meta_description': config.meta_description, | ||
'ga_id': config.ga_id, | ||
'contact_api_key': config.contact_api_key, | ||
} | ||
self.root = PseudoParent.get_or_insert('root') | ||
self.jinja = jinja2.Environment( | ||
loader=jinja2.FileSystemLoader( | ||
'/'.join([ | ||
os.path.dirname(__file__).rstrip('/'), | ||
'templates' | ||
]) | ||
) | ||
) | ||
webapp2.RequestHandler.__init__(self, request, response) | ||
|
||
def render(self, tpl): | ||
self.response.write(self.jinja.get_template(tpl).render(self.context)) | ||
|
||
def user(self): | ||
return users.get_current_user() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# MAILGUN API URL | ||
# (e.g. 'https://api.mailgun.net/v2/mailbot.newmanapi.com/messages') | ||
mailgun_api_url = '' | ||
|
||
# MAILGUN API KEY (e.g. 'key-32hfd27d2hd287h2d2d8azd8jwkx7da9') | ||
mailgun_api_key = '' | ||
|
||
# MAILGUN SENDER NAME (e.g. 'Newman API') | ||
mailgun_sender_name = '' | ||
|
||
# MAILGUN SENDER EMAIL (e.g. 'noreply@mailbot.newmanapi.com') | ||
mailgun_sender_email = '' | ||
|
||
# EMAIL SUBJECT (e.g. 'Form Submission') | ||
email_subject = '' | ||
|
||
# HTML META INFORMATION | ||
meta_title = 'Newman API' | ||
meta_keywords = 'form, form mailing, contact form, static website' | ||
meta_description = 'Form mailing for static websites' | ||
|
||
# GOOGLE ANALYTICS ID (e.g. 'UA-11111111-1') | ||
ga_id = '' | ||
|
||
# CONTACT FORM API KEY (e.g. 'c2qrs3vztafh10nntxazfukbzr7ikzmw') | ||
contact_api_key = '' | ||
|
||
#MISC | ||
default_api_key_quota = 3 # Newman API keys per user | ||
static_url = '/static/' # location of static files (css, js, etc.) | ||
debug = False # debug mode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
indexes: | ||
|
||
# AUTOGENERATED | ||
|
||
# This index.yaml is automatically updated whenever the dev_appserver | ||
# detects that a new type of query is run. If you want to manage the | ||
# index.yaml file manually, remove the above marker line (the line | ||
# saying "# AUTOGENERATED"). If you want to manage some indexes | ||
# manually, move them above the marker line. The index.yaml file is | ||
# automatically uploaded to the admin console when you next deploy | ||
# your application using appcfg.py. | ||
|
||
- kind: ApiKey | ||
properties: | ||
- name: user_id | ||
- name: created | ||
direction: desc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import config | ||
import webapp2 | ||
import routes | ||
|
||
app = webapp2.WSGIApplication( | ||
routes.routes, | ||
debug=config.debug | ||
) | ||
|
||
if __name__ == '__main__': | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import webapp2 | ||
from base_handler import BaseHandler | ||
|
||
|
||
class Handler(BaseHandler): | ||
|
||
def get(self, page): | ||
self.context.update({ | ||
'active_page': page, | ||
'url': ( | ||
self.request.get('url') or | ||
webapp2.uri_for('index') | ||
) | ||
}) | ||
return self.render('.'.join([page, 'html'])) |
Oops, something went wrong.