public
Last active

  • Download Gist
app.yaml
YAML
1 2 3 4 5 6 7 8
application: zip-site
version: main
runtime: python
api_version: 1
 
handlers:
- url: /.*
script: main.py
base.html
HTML
1 2 3 4 5 6 7 8
<html>
<head>
<title>{% block title %}Zip-site hosting{% endblock %}</title>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
index.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
{% extends "base.html" %}
{% block body %}
<h1>Zip-site hosting</h1>
<p>This is a demo app for BlobReader and other features. See the article
<a href="http://blog.notdot.net/2010/08/Using-BlobReader-wildcard-subdomains-and-webapp2">here</a>,
and get the complete source <a href="http://gist.github.com/509659">here</a>.</p>
{% if sites %}
<h2>Your sites</h2>
<table>
<tr><th>Site Name</th><th>Last Updated</th><th>Actions</th></tr>
{% for site in sites %}
<tr>
<th><a href="http://{{site.url}}/">{{site.key.name|escape}}</a></th>
<td>{{site.last_updated}}</td>
<td><a href="/upload?site={{site.key.name|escape}}">Upload</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
<h2><a href="{{upload_url}}">Upload a new site</a></h2>
{% endblock %}
main.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
from google.appengine.api import users
from google.appengine.ext import blobstore
from google.appengine.ext import db
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
 
import logging
import mimetypes
import os
import re
import zipfile
 
import webapp2
 
 
BASE_DOMAIN = "%s.appspot.com" % os.environ['APPLICATION_ID']
 
 
class Site(db.Model):
owner = db.UserProperty(required=True)
last_updated = db.DateTimeProperty(required=True, auto_now=True)
zipfile = blobstore.BlobReferenceProperty(required=True)
 
@property
def url(self):
return '%s.%s' % (self.key().name(), BASE_DOMAIN)
 
 
class BaseHandler(webapp2.RequestHandler):
def __call__(self, *args, **kwargs):
self.user = users.get_current_user()
if not self.user:
self.redirect(users.create_login_url(self.request.url))
else:
return super(BaseHandler, self).__call__(*args, **kwargs)
 
def render_template(self, file, template_vars):
path = os.path.join(os.path.dirname(__file__), 'templates', file)
self.response.out.write(template.render(path, template_vars))
 
 
class MainHandler(BaseHandler):
def get(self):
sites = Site.all().filter('owner =', self.user).fetch(20)
self.render_template('index.html', {
'sites': sites,
'upload_url': self.url_for('upload'),
})
 
 
class UploadHandler(BaseHandler):
def get(self):
self.render_template('upload.html', {
'upload_url': blobstore.create_upload_url(self.url_for('upload')),
'site_name': self.request.GET.get('site', None),
})
 
def post(self):
site = self.request.POST['site']
blob_key = blobstore.parse_blob_info(self.request.POST['file'])
db.run_in_transaction(self.upload_tx, site, blob_key)
self.redirect_to('main')
 
def upload_tx(self, site_name, blob_key):
site = Site.get_by_key_name(site_name)
if site:
if site.owner != self.user: return
site.zipfile.delete()
site.zipfile = blob_key
else:
site = Site(key_name=site_name, owner=self.user, zipfile=blob_key)
db.put(site)
 
 
INDEX_FILES = ['index.html', 'index.htm']
SUBDOMAIN_RE = re.compile("^([^.]+)\.%s\.appspot\.com$"
% os.environ['APPLICATION_ID'])
 
 
class SiteHandler(webapp2.RequestHandler):
def get(self, path):
site_name = SUBDOMAIN_RE.search(self.request.host).group(1)
site = Site.get_by_key_name(site_name)
if not site:
self.abort(404)
zip_key = Site.zipfile.get_value_for_datastore(site)
site_zip = zipfile.ZipFile(blobstore.BlobReader(zip_key))
 
path, data = self.get_contents(site_zip, path)
self.response.headers['Content-Type'] = mimetypes.guess_type(path)[0]
self.response.out.write(data)
 
def get_contents(self, site_zip, path):
if path.endswith('/'):
for idx in INDEX_FILES:
newpath = os.path.join(path, idx)[1:]
try:
data = site_zip.read(newpath)
return newpath, data
except KeyError:
pass
self.abort(404)
else:
try:
return path, site_zip.read(path[1:])
except KeyError:
self.abort(404)
 
 
main_app = webapp2.WSGIApplication([
webapp2.Route(r'/', MainHandler, name='main'),
webapp2.Route(r'/upload', UploadHandler, name='upload'),
])
 
 
site_app = webapp2.WSGIApplication([
webapp2.Route(r'<path:.*>', SiteHandler),
])
 
 
def domain_middleware(domain_map):
domain_map = [(re.compile('^%s$' % x) if isinstance(x, basestring) else x, y)
for x, y in domain_map]
def middleware(environ, start_response):
domain = environ['SERVER_NAME']
for regex, app in domain_map:
if regex.match(domain):
return app(environ, start_response)
return middleware
 
 
app = domain_middleware([
(SUBDOMAIN_RE, site_app),
('.*', main_app),
])
 
 
def main():
run_wsgi_app(app)
 
 
if __name__ == '__main__':
main()
upload.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
{% extends "base.html" %}
{% block body %}
<h1>Zip-site hosting</h1>
<h2>Upload zipped site</h2>
<form action="{{upload_url}}" method="POST" enctype="multipart/form-data">
<table>
<tr>
<th>Site name:</th>
<td>{% if site_name %}
{{site_name|escape}}
<input type="hidden" name="site" value="{{site_name|escape}}" />
{% else %}
<input type="text" name="site" />
{% endif %}</td>
</tr>
<tr>
<th>Zip file</th>
<td><input type="file" name="file" /></td>
</tr>
</table>
<input type="submit" value="Upload" />
</form>
{% endblock %}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.