initial commit
This commit is contained in:
commit
6cc6650e15
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
||||
/testenv
|
||||
__pycache__
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/testenv
|
||||
__pycache__
|
15
Dockerfile
Normal file
15
Dockerfile
Normal file
@ -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/"]
|
10
requirements.txt
Normal file
10
requirements.txt
Normal file
@ -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
|
25
setup.py
Normal file
25
setup.py
Normal file
@ -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)
|
0
wastebin/__init__.py
Normal file
0
wastebin/__init__.py
Normal file
123
wastebin/daemon.py
Normal file
123
wastebin/daemon.py
Normal file
@ -0,0 +1,123 @@
|
||||
import os
|
||||
import cherrypy
|
||||
import logging
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
|
||||
PAGE = """<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Wastebin</title>
|
||||
</head>
|
||||
<body>
|
||||
<form action="/make" method="post">
|
||||
<textarea name="contents" rows="30" cols="120">{data}</textarea><br />
|
||||
<input type="text" name="name" placeholder="url name" value="{load}" /><br />
|
||||
<input type="submit" value="Go">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
|
||||
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()
|
Loading…
Reference in New Issue
Block a user