فهرست منبع

get barcode preview

Daniel Sheffield 4 ماه پیش
والد
کامیت
9d9c43e8fc

+ 81 - 43
rest/pyapi.py

@@ -3,14 +3,11 @@
 # All rights reserved
 #
 # THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
-from io import BytesIO
+
 import time
 from bottle import (
-    Bottle,
-    default_app,
     route, request, response,
-    redirect, abort,
-    template, static_file,
+    redirect, abort, static_file,
     HTTPResponse,
 )
 from itertools import chain
@@ -19,9 +16,10 @@ from linkpreview import link_preview
 
 from .hash_util import B32REGEX, normalize_base32, blake, bytes_to_base32
 from .qr import get_qr_code
+from .bar import get_bar_code
 from json import dumps, load
 
-from sqlite3 import connect, Row
+from sqlite3 import connect
 
 SCHEME = "https://"
 HOST = ""
@@ -29,9 +27,29 @@ DOMAIN = "shandan.one"
 PORT = ""
 LOCATION = SCHEME + (f"{HOST}." if HOST else "") + DOMAIN + (f":{PORT}" if PORT else "")
 
-@route('/preview', method=['GET'])
-def get_preview():
+def parse_data_uri(content):
+    # extract bytes from data url
+    _, *media, data = chain.from_iterable(map(
+        lambda x: x.split(',', 1), content.split(':', 1)
+    ))
+    media = media and media[0]
+    mimetype, *params, encoding = media.split(';')
+    if '=' in encoding:
+        params.append(encoding)
+        encoding = None
+    
+    return {
+        'mimetype': mimetype,
+        'params': dict(map(lambda x: x.split('='), params)),
+        'encoding': None,
+        'data': data,
+    }
+
+
+@route('/goto/preview', method=['GET'])
+def get_goto_preview():
     link = request.params.link
+    response.content_type = 'application/json'
     if not link:
         return dumps(None)
     try:
@@ -45,16 +63,32 @@ def get_preview():
     except:
         return dumps(None)
 
-@route('/hash', method=['POST'])
+@route('/code/preview', method=['GET'])
+def get_code_preview():
+    body = request.body
+    response.content_type = 'image/svg+xml'
+    if not body:
+        abort(404, 'Empty request')
+
+    return get_bar_code(body).decode('utf-8')
+
+
+@route('/<route:re:(clip|goto|upload|code)>/hash', method=['POST'])
 def get_hash():
-    data = dict(map(
-        lambda x: (x[0], x[1].encode('utf-8')),
-        load(request.body).items()
-    ))
-    _bytes = blake(**data)
+    body = load(request.body)
+    data = body['data']
+    person = body.get('person', None)
+    if route == 'upload':
+        data = parse_data_uri(data)
+        assert data['encoding'] == 'base64', f"unsupported encoding: {data['encoding']}"
+        data = b64decode(data['data'] + '==')
+    else:
+        data = data.encode('utf-8')
+
+    _bytes = blake(data, person = person and person.encode('utf-8'))
     return bytes_to_base32(_bytes)
 
-@route('/normalize', method=['GET'])
+@route('/<route:re:(clip|goto|upload|code)>/normalize', method=['GET'])
 def normalize():
     _hash = request.params.hash
     response.content_type = 'application/json'
@@ -63,10 +97,20 @@ def normalize():
         'o': normalize_base32(_hash) if _hash else None,
     })
 
-@route('/qr', method=['POST'])
-def get_qr():
-    data = load(request.body)
-    return get_qr_code(**data).decode('utf-8')
+@route('/<route:re:(clip|goto|upload)>/qr', method=['POST'])
+def get_qr(route):
+    body = load(request.body)
+    data = body['data']
+    fallback = body.get('fallback', None)
+    if route == 'upload':
+        data = parse_data_uri(data)
+        assert data['encoding'] == 'base64', f"unsupported encoding: {data['encoding']}"
+        data = b64decode(data['data'] + '==')
+    else:
+        data = data.encode('utf-8')
+
+    response.content_type = 'image/svg+xml'
+    return get_qr_code(data, fallback = fallback).decode('utf-8')
 
 @route('/static/<filename:path>')
 def send_static(filename):
@@ -76,22 +120,24 @@ def send_static(filename):
 def _get_clip(route):
     return redirect(f'/{route}/open.sql')
 
-@route('/<route:re:(clip|goto|upload)>', method=['GET', 'POST'])
+@route('/<route:re:(clip|goto|upload|code)>', method=['GET', 'POST'])
 def clip(route):
     return redirect(f'/{route}.sql')
 
-@route(f'/<route:re:(clip|goto)>/<hash:re:{B32REGEX}{{1,5}}>', method='GET')
+@route(f'/<route:re:(clip|goto|code)>/<hash:re:{B32REGEX}{{1,5}}>', method='GET')
 def get_clip(route, hash):
+    hash = hash and normalize_base32(hash)
     return redirect(f'/{route}.sql?hash={hash}&go=true')
 
+
 @route(f'/upload/<hash:re:{B32REGEX}{{1,5}}>', method='GET')
 def get_upload(hash):
     hash = hash and normalize_base32(hash)
     con = connect('util.db')
     fname, mimetype, content = (None, None, None)
     try:
-        fname, mimetype, content = con.cursor().execute(f"""
-SELECT name, mime, content
+        fname, mimetype, content, created = con.cursor().execute(f"""
+SELECT name, mime, content, created
 FROM upload
 WHERE hash = '{hash}'
 LIMIT 1;
@@ -99,33 +145,25 @@ LIMIT 1;
     finally:
         con.close()
 
-    # extract bytes from data url
-    _, mime, encoding, content = chain.from_iterable(map(
-        lambda x: x.split(','), chain.from_iterable(map(
-            lambda x: x.split(';'), content.split(':')
-        ))
-    ))
-    assert encoding == 'base64', f'unsupported encoding: {encoding}'
-    content = b64decode(content + '==')
+    data = parse_data_uri(content)
+    assert data['mimetype'].split(';', 1)[0] == mimetype.split(';', 1)[0], f"mimetype in db and data uri differ"
+    charset = data['params'].get('charset', None)
+    assert data['encoding'] == 'base64', f"unsupported encoding: {data['encoding']}"
+    content = b64decode(data['data'] + '==')
 
-    if request.params.download == "false":
-        download = False
     headers = {}
     headers['Content-Length'] = len(content)
 
     # TODO: create ext from mime type?
     headers['Content-Disposition'] = 'attachment; filename="%s"' % (fname or hash)
     headers['Content-Encoding'] = 'application/octet-stream'
-    # if mimetype == 'auto':
-    #     mimetype, encoding = mimetypes.guess_type(filename)
-    #     if encoding: headers['Content-Encoding'] = encoding
-
-    # if mimetype:
-    #     if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
-    #         mimetype += '; charset=%s' % charset
-    #     headers['Content-Type'] = mimetype
-    #lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(created))
-    #headers['Last-Modified'] = lm
+
+    if mimetype:
+        if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
+            mimetype += '; charset=%s' % charset
+        headers['Content-Type'] = mimetype
+    lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(created))
+    headers['Last-Modified'] = lm
     return HTTPResponse(content, **headers)
 
 @route('/<any>/', method='GET')

+ 1 - 0
rest/requirements.txt

@@ -4,3 +4,4 @@ base32-lib
 lxml
 linkpreview
 qrcode
+python-barcode

+ 2 - 2
util-sqlpage/clip/form.sql

@@ -9,12 +9,12 @@ SELECT 'Open' AS title
 SELECT 'New' AS title
 , 1 AS width
 , 'gray-500' AS color
-, 'https://shandan.one/clip.sql?' AS link
+, '/clip.sql?' AS link
 ;
 SELECT 'Download' AS title
 , 2 AS width
 , 'gray-500' AS color
-, 'https://shandan.one/clip/download.sql?hash='||$hash AS link
+, '/clip/download.sql?hash='||$hash AS link
 ;
 
 SELECT 'form' AS component

+ 2 - 2
util-sqlpage/clip/save.sql

@@ -1,6 +1,6 @@
 SET request = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/hash',
+    'url', 'https://shandan.one/clip/hash',
     'headers', json_object(),
     'body', json_object(
         'data', $content,
@@ -11,7 +11,7 @@ SET hash = sqlpage.fetch($request);
 SET fallback = 'https://shandan.one/clip/' || sqlpage.url_encode($hash);
 SET request = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/qr',
+    'url', 'https://shandan.one/clip/qr',
     'headers', json_object(),
     'body', json_object(
         'data', $content,

+ 1 - 0
util-sqlpage/code/form-fuel.sql

@@ -8,6 +8,7 @@ SET ":used" = COALESCE(:used, (SELECT used FROM code_detail WHERE hash = $hash))
 SET ":validate" = 'Update';
 SET "$action" = 'Update';
 SET ":method" = 'post';
+SET ":autofill" = TRUE;
 SET ":filter_config" = '[
   {"name": "store", "required": true},
   {"name": "value", "required": true},

+ 1 - 1
util-sqlpage/code/form.sql

@@ -4,7 +4,7 @@ SET ":filter_options" = (
   SELECT json_group_array(json_object('name', q.k, 'options', q.o))
   FROM (
     SELECT options.k, jsonb_group_array(
-      jsonb_object('label', v, 'value', v, 'selected', c == 1)
+      jsonb_object('label', v, 'value', v, 'selected', CASE $autofill WHEN TRUE THEN c == 1 ELSE FALSE END)
       ORDER BY v) o
     FROM (
       SELECT DISTINCT 'store' k, store v, count(store) OVER () c FROM code_detail

+ 2 - 1
util-sqlpage/code/recent.sql

@@ -15,11 +15,12 @@ SET ":filter_config" = '[
 SET ":validate" = 'Apply';
 SET ":action" = 'Apply';
 SET ":method" = 'get';
+SET ":autofill" = FALSE;
 SELECT 'dynamic' AS component, sqlpage.run_sql('code/form.sql') AS properties;
 
 SELECT 'list' AS component;
 SELECT COALESCE(type||' ','') || COALESCE(store||' ', '') || COALESCE(expiry, created) AS title
-, content AS description
+, COALESCE(content->>'content'||' ', '') || COALESCE(content->>'type', '') AS description
 , '/code.sql?hash='||c.hash AS link
 FROM code c
 LEFT JOIN code_detail cd

+ 3 - 7
util-sqlpage/code/save.sql

@@ -1,6 +1,6 @@
 SET ":request" = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/hash',
+    'url', 'https://shandan.one/code/hash',
     'headers', json_object(),
     'body', json_object(
         'data', $content,
@@ -8,15 +8,11 @@ SET ":request" = json_object(
     )
 );
 SET ":hash" = sqlpage.fetch($request);
-SET ":fallback" = 'https://shandan.one/upload/' || sqlpage.url_encode($hash);
 SET ":request" = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/qr',
+    'url', 'https://shandan.one/code/preview',
     'headers', json_object(),
-    'body', json_object(
-        'data', $content,
-        'fallback', $fallback
-    )
+    'body', $content
 );
 SET ":qr" = sqlpage.fetch($request);
 SELECT 'dynamic' AS component, sqlpage.run_sql('code/new.sql') AS properties;

+ 1 - 1
util-sqlpage/goto/preview.sql

@@ -1,5 +1,5 @@
 SET content = COALESCE($content, '');
-SET url = 'https://shandan.one/preview?link=' || sqlpage.url_encode($content);
+SET url = 'https://shandan.one/goto/preview?link=' || sqlpage.url_encode($content);
 SET api_results = sqlpage.fetch($url);
 SET title = $api_results->>'title';
 SET image = $api_results->>'img';

+ 2 - 2
util-sqlpage/goto/save.sql

@@ -1,6 +1,6 @@
 SET request = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/hash',
+    'url', 'https://shandan.one/goto/hash',
     'headers', json_object(),
     'body', json_object(
         'data', $content,
@@ -11,7 +11,7 @@ SET hash = sqlpage.fetch($request);
 SET fallback = 'https://shandan.one/goto/' || sqlpage.url_encode($hash);
 SET request = json_object(
     'method', 'POST',
-    'url', 'https://shandan.one/qr',
+    'url', 'https://shandan.one/goto/qr',
     'headers', json_object(),
     'body', json_object(
         'data', $content,

+ 1 - 1
util-sqlpage/sqlpage/link.sql

@@ -1,4 +1,4 @@
-SET url = 'https://shandan.one/normalize?hash=' || sqlpage.url_encode($hash);
+SET url = 'https://shandan.one/'||$tool||'/normalize?hash=' || sqlpage.url_encode($hash);
 SET api_results = sqlpage.fetch($url);
 SET hash = $api_results->>'o';
 SET link = COALESCE('https://shandan.one/'||$tool||'/'||$hash, NULL);