commit 6cc6650e15f3a3fbf4cf6810aaaf2fce23b9660a Author: dave Date: Mon Jan 28 22:23:35 2019 -0800 initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c3aea08 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +/testenv +__pycache__ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3aea08 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/testenv +__pycache__ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..adb390e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM ubuntu:bionic + +ADD . /tmp/code/ + +RUN apt-get update && \ + apt-get install -y python3-pip + +RUN pip3 install -U pip && \ + cd /tmp/code && \ + python3 setup.py install && \ + useradd --uid 1000 app + +VOLUME /data/ + +ENTRYPOINT ["wastebind", "-d", "/data/"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f32e909 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +backports.functools-lru-cache==1.5 +cheroot==6.5.4 +CherryPy==18.1.0 +jaraco.functools==2.0 +more-itertools==5.0.0 +portend==2.3 +pytz==2018.9 +six==1.12.0 +tempora==1.14 +zc.lockfile==1.4 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d03c204 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +from setuptools import setup +import os + + +__version__ = "0.0.0" +with open(os.path.join(os.path.dirname(__file__), "requirements.txt")) as f: + __requirements__ = [line.strip() for line in f.readlines()] + + +setup(name='wastebin', + version=__version__, + description='lazy pastebin', + url='', + author='dpedu', + author_email='dave@davepedu.com', + packages=['wastebin'], + install_requires=__requirements__, + entry_points={ + "console_scripts": [ + "wastebind = wastebin.daemon:main" + ] + }, + zip_safe=False) diff --git a/wastebin/__init__.py b/wastebin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wastebin/daemon.py b/wastebin/daemon.py new file mode 100644 index 0000000..42e6936 --- /dev/null +++ b/wastebin/daemon.py @@ -0,0 +1,123 @@ +import os +import cherrypy +import logging +import hashlib +import re + + +PAGE = """ + + + + Wastebin + + +
+
+
+ +
+ + +""" + + +RE_NAME = re.compile(r'^[a-z0-9_\-/]+$') + + +def sha256(data): + h = hashlib.sha256() + h.update(data.encode("utf-8")) + return h.hexdigest() + + +class WasteWeb(object): + def __init__(self, datadir): + self.datadir = datadir + + @cherrypy.expose + def index(self, load=None): + data = "" + if load: + assert RE_NAME.match(load) + data = self.loadpaste(load) + yield PAGE.format(data=data.replace("<", "<"), load=load or "") + + @cherrypy.expose + def make(self, name, contents): + assert RE_NAME.match(name) + pname = name or sha256(contents) + self.writepaste(pname, contents) + raise cherrypy.HTTPRedirect("/" + pname) + + @cherrypy.expose + def default(self, *args): + data = self.loadpaste(args[0]) + yield data + + def loadpaste(self, name): + path = self.pastepath(sha256(name)) + with open(path) as f: + f.readline() # the name + return f.read() + + def writepaste(self, name, contents): + hname = sha256(name) + path = self.pastepath(hname) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w") as f: + f.write(name) + f.write("\n") + f.write(contents) + + def pastepath(self, hashedname): + return os.path.join(self.datadir, hashedname[0], hashedname[1], hashedname + ".txt") + + +def main(): + import argparse + import signal + + parser = argparse.ArgumentParser(description="") + + parser.add_argument('-p', '--port', default=8080, type=int, help="http port") + parser.add_argument('-d', '--data', default="./", help="data dir") + parser.add_argument('--debug', action="store_true", help="enable development options") + + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO if args.debug else logging.WARNING, + format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s") + + web = WasteWeb(args.data) + + cherrypy.tree.mount(web, '/', {'/': {'tools.trailing_slash.on': False}}) + + cherrypy.config.update({ + 'tools.sessions.on': False, + 'request.show_tracebacks': True, + 'server.socket_port': args.port, + 'server.thread_pool': 5, + 'server.socket_host': '0.0.0.0', + 'server.show_tracebacks': args.debug, + 'log.screen': False, + 'engine.autoreload.on': args.debug + }) + + def signal_handler(signum, stack): + logging.critical('Got sig {}, exiting...'.format(signum)) + cherrypy.engine.exit() + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + try: + cherrypy.engine.start() + cherrypy.engine.block() + finally: + logging.info("API has shut down") + cherrypy.engine.exit() + + +if __name__ == '__main__': + main()