|
@@ -4,27 +4,20 @@
|
|
|
|
|
|
|
|
|
import os
|
|
|
-from io import BytesIO
|
|
|
from threading import Thread
|
|
|
from typing import Union
|
|
|
from bottle import (
|
|
|
route, request, response,
|
|
|
- redirect, abort,
|
|
|
template, static_file,
|
|
|
- FormsDict, HTTPError,
|
|
|
+ FormsDict,
|
|
|
)
|
|
|
from psycopg import Cursor, connect
|
|
|
from psycopg.rows import TupleRow
|
|
|
-from linkpreview import link_preview
|
|
|
-
|
|
|
-from .validate import get_file_mimetype, get_file_size, get_filename, validate, validate_file, validate_parameter, validate_url
|
|
|
-from .hash_util import normalize_base32
|
|
|
from .route_decorators import normalize, poison, cursor
|
|
|
from .query_to_xml import get_categories, get_groups, get_products, get_tags
|
|
|
from .CachedLoadingPage import CachedLoadingPage
|
|
|
from .Cache import Cache
|
|
|
from . import trend as worker
|
|
|
-from .save import save, save_upload
|
|
|
|
|
|
host = f"host={os.getenv('HOST')}"
|
|
|
db = f"dbname={os.getenv('DB', 'grocery')}"
|
|
@@ -91,222 +84,3 @@ def products(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cach
|
|
|
def tags(cur: Cursor[TupleRow], key: Union[int, str], forms: FormsDict, cache: Cache):
|
|
|
response.content_type = 'application/xhtml+xml; charset=utf-8'
|
|
|
return get_tags(cur, forms)
|
|
|
-
|
|
|
-
|
|
|
-SCHEME = "https://"
|
|
|
-HOST = ""
|
|
|
-DOMAIN = "shandan.one"
|
|
|
-PORT = ""
|
|
|
-LOCATION = SCHEME + (f"{HOST}." if HOST else "") + DOMAIN + (f":{PORT}" if PORT else "")
|
|
|
-
|
|
|
-
|
|
|
-@route('/clip', method=['GET', 'POST'])
|
|
|
-def clip():
|
|
|
-
|
|
|
- if request.method == 'GET':
|
|
|
- _hash = request.params.hash
|
|
|
- if _hash:
|
|
|
- _hash = normalize_base32(_hash)
|
|
|
- content = validate(_hash).decode('utf-8')
|
|
|
- else:
|
|
|
- content = None
|
|
|
- link = f'{LOCATION}/clip/{_hash}' if content else f'{LOCATION}/clip'
|
|
|
-
|
|
|
- response.content_type = 'text/html; charset=utf-8'
|
|
|
- form = template(
|
|
|
- 'clip-form',
|
|
|
- action='/clip',
|
|
|
- method='post',
|
|
|
- content=content,
|
|
|
- disabled=True if content else False
|
|
|
- )
|
|
|
- return template(
|
|
|
- 'paste',
|
|
|
- form=form,
|
|
|
- qr=f'{LOCATION}/clip/{_hash}.qr' if content else f'{LOCATION}/grocery/static/clip-qr.svg',
|
|
|
- link=link,
|
|
|
- disabled=True if content else False,
|
|
|
- download=f'/clip/{_hash}' if content else None
|
|
|
- )
|
|
|
-
|
|
|
- if request.method == 'POST':
|
|
|
- content = validate_parameter(request, 'paste')
|
|
|
- if request.params.copy != 'true':
|
|
|
- _b32 = save(content, LOCATION)
|
|
|
- return redirect(f'/clip?hash={_b32}')
|
|
|
-
|
|
|
- response.content_type = 'text/html; charset=utf-8'
|
|
|
- form = template(
|
|
|
- 'clip-form',
|
|
|
- action='/clip',
|
|
|
- method='post',
|
|
|
- content=content,
|
|
|
- disabled=False
|
|
|
- )
|
|
|
- link = f'{LOCATION}/clip'
|
|
|
- return template(
|
|
|
- 'paste',
|
|
|
- form=form,
|
|
|
- qr=f'{LOCATION}/grocery/static/clip-qr.svg',
|
|
|
- link=link,
|
|
|
- disabled=False,
|
|
|
- download=None
|
|
|
- )
|
|
|
-
|
|
|
-
|
|
|
-@route('/clip/<filename:path>', method='GET')
|
|
|
-def get_clip(filename):
|
|
|
- ext = 'file'
|
|
|
- if filename and filename.endswith('.qr'):
|
|
|
- filename, ext = filename.split('.', 1)
|
|
|
-
|
|
|
- filename = filename and normalize_base32(filename)
|
|
|
- path = f'{filename}/{filename}.{ext}'
|
|
|
-
|
|
|
- if ext == 'qr':
|
|
|
- return static_file(path, root='app/rest/static', mimetype='image/svg+xml')
|
|
|
-
|
|
|
- filename = filename and normalize_base32(filename)
|
|
|
- if not request.params.raw.lower() == 'true':
|
|
|
-
|
|
|
- return redirect(f'/clip?hash={filename}')
|
|
|
-
|
|
|
- ret = validate(filename)
|
|
|
- if isinstance(ret, HTTPError):
|
|
|
- return ret
|
|
|
-
|
|
|
- return static_file(path, root='app/rest/static')
|
|
|
-
|
|
|
-
|
|
|
-@route('/upload', method=['GET', 'POST'])
|
|
|
-def upload():
|
|
|
-
|
|
|
- if request.method == 'GET':
|
|
|
- _hash = request.params.hash
|
|
|
- mimetype = None
|
|
|
- if _hash:
|
|
|
- _hash = normalize_base32(_hash)
|
|
|
- size = get_file_size(_hash)
|
|
|
- name = get_filename(_hash)
|
|
|
- mimetype = get_file_mimetype(name)
|
|
|
- if mimetype is not True and mimetype.startswith('text'):
|
|
|
- mimetype = None if size > CLIP_SIZE_LIMIT else mimetype
|
|
|
-
|
|
|
- link = f'{LOCATION}/upload/{_hash}' if _hash else f'{LOCATION}/upload'
|
|
|
- response.content_type = 'text/html; charset=utf-8'
|
|
|
- disabled = True if _hash else False
|
|
|
- form = template('file-form', action='/upload', method='post', disabled=disabled)
|
|
|
-
|
|
|
- return template(
|
|
|
- 'upload',
|
|
|
- form=form,
|
|
|
- qr=f'{LOCATION}/upload/{_hash}.qr' if _hash else f'{LOCATION}/grocery/static/upload-qr.svg',
|
|
|
- link=link,
|
|
|
- mimetype=mimetype,
|
|
|
- disabled=disabled
|
|
|
- )
|
|
|
-
|
|
|
- if request.method == 'POST':
|
|
|
- if 'paste' not in request.files:
|
|
|
- return abort(400, "Parameter 'paste' must be specified")
|
|
|
-
|
|
|
- upload = request.files['paste']
|
|
|
- if isinstance(upload.file, BytesIO):
|
|
|
- if len(upload.file.read()) == 0:
|
|
|
- return abort(400, "File is empty")
|
|
|
-
|
|
|
- _b32 = save_upload(upload.raw_filename, LOCATION, upload.file, root='app/rest/static')
|
|
|
- return redirect(f'/upload?hash={_b32}')
|
|
|
-
|
|
|
-
|
|
|
-@route('/upload/<filename:path>', method='GET')
|
|
|
-def get_upload(filename):
|
|
|
- ext = 'file'
|
|
|
- if filename and filename.endswith('.qr'):
|
|
|
- filename, ext = filename.split('.', 1)
|
|
|
-
|
|
|
- filename = filename and normalize_base32(filename)
|
|
|
- path = f'{filename}/{filename}.{ext}'
|
|
|
-
|
|
|
- if ext == 'qr':
|
|
|
- return static_file(path, root='app/rest/static', mimetype='image/svg+xml')
|
|
|
-
|
|
|
- download = True
|
|
|
- mimetype = True
|
|
|
- if request.params.download == "false":
|
|
|
- download = False
|
|
|
- mimetype = request.params.mimetype or None
|
|
|
-
|
|
|
- return validate_file(filename, download=download, mimetype=mimetype)
|
|
|
-
|
|
|
-
|
|
|
-@route('/goto', method=['GET', 'POST'])
|
|
|
-def goto():
|
|
|
-
|
|
|
- if request.method == 'GET':
|
|
|
- _hash = request.params.hash
|
|
|
- if _hash:
|
|
|
- _hash = normalize_base32(_hash)
|
|
|
- content = validate(_hash).decode('utf-8')
|
|
|
- else:
|
|
|
- content = None
|
|
|
-
|
|
|
- if content and request.params.go == 'true':
|
|
|
- target = validate_url(content)
|
|
|
- return redirect(target)
|
|
|
-
|
|
|
- link = f'{LOCATION}/goto/{_hash}' if content else f'{LOCATION}/goto'
|
|
|
- disabled = True if content else False
|
|
|
- response.content_type = 'text/html; charset=utf-8'
|
|
|
- form = template(
|
|
|
- 'goto-form',
|
|
|
- action='/goto',
|
|
|
- method='post',
|
|
|
- content=content,
|
|
|
- disabled=disabled
|
|
|
- )
|
|
|
- preview = dict()
|
|
|
- if content:
|
|
|
- try:
|
|
|
- page = link_preview(link, parser="lxml")
|
|
|
- preview['title'] = page.title
|
|
|
- preview['img'] = page.absolute_image
|
|
|
- preview['domain'] = page.site_name
|
|
|
- preview['link'] = content
|
|
|
- except:
|
|
|
- pass
|
|
|
-
|
|
|
- return template(
|
|
|
- 'goto',
|
|
|
- form=form,
|
|
|
- qr=f'{LOCATION}/goto/{_hash}.qr' if content else f'{LOCATION}/grocery/static/goto-qr.svg',
|
|
|
- link=link,
|
|
|
- disabled=disabled,
|
|
|
- preview=preview,
|
|
|
- )
|
|
|
-
|
|
|
- if request.method == 'POST':
|
|
|
- content = validate_parameter(request, 'url')
|
|
|
- _b32 = save(content, LOCATION)
|
|
|
-
|
|
|
-
|
|
|
- _ = validate_url(content.decode('utf-8'))
|
|
|
- return redirect(f'/goto?hash={_b32}')
|
|
|
-
|
|
|
-
|
|
|
-@route('/goto/<filename:path>', method='GET')
|
|
|
-def redirect_goto(filename):
|
|
|
- ext = 'file'
|
|
|
- if filename and filename.endswith('.qr'):
|
|
|
- filename, ext = filename.split('.', 1)
|
|
|
-
|
|
|
- filename = filename and normalize_base32(filename)
|
|
|
- path = f'{filename}/{filename}.{ext}'
|
|
|
-
|
|
|
- if ext == 'qr':
|
|
|
- return static_file(path, root='app/rest/static', mimetype='image/svg+xml')
|
|
|
-
|
|
|
- return redirect(f'/goto?hash={filename}&go=true')
|
|
|
-
|
|
|
-@route('/<any>/', method='GET')
|
|
|
-def redirect_trailing_slash(any): return redirect(f'/{any}')
|