6 Commits bc6df424ca ... e731eefd80

Author SHA1 Message Date
  Pi e731eefd80 revert accidental change 3 weeks ago
  Pi bd9798f4ef no need to publish ports 3 weeks ago
  Pi c83bf60700 migrate to podman 3 weeks ago
  Pi a2062da91c remove auth service 4 weeks ago
  Pi c53e1adf40 remove util files 1 month ago
  Pi 6ac2b92a45 move into util repo 1 month ago
62 changed files with 37 additions and 1590 deletions
  1. 1 1
      Dockerfile
  2. 0 7
      Dockerfile-auth
  3. 31 58
      docker-compose.yml
  4. 0 68
      forward_auth.py
  5. 1 0
      pg/license/ESV.txt
  6. 1 0
      pg/license/KJV.txt
  7. 1 0
      pg/license/MKJV.txt
  8. 1 0
      pg/license/UKJV.txt
  9. 1 0
      pg/license/WEB.txt
  10. 0 3
      requirements-auth.txt
  11. 0 0
      test/__init__.py
  12. 0 112
      test/rest/rfc2396.py
  13. 0 308
      test/rest/test_hash_util.py
  14. 0 72
      test/rest/test_url.py
  15. 0 8
      util-sqlpage/clip/Index.sql
  16. 0 47
      util-sqlpage/clip/form.sql
  17. 0 28
      util-sqlpage/clip/index.sql
  18. 0 8
      util-sqlpage/clip/save.sql
  19. 0 11
      util-sqlpage/code/Index.sql
  20. 0 67
      util-sqlpage/code/form-fuel.sql
  21. 0 59
      util-sqlpage/code/form.sql
  22. 0 16
      util-sqlpage/code/index.sql
  23. 0 24
      util-sqlpage/code/json/filters.json
  24. 0 9
      util-sqlpage/code/new.sql
  25. 0 52
      util-sqlpage/code/recent.sql
  26. 0 12
      util-sqlpage/code/save.sql
  27. 0 12
      util-sqlpage/code/update.sql
  28. 0 8
      util-sqlpage/goto/Index.sql
  29. 0 36
      util-sqlpage/goto/form.sql
  30. 0 32
      util-sqlpage/goto/index.sql
  31. 0 12
      util-sqlpage/goto/preview.sql
  32. 0 11
      util-sqlpage/goto/redirect.sql
  33. 0 9
      util-sqlpage/goto/save.sql
  34. 0 22
      util-sqlpage/sqlpage/Link.sql
  35. 0 46
      util-sqlpage/sqlpage/Open.sql
  36. 0 11
      util-sqlpage/sqlpage/QR.sql
  37. 0 50
      util-sqlpage/sqlpage/Style.sql
  38. 0 5
      util-sqlpage/sqlpage/alert.sql
  39. 0 5
      util-sqlpage/sqlpage/link.sql
  40. 0 7
      util-sqlpage/sqlpage/migrations/000_clip.sql
  41. 0 7
      util-sqlpage/sqlpage/migrations/001_goto.sql
  42. 0 7
      util-sqlpage/sqlpage/migrations/003_upload.sql
  43. 0 8
      util-sqlpage/sqlpage/migrations/004_upload.sql
  44. 0 9
      util-sqlpage/sqlpage/migrations/005_upload.sql
  45. 0 7
      util-sqlpage/sqlpage/migrations/006_code.sql
  46. 0 9
      util-sqlpage/sqlpage/migrations/007_code_detail.sql
  47. 0 6
      util-sqlpage/sqlpage/migrations/008_upload_temp.sql
  48. 0 1
      util-sqlpage/sqlpage/migrations/009_goto.sql
  49. 0 17
      util-sqlpage/sqlpage/save.sql
  50. 0 6
      util-sqlpage/sqlpage/sqlpage.json
  51. 0 6
      util-sqlpage/sqlpage/templates/loader-start.handlebars
  52. 0 3
      util-sqlpage/sqlpage/templates/loader-stop.handlebars
  53. 0 42
      util-sqlpage/sqlpage/templates/progress.handlebars
  54. 0 2
      util-sqlpage/sqlpage/templates/spinner-start.handlebars
  55. 0 2
      util-sqlpage/sqlpage/templates/spinner-stop.handlebars
  56. 0 46
      util-sqlpage/sqlpage/theme.sql
  57. 0 45
      util-sqlpage/sqlpage/validate.sql
  58. 0 8
      util-sqlpage/upload/Index.sql
  59. 0 57
      util-sqlpage/upload/form.sql
  60. 0 43
      util-sqlpage/upload/index.sql
  61. 0 20
      util-sqlpage/upload/save.sql
  62. 0 3
      util-sqlpage/upload/temp.sql

+ 1 - 1
Dockerfile

@@ -1,5 +1,5 @@
 FROM nginx:alpine
 COPY nginx.conf /etc/nginx/nginx.conf
-COPY prayer-generator/license /usr/share/nginx/html/license
+COPY pg/license /usr/share/nginx/html/license
 COPY css /usr/share/nginx/html/css
 COPY robots.txt /usr/share/nginx/html

+ 0 - 7
Dockerfile-auth

@@ -1,7 +0,0 @@
-FROM python:3-slim
-WORKDIR /usr/src/app
-COPY requirements-auth.txt ./
-RUN python3 -m pip install --upgrade pip && \
-    python3 -m pip install --no-cache-dir -r requirements-auth.txt
-COPY forward_auth.py ./
-CMD [ "python", "forward_auth.py" ]

+ 31 - 58
docker-compose.yml

@@ -1,89 +1,62 @@
+---
+x-podman:
+  in_pod: false
+
 services:
-  activities:
-    image: lovasoa/sqlpage:latest
-    hostname: activities
+  activity:
+    image: docker.io/lovasoa/sqlpage:latest
+    hostname: home-activity
+    user: "1000:1000"
+    userns_mode: "keep-id:uid=1000,gid=1000"
+    restart: unless-stopped
     volumes:
       - /etc/timezone:/etc/timezone:ro
       - /etc/localtime:/etc/localtime:ro
       - ./activities/www/:/var/www/
       - ./activities/sqlpage:/etc/sqlpage/
       - ./activities/activities.db:/var/www/activities.db
+    expose:
+      - 8080
     networks:
-      - traefik
-    restart: always
+      - priv
 
-  util-sqlpage:
-    image: lovasoa/sqlpage:latest
-    hostname: util-sqlpage
+  landing:
+    image: docker.io/lovasoa/sqlpage:latest
+    hostname: home-landing
+    user: "1000:1000"
+    userns_mode: "keep-id:uid=1000,gid=1000"
+    restart: unless-stopped
     volumes:
       - /etc/timezone:/etc/timezone:ro
       - /etc/localtime:/etc/localtime:ro
-      - ./util-sqlpage/:/var/www/
-      - ./util-sqlpage/sqlpage:/etc/sqlpage:ro
-    networks:
-      - traefik
-    restart: always
-
-  util-pyapi:
-    image: util-pyapi
-    hostname: util-pyapi
-    build:
-      context: .
-      dockerfile: rest/Dockerfile
-    volumes:
-      - ./util-sqlpage/util.db:/usr/src/app/util.db:ro
-    expose:
-      - 6772
-    networks:
-      - traefik
-    restart: always
-
-  home-sqlpage:
-    image: lovasoa/sqlpage:latest
-    hostname: home-sqlpage
-    volumes:
       - ./home-sqlpage/:/var/www/
       - ./home-sqlpage.json:/etc/sqlpage/sqlpage.json
       - ./home-sqlpage/sqlpage/migrations:/etc/sqlpage/migrations
     expose:
       - 8080
     networks:
-      - traefik
-    restart: always
+      - priv
 
-  home:
+  proxy:
     image: home-launcher
-    hostname: home-launcher
+    hostname: home-proxy
+    #user: "1000:1000"
+    #userns_mode: "keep-id:uid=1000,gid=1000"
+    restart: unless-stopped
     build: .
     expose:
       - 80
-    restart: always
     volumes:
       - /home/pi/www/feed:/usr/share/nginx/html/feed
       - /home/cropswap-archive/www:/usr/share/nginx/html/mail-archive
       - ./media:/usr/share/nginx/html/media
-      - ./fdroid:/usr/share/nginx/html/fdroid
-      - /home/pi/syncthing/wyn-apps:/usr/share/nginx/html/fdroid/repo
       - ./.well-known:/usr/share/nginx/html/.well-known
     networks:
-      - traefik
-
-  auth:
-    image: auth
-    hostname: auth
-    build:
-      context: .
-      dockerfile: Dockerfile-auth
-    volumes:
-      - /etc/private-ca:/etc/private-ca:ro
-      - ./hashes.py:/usr/src/app/hashes.py
-    expose:
-      - 1234
-    networks:
-      - traefik
-    restart: always
+      - priv
 
 networks:
-  traefik:
-    external: true
-
+  priv:
+    enable_ipv6: true
+    ipam:
+      config:
+       - subnet: fd00:0008::/64

+ 0 - 68
forward_auth.py

@@ -1,68 +0,0 @@
-import cherrypy
-from bottle import (
-  default_app,
-  auth_basic,
-  HTTPError,
-  HTTPResponse,
-  route,
-  request,
-)
-from passlib.hash import bcrypt
-from hashes import users
-
-def basic_auth(user, password, realm=None):
-  return bcrypt.verify(password, users[realm][user])
-
-def verify_cert(pem):
-    return True
-
-def auth_client_cert(check):
-    def decorator(func):
-        def wrapper(*a, **ka):
-            if 'X-Forwarded-Tls-Client-Cert' in request.headers:
-                cert = request.headers['X-Forwarded-Tls-Client-Cert']
-            else:
-                cert = None
-            if cert and verify_cert(cert):
-                return HTTPResponse(status=200)
-            return func(*a, **ka)
-        return wrapper
-    return decorator
-
-def auth_basic(check, text="Access denied"):
-    ''' Callback decorator to require HTTP auth (basic).
-        TODO: Add route(check_auth=...) parameter. '''
-    def decorator(func):
-        def wrapper(*a, **ka):
-            realm = '.'.join(request.headers['X-Forwarded-Host'].split('.')[:-2])
-            user, password = request.auth or (None, None)
-            if realm in users and user in users[realm]:
-                if user is None or not check(user, password, realm=realm):
-                    err = HTTPError(401, text)
-                    err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
-                    return err
-            else:
-                err = HTTPError(401, f"User not found {realm}\{user}")
-                err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
-                return err
-            return func(*a, **ka)
-        return wrapper
-    return decorator
-
-@route('/authenticate')
-@auth_client_cert(verify_cert)
-@auth_basic(basic_auth)
-def auth():
-    return HTTPResponse(status=200)
-
-cherrypy.server.ssl_certificate = '/etc/private-ca/server-cert.pem'
-cherrypy.server.ssl_private_key = '/etc/private-ca/server-key.pem'
-
-cherrypy.config.update({
-    'server.socket_host': "0.0.0.0",
-    'server.socket_port': 1234,
-})
-
-cherrypy.tree.graft(default_app(), "/")
-cherrypy.engine.start()
-cherrypy.engine.block()

+ 1 - 0
pg/license/ESV.txt

@@ -0,0 +1 @@
+Scripture quotations marked ESV are from The Holy Bible, English Standard Version, copyright © 2001 by Crossway Bibles, a division of Good News Publishers. Used by permission. All rights reserved.

+ 1 - 0
pg/license/KJV.txt

@@ -0,0 +1 @@
+General public license for distribution for any purpose. Rights in The Authorized Version of the Bible (King James Bible) in the United Kingdom are vested in the Crown and administered by the Crown’s patentee, Cambridge University Press.

+ 1 - 0
pg/license/MKJV.txt

@@ -0,0 +1 @@
+Copyright (c) Jay P. Green, Sr. 1993. Licensed for non-commercial distribution granted to CrossWire on Oct. 3, 2002

+ 1 - 0
pg/license/UKJV.txt

@@ -0,0 +1 @@
+Freely distributable. Public Domain. I love Jesus (UKJV).

+ 1 - 0
pg/license/WEB.txt

@@ -0,0 +1 @@
+The World English Bible is a 1997 revision of the American Standard Version of the Holy Bible, first published in 1901. It is in the Public Domain. Please feel free to copy and distribute it freely.

+ 0 - 3
requirements-auth.txt

@@ -1,3 +0,0 @@
-bottle
-cherrypy
-passlib

+ 0 - 0
test/__init__.py


+ 0 - 112
test/rest/rfc2396.py

@@ -1,112 +0,0 @@
-# https://www.ietf.org/rfc/rfc2396.txt
-"""   
-The following definitions are common to many elements:
-
-      alpha    = lowalpha | upalpha
-
-      lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
-                 "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
-                 "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
-
-      upalpha  = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
-                 "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
-                 "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
-
-      digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
-                 "8" | "9"
-                 
-      alphanum = alpha | digit
-
-      uric          = reserved | unreserved | escaped
-
-      reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
-                    "$" | ","
-
-      unreserved  = alphanum | mark
-
-      mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
-
-      escaped     = "%" hex hex
-      hex         = digit | "A" | "B" | "C" | "D" | "E" | "F" |
-                            "a" | "b" | "c" | "d" | "e" | "f"
-
-      control     = <US-ASCII coded characters 00-1F and 7F hexadecimal>
-
-      space       = <US-ASCII coded character 20 hexadecimal>
-
-      delims      = "<" | ">" | "#" | "%" | <">
-
-      unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
-
-      <scheme>:<scheme-specific-part>
-
-      <scheme>://<authority><path>?<query>
-
-      absoluteURI   = scheme ":" ( hier_part | opaque_part )
-
-      hier_part     = ( net_path | abs_path ) [ "?" query ]
-
-      net_path      = "//" authority [ abs_path ]
-
-      abs_path      = "/"  path_segments
-
-      opaque_part   = uric_no_slash *uric
-
-      uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
-                      "&" | "=" | "+" | "$" | ","
-    
-      scheme        = alpha *( alpha | digit | "+" | "-" | "." )
-
-      authority     = server | reg_name
-
-      Within the authority component, the characters ";", ":",
-   "@", "?", and "/" are reserved.
-      
-      reg_name      = 1*( unreserved | escaped | "$" | "," |
-                          ";" | ":" | "@" | "&" | "=" | "+" )
-    
-      <userinfo>@<host>:<port>
-
-      server        = [ [ userinfo "@" ] hostport ]
-
-      userinfo      = *( unreserved | escaped |
-                         ";" | ":" | "&" | "=" | "+" | "$" | "," )
-    
-      hostport      = host [ ":" port ]
-      host          = hostname | IPv4address
-      hostname      = *( domainlabel "." ) toplabel [ "." ]
-      domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
-      toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
-
-      IPv4address   = 1*digit "." 1*digit "." 1*digit "." 1*digit
-      port          = *digit
-
-"""
-# uric := URI characters
-lowalpha = 'a-z'
-upalpha = 'A-Z'
-alpha = f'{lowalpha}{upalpha}'
-digit = '0-9'
-alphanum = f'{alpha}{digit}'
-reserved_no_slash = r';\?:@&=\+\$,'
-reserved = f'/{reserved_no_slash}'
-_mark = r"\-_\.!~*'()"
-hex = f'a-fA-F{digit}'
-escaped = f"%[{hex}][{hex}]"
-unreserved = f'{alphanum}{_mark}'
-control = b''
-#for i in chain(range(int('0x1F', 0)+1), [int('0x7F', 0)]):
-#    control += control + bytes(i)
-control = control.decode('ascii')
-space = ' '
-delims = r'\<\>#%"'
-unwise = r'{}|\\\^\[\]`'
-excluded = f'{control}{space}{delims}{unwise}'
-scheme = f'[{alpha}][{alphanum}+.-]*'
-authority_reserved = r';:@\?/'
-reg_name_aux = r'\$,;:@&=\+'
-reg_name = f'([{unreserved}]|{escaped}|{reg_name_aux})+'
-user_info_aux = r'\$,;:&=\+'
-user_info = f'([{unreserved}]|{escaped}|{user_info_aux})+'
-server = r'({user_info}@)?'
-# ... gave up

File diff suppressed because it is too large
+ 0 - 308
test/rest/test_hash_util.py


+ 0 - 72
test/rest/test_url.py

@@ -1,72 +0,0 @@
-#
-# Copyright (c) Daniel Sheffield 2023
-#
-# All rights reserved
-#
-# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
-
-from pytest import mark, raises
-from bottle import HTTPError
-from rest.validate import validate_url
-
-@mark.parametrize('url, expected', [
-    ['file:///',]*2,
-    ['file:///a/b/c',]*2,
-    ['https://shandan.one',]*2,
-    ['https://www.shandan.one',]*2,
-    ['https://www.shandan.one/clip?id=123',]*2,
-    
-    # empty query
-    ['https://www.shandan.one?', 'https://www.shandan.one',],
-
-    # empty fragment
-    ['https://www.shandan.one/clip?id=123#', 'https://www.shandan.one/clip?id=123',],
-
-    # no double slash
-    #['file:/a/b/c', (HTTPError, ""),],
-    #['file:/abc', (HTTPError, ""),],
-
-    # no scheme
-    ['/a/b/c', (HTTPError, 400, "URL has no scheme"),],
-
-    # no file path scheme
-    ['file:', (HTTPError, 400, "File URL has no path"),],
-    
-    # no HTTPS domain
-    #['https://abc?id=1:', (HTTPError, 400, "HTTP(S) URL has no netloc"),],
-    
-    # conecutive dots
-    #['https://shandan.one/abc..id', 'https://shandan.one/abc..id',],
-
-    # unescaped char in reg_name
-    # TODO: should be invalid because netloc must be a domain name or ip ?
-    ['https://🌚.shandan.one', 'https://%F0%9F%8C%9A.shandan.one',],
-
-    # @ in user_info not allowed
-    # TODO: check this - final @ should not be encoded ?
-    ['https://user@mail@www.shandan.one','https://user%40mail@www.shandan.one'],
-
-    # delimiters
-    # TODO: should < be translated to %3C ?
-    ['https://www.shandan.one?a<b', 'https://www.shandan.one?a<b'],
-
-    # more delimiters
-    ['https://www.shandan.one/clip?proportion=69%', 'https://www.shandan.one/clip?proportion=69%'],
-
-    # fragment before end of reference URI
-    ['https://www.shandan.one/tiny#url?id=123', 'https://www.shandan.one/tiny#url?id=123'],
-])
-def test_validate_url_invalid(url: str, expected: str):
-    if isinstance(expected, tuple):
-        exp_exception, *ex_args = expected
-    else:
-        exp_exception = None
-    
-    if not exp_exception:
-        assert validate_url(url) == expected
-        return
-
-    with raises(exp_exception) as ex:
-        validate_url(url)
-    
-    assert list(ex.value.args) == ex_args

+ 0 - 8
util-sqlpage/clip/Index.sql

@@ -1,8 +0,0 @@
-SET ":inner" = CASE COALESCE(:content,'') <> '' AND COALESCE(:action, '') = 'Paste'
-  WHEN TRUE THEN 'sqlpage/save.sql'
-  ELSE CASE COALESCE(:hash, '')
-    WHEN '' THEN 'sqlpage/Link.sql'
-    ELSE 'sqlpage/link.sql'
-  END
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 47
util-sqlpage/clip/form.sql

@@ -1,47 +0,0 @@
-SET ":view" = COALESCE(:content, '') <> '' AND COALESCE(:action, '') <> 'Edit as New';
-SELECT 'button' AS component;
-SELECT 'Open' AS title
-, 1 AS width
-, '/clip?action=open' AS link
-;
-SELECT 'New' AS title
-, 1 AS width
-, 'gray-500' AS color
-, '/clip' AS link
-;
-
-SELECT 'form' AS component
-, '/clip/' AS action
-, CASE :view WHEN TRUE THEN 'Edit as New' ELSE 'Paste' END AS validate
-, :tabler_color AS validate_color
-, 'post' AS method
-;
-SELECT 'Paste' AS value
-, '' AS label
-, 'hidden' type
-, 'action' AS name
-WHERE NOT :view
-;
-SELECT 'Edit as New' AS value
-, '' AS label
-, 'hidden' AS type
-, 'action' AS name
-WHERE :view
-;
-SELECT :hash AS value
-, '' AS label
-, 'hidden' AS type
-, 'hash' AS name
-WHERE :view
-;
-SELECT 'Paste something here...' AS placeholder
-, 'content' AS name
-, 'textarea' AS type
-, '' AS label
-, 12 AS rows
-, :view::bool AS disabled
-, CASE COALESCE(:action, '')
-  WHEN 'New' THEN NULL
-  ELSE :content
-END AS value
-;

+ 0 - 28
util-sqlpage/clip/index.sql

@@ -1,28 +0,0 @@
-SET ":title" = 'Clip';
-SET ":tool" = 'clip';
-SET ":hash" = COALESCE(:hash, $hash, '');
-SET ":hash" = sqlpage.url_encode(:hash);
-SET ":color" = '#2fb344';
-SET ":tabler_color" = 'green';
-SET ":image" = '/static/clip/clip-favicon_square.svg';
-SET ":favicon" = :image;
-SET ":manifest" = '/static/clip/manifest.json';
-SET ":action" = COALESCE(:action, $action, '');
-SET ":inner" = (CASE :action
-  WHEN 'open' THEN 'sqlpage/Open.sql'
-  ELSE 'clip/Index.sql'
-END);
-SET ":spinner" = COALESCE(:content,'') <> '' AND COALESCE(:action, '') = 'Paste';
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/theme.sql') AS properties;
-SELECT 'loader-start' AS component
-, 'lg' AS size
-, :tabler_color AS color
-, '' AS spinner
-WHERE :spinner
-;
-SELECT 'progress' AS component
-, 'lg' AS size
-, :tabler_color AS color
-WHERE :spinner
-;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 8
util-sqlpage/clip/save.sql

@@ -1,8 +0,0 @@
-INSERT INTO clip (hash, content, qr, created) VALUES (:hash, :content, :qr, CURRENT_TIMESTAMP)
-ON CONFLICT DO
-UPDATE SET
-  content = excluded.content,
-  created = excluded.created,
-  qr = excluded.qr
-WHERE excluded.created > clip.created;
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/link.sql') AS properties;

+ 0 - 11
util-sqlpage/code/Index.sql

@@ -1,11 +0,0 @@
-SET ":inner" = CASE :has_post_params
-  WHEN 1 THEN CASE COALESCE(:hash, '')
-    WHEN '' THEN 'code/save.sql'
-    ELSE 'code/form-fuel.sql'
-  END
-  ELSE CASE COALESCE(:hash, '')
-    WHEN '' THEN 'code/recent.sql'
-    ELSE 'code/form-fuel.sql'
-  END
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

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

@@ -1,67 +0,0 @@
-SET ":created" = COALESCE(:created, (SELECT created FROM code WHERE hash = :hash));
-SET ":expiry" = COALESCE(:expiry, (SELECT expiry FROM code_detail WHERE hash = :hash));
-SET ":value" = COALESCE(:value, (SELECT value FROM code_detail WHERE hash = :hash));
-SET ":store" = COALESCE(:store, json_array((SELECT store FROM code_detail WHERE hash = :hash)));
-SET ":used" = COALESCE(:used, (SELECT used FROM code_detail WHERE hash = :hash));
-SET ":type" = COALESCE(:type, json_array((SELECT type FROM code_detail WHERE hash = :hash)));
-SET ":title" = COALESCE(:type->>0, 'New')||' Voucher';
-SET ":type" = COALESCE(:type, json_array('Fuel'));
-SET ":content" = (SELECT json(content) FROM code WHERE hash = :hash);
-SET ":validate" = 'Update';
-SET ":action" = (CASE COALESCE(:action, '') WHEN '' THEN NULL ELSE :action END);
-SET ":method" = 'post';
-SET ":preview" = (SELECT 'data:image/svg+xml;base64,'||svg FROM code WHERE hash = :hash);
-SET ":autofill" = TRUE;
-SET ":filter_config" = '[
-  {"name": "store[]", "required": true},
-  {"name": "value", "required": true},
-  {"name": "expiry", "required": true},
-    { "name": "used", "label": "Used",
-      "type": "radio", "value": "true",
-      "width": 2, "checked": "'||CASE COALESCE(:used,'')
-  WHEN 'true' THEN 'true' ELSE 'false' END||'"
-    },
-    { "name": "used", "label": "Not Used",
-      "type": "radio", "value": "false",
-      "width": 2, "checked": "'||CASE COALESCE(:used,'')
-  WHEN 'true' THEN 'false' ELSE 'true' END||'"
-    }
-]';
-
-SET ":inner" = CASE COALESCE(:action, '')
-  WHEN 'Update' THEN 'code/update.sql'
-  ELSE 'code/form.sql'
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/theme.sql') AS properties
-WHERE COALESCE(:action, '') <> 'Update';
-
-SELECT 'card' AS component
-, 2 AS columns
-WHERE COALESCE(:action, '') <> 'Update';
-;
-SELECT COALESCE(:store->>'0'||' ', '') || COALESCE(:expiry, :created, '') AS title
-, '
-
-Type: '||COALESCE(:type->>0,'')||'
-
-Value: $'||COALESCE(:value,'')||'
-
-Expires: '||COALESCE(:expiry,'')||'
-
-Submitted: '||COALESCE(:created,'')||'
-
-
-| Type | Content |
-|:-----|:--------|
-| ' || COALESCE(:content->>'format', '') || ' | ' || COALESCE(:content->>'content', '') || ' |
-' AS description_md
-, :preview AS top_image
-, :tabler_color AS color
-WHERE COALESCE(:action, '') <> 'Update';
-;
-
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;
-
-SELECT 'table' AS component;
-SELECT * FROM code_detail
-WHERE hash = :hash;

+ 0 - 59
util-sqlpage/code/form.sql

@@ -1,59 +0,0 @@
-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', s OR CASE :autofill WHEN TRUE THEN c = 1 ELSE FALSE END)
-      ORDER BY v) o
-    FROM (
-      SELECT DISTINCT k, v, s, count(v) OVER (
-        PARTITION BY k ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-      ) c FROM (
-        SELECT 'store'||'[]' k, value v, TRUE s FROM (SELECT value FROM json_each($store))
-        UNION
-        SELECT 'store'||'[]', store, NULL s FROM code_detail
-        UNION
-        SELECT 'type'||'[]' k, value v, TRUE s FROM (SELECT value FROM json_each($type))
-        UNION
-        SELECT 'type'||'[]', type, NULL s FROM code_detail
-        UNION
-        SELECT 'value', value, CASE COALESCE($value, '') WHEN '' THEN FALSE ELSE value = :value END s FROM code_detail
-      )
-    ) options
-    WHERE v IS NOT NULL
-    GROUP BY options.k
-  ) q
-);
-
-SELECT 'form' AS component
-, '/code/' AS action
-, :validate AS validate
-, :tabler_color AS validate_color
-, :method AS method
-;
-SELECT COALESCE(c.v->>'name', j.v->>'name') AS name
---, fo.j#>>'{options}' AS label
-, COALESCE(c.v->>'label', j.v->>'label') AS label
-, COALESCE(c.v->>'type', j.v->>'type') AS type
-, COALESCE(c.v->>'dropdown', j.v->>'dropdown') AS dropdown
-, COALESCE(c.v->>'create_new', j.v->>'create_new') AS create_new
-, COALESCE(c.v->>'multiple', j.v->>'multiple') AS multiple
-, COALESCE(c.v->>'placeholder', j.v->>'placeholder') AS placeholder
-, COALESCE(c.v->>'width', j.v->>'width') AS width
-, o.j->>'options' AS options
-, COALESCE(c.v->>'prefix', j.v->>'prefix') AS prefix
-, COALESCE(c.v->>'value', j.v->>'value', v.v) AS value
-, CASE COALESCE(c.v->>'type', j.v->>'type')
-    WHEN 'checkbox' THEN COALESCE(c.v->>'checked', v.v) = 'true'
-    WHEN 'radio' THEN COALESCE(c.v->>'checked', v.v) = 'true'
-    ELSE NULL
-  END AS checked
-, j.v->>'formaction'
-, COALESCE(c.v->>'required', j.v->>'required') AS required
-FROM (SELECT value v FROM json_each(sqlpage.read_file_as_text('code/json/filters.json'))) j
-FULL JOIN (SELECT value v FROM json_each(:filter_config)) c
-ON (j.v->>'name' = c.v->>'name')
-LEFT JOIN (SELECT value j FROM json_each(:filter_options)) o
-ON (o.j->>'name') = j.v->>'name'
-LEFT JOIN (SELECT "key" k, value v FROM json_each(sqlpage.variables())) v
-ON v.k = j.v->>'name' OR v.k = c.v->>'name'
-;

+ 0 - 16
util-sqlpage/code/index.sql

@@ -1,16 +0,0 @@
-SET ":has_post_params" = (SELECT 1 FROM json_each(sqlpage.variables('post')) LIMIT 1);
-SET ":content" = (SELECT json_patch(sqlpage.variables('post'), '{
-    "has_post_params": null,
-    "timestamp": null
-}'));
-SET ":title" = 'Code';
-SET ":tool" = 'code';
-SET ":hash" = COALESCE($hash, '');
-SET ":hash" = sqlpage.url_encode(:hash);
-SET ":link" = '/code';
-SET ":color" = '#f59f00';
-SET ":tabler_color" = 'azure';
-SET ":image" = '/static/code/qr.svg';
-SET ":favicon" = :image;
---SET ":manifest" = '/static/upload/manifest.json';
-SELECT 'dynamic' AS component, sqlpage.run_sql('code/Index.sql') AS properties;

+ 0 - 24
util-sqlpage/code/json/filters.json

@@ -1,24 +0,0 @@
-[
-    { "name": "type[]", "label": "Type",
-      "type": "select", "dropdown": true, "create_new": true,
-      "width": 2
-    },
-    { "name": "store[]", "label": "Store",
-      "type": "select", "dropdown": true, "create_new": true,
-      "width": 2
-    },
-    { "name": "value", "label": "Value", "prefix": "$",
-      "type": "select", "dropdown": true, "create_new": true,
-      "width": 2
-    },
-    { "name": "expiry", "label": "Expiry",
-      "type": "date",
-      "width": 2
-    },
-    { "name": "action", "label": "", "value": "Update",
-      "type": "hidden"
-    },
-    { "name": "hash", "label": "",
-      "type": "hidden"
-    }
-]

+ 0 - 9
util-sqlpage/code/new.sql

@@ -1,9 +0,0 @@
-INSERT INTO code(hash, content, svg, created)
-VALUES (:hash, :content, :preview, CURRENT_TIMESTAMP)
-ON CONFLICT DO
-UPDATE SET
-  content = excluded.content,
-  created = excluded.created,
-  svg = excluded.svg
-WHERE excluded.created > code.created;
-SELECT 'json' AS component, json('"'||:hash||'"') AS contents;

+ 0 - 52
util-sqlpage/code/recent.sql

@@ -1,52 +0,0 @@
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/theme.sql') AS properties;
-SET ":filter_config" = '[
-  {"name": "expiry", "type": "hidden"},
-  {"name": "value", "type": "hidden"},
-  {"name": "type[]", "label": "Type",
-   "type": "select", "dropdown": true,
-   "multiple": true,
-   "width": 3
-  },
-  {"name": "store[]", "create_new": false,
-   "multiple": true,
-   "width": 3
-  },
-  {"name": "expired", "label": "Show Expired",
-   "type": "checkbox", "value": "true",
-   "width": 2
-  },
-  {"name": "used", "label": "Show Used",
-   "type": "checkbox", "value": "true",
-   "width": 2
-  }
-]';
-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
-, COALESCE(content->>'content'||' ', '') || COALESCE(content->>'type', '') AS description
-, '/code?hash='||c.hash AS link
-FROM code c
-LEFT JOIN code_detail cd
-ON c.hash = cd.hash
-WHERE (
-    cd.expiry IS NULL OR COALESCE($expired, 'false') = 'true' OR date(cd.expiry) > date(datetime(CURRENT_TIMESTAMP, 'localtime'))
-) AND (
-    cd.used IS NULL OR COALESCE($used||'', 'false') = 'true' OR COALESCE(cd.used||'','') = 'false'
-) AND (
-    CASE COALESCE($type, '')
-      WHEN '' THEN TRUE
-      ELSE $type IS NULL OR cd.type IN (SELECT value FROM json_each($type))
-    END
-) AND (
-    CASE COALESCE($store, '')
-      WHEN '' THEN TRUE
-      ELSE $store IS NULL OR cd.store IN (SELECT value FROM json_each($store))
-    END
-)
-ORDER BY expiry, created DESC, type, c.hash NULLS FIRST
-;

+ 0 - 12
util-sqlpage/code/save.sql

@@ -1,12 +0,0 @@
-SET ":request" = json_object(
-    'method', 'POST',
-    'url', 'https://shandan.one/code/meta',
-    'headers', json_object(),
-    'body', json_object(
-        'data', json(:content)
-    )
-);
-SET ":meta" = sqlpage.fetch(:request);
-SET ":hash" = :meta->>'hash';
-SET ":preview" = :meta->>'preview';
-SELECT 'dynamic' AS component, sqlpage.run_sql('code/new.sql') AS properties;

+ 0 - 12
util-sqlpage/code/update.sql

@@ -1,12 +0,0 @@
-INSERT INTO code_detail(hash, type, store, value, expiry, used)
-VALUES (:hash, :type->>0, :store->>0, :value, :expiry, :used)
-ON CONFLICT DO
-UPDATE SET
-  type = excluded.type,
-  store = excluded.store,
-  value = excluded.value,
-  expiry = excluded.expiry,
-  used = excluded.used
-WHERE TRUE
-;
-SELECT 'redirect' AS component, '/code' AS link;

+ 0 - 8
util-sqlpage/goto/Index.sql

@@ -1,8 +0,0 @@
-SET ":inner" = CASE COALESCE(:content,'') <> '' AND COALESCE(:action, '') = 'Shrtn It!'
-  WHEN TRUE THEN 'sqlpage/save.sql'
-  ELSE CASE COALESCE(:hash,'')
-    WHEN '' THEN 'sqlpage/Link.sql'
-    ELSE 'sqlpage/link.sql'
-  END
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 36
util-sqlpage/goto/form.sql

@@ -1,36 +0,0 @@
-SET ":view" = COALESCE(:content, '') <> '';
-SELECT 'button' AS component;
-SELECT 'Open' AS title
-, 1 AS width
-, '/goto?action=open' AS link
-;
-SELECT 'New' AS title
-, 1 AS width
-, 'gray-500' AS color
-, '/goto' AS link
-;
-
-SELECT 'form' AS component
-, '/goto/' AS action
-, 'Shrtn It!' AS validate
-, :tabler_color AS validate_color
-, 'post' AS method
-WHERE NOT :view
-;
-SELECT 'action' AS name
-, '' AS label
-, 'hidden' AS type
-, 'Shrtn It!' AS value
-WHERE NOT :view
-;
-SELECT 'content' AS name
-, '' AS label
-, 'input' AS type
-, :content AS value
-, 'Paste URL here...' AS placeholder
-WHERE NOT :view
-;
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('goto/preview.sql') AS properties
-WHERE :content <> ''
-;

+ 0 - 32
util-sqlpage/goto/index.sql

@@ -1,32 +0,0 @@
-SET ":title" = 'GoTo';
-SET ":tool" = 'goto';
-SET ":hash" = COALESCE($hash, '');
-SET ":hash" = sqlpage.url_encode(:hash);
-SET ":go" = COALESCE($go, '');
-SET ":color" = '#dc4e41';
-SET ":tabler_color" = 'google';
-SET ":image" = '/static/goto/chain-link2fr-3-2.svg';
-SET ":favicon" = :image;
-SET ":manifest" = '/static/goto/manifest.json';
-SET ":action" = COALESCE($action, :action, '');
-SET ":inner" = (CASE :action
-  WHEN 'open' THEN 'sqlpage/Open.sql'
-  ELSE 'goto/Index.sql'
-END);
-SELECT 'dynamic' AS component, sqlpage.run_sql('goto/redirect.sql') AS properties
-WHERE (:go = 'true' AND :hash <> '');
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/theme.sql') AS properties;
-SET ":spinner" = COALESCE(:content,'') <> '' AND COALESCE(:action, '') = 'Shrtn It!';
-SELECT 'loader-start' AS component
-, 'lg' AS size
-, :tabler_color AS color
-, '' AS spinner
-WHERE :spinner
-;
-SELECT 'progress' AS component
-, 'lg' AS size
-, :tabler_color AS color
-WHERE :spinner
-;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 12
util-sqlpage/goto/preview.sql

@@ -1,12 +0,0 @@
-SET ":title" = :preview->>'title';
-SET ":image" = :preview->>'img';
-SET ":domain" = :preview->>'domain';
-SELECT 'card' AS component
-, 2 AS columns
-;
-SELECT :content AS link
-, :title AS title
-, :image AS top_image
-, :domain AS description
-, :tabler_color AS color
-;

+ 0 - 11
util-sqlpage/goto/redirect.sql

@@ -1,11 +0,0 @@
-SET ":content" = (SELECT content FROM goto WHERE hash = :hash);
-SET ":icon" = 'error-404';
-SET ":status" = '404';
-SET ":title" = :status||' - Not found';
-SET ":description" = 'No such '||:tool||': '||:hash;
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/alert.sql') AS properties
-WHERE COALESCE(:content,'') = ''
-;
-SELECT 'redirect' AS component
-, :content AS link
-;

+ 0 - 9
util-sqlpage/goto/save.sql

@@ -1,9 +0,0 @@
-INSERT INTO goto (hash, content, qr, preview, created) VALUES (:hash, :content, :qr, json(:preview), CURRENT_TIMESTAMP)
-ON CONFLICT DO
-UPDATE SET
-  content = excluded.content,
-  created = excluded.created,
-  qr = excluded.qr,
-  preview = excluded.preview
-WHERE excluded.created > goto.created;
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/link.sql') AS properties;

+ 0 - 22
util-sqlpage/sqlpage/Link.sql

@@ -1,22 +0,0 @@
-SELECT 'loader-stop' AS component
-WHERE :spinner;
-
-SET ":link" = COALESCE(:link, 'https://shandan.one/'||:tool);
-SELECT 'text' AS component
-, '<div class="pure-g" sty>
-  <div class="pure-u-1">
-    <div class="pure-button" style="margin: 1em 0 0; background: '||:color||';">
-      <a href="'||:link||'" style="color: floralwhite;">'||:link||'</a>
-    </div>
-  </div>
-</div>' AS html
-;
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/QR.sql') AS properties
-WHERE COALESCE (:hash, '') = '';
-
-SET ":inner" = CASE COALESCE(:hash, '')
-  WHEN '' THEN :tool||'/form.sql'
-  ELSE 'sqlpage/validate.sql'
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 46
util-sqlpage/sqlpage/Open.sql

@@ -1,46 +0,0 @@
-SELECT 'text' AS component
-, '<style>
-.form-fieldset {
-    border: none;
-}
-.form-fieldset .row {
-    align-items: center;
-    justify-content: center;
-}
-input[name=hash] {
-    text-align: center;
-}
-</style>' AS html
-;
-SELECT 'button' AS component
-, 'center' AS justify
-, 'lg' AS size
-;
-SELECT 'New '||$title AS title
-, 2 AS width
-, 'gray-500' AS color
-, '/'||$tool AS link
-;
-SELECT 'form' AS component
-, $tool||'-open' AS id
-, '/'||$tool||'/' AS action
-, '' AS validate
-, 'get' AS method
-;
-SELECT 'C0DE' AS placeholder
-, 'input' AS type
-, '' AS label
-, 'hash' AS name
-, 2 AS width
-, TRUE AS autofocus
-;
-SELECT 'button' AS component
-, 'center' AS justify
-, 'lg' AS size
-, 2 AS width
-;
-SELECT 'Submit' AS title
-, 1 AS width
-, $tabler_color AS color
-, $tool||'-open' AS form
-;

+ 0 - 11
util-sqlpage/sqlpage/QR.sql

@@ -1,11 +0,0 @@
-SET ":qr" = COALESCE(:qr, '<img src="/static/'||:tool||'/qr.svg"/>');
-
-SELECT 'text' AS component
-, '<div class="pure-g" sty>
-  <div class="pure-u-1">
-    <details><summary>Show QR code ...</summary>
-'||:qr||'
-    </details>
-  </div>
-</div>' AS html
-;

+ 0 - 50
util-sqlpage/sqlpage/Style.sql

@@ -1,50 +0,0 @@
-SELECT 'text' AS component
-, '<style>
-/* loader container */
-.sqlpage-loader-container {
-  position: fixed;
-  text-align: center;
-  left: 50vw;
-  top: 50vh;
-  margin-top: -5.5em;
-  margin-left: -87.5px;
-  padding-bottom: 2em;
-  height: 9em;
-  width: 175px;
-}
-.sqlpage-loader-container:has(.status) {
-  position: inherit;
-  text-align: inherit;
-  left: inherit;
-  top: inherit;
-  margin-top: inherit;
-  margin-left: inherit;
-  padding-bottom: inherit;
-  height: inherit;
-  width: inherit;
-}
-div.sqlpage-loader-start:has(+ .sqlpage-loader-stop) {
-  /* hide if followed by sqlpage-loader-stop */
-  display: none;
-}
-/* end loader container */
-
-/* progress container */
-.sqlpage-progress-container {
-  margin: 1em 0 1em;
-}
-div.sqlpage-progress-container:has(+ .sqlpage-progress-container) {
-  /* hide if followed by sqlpage-progress-container */
-  display: none;
-}
-.sqlpage-progress-container label {
-  text-align:left;
-  color: var(--tblr-text-primary);
-}
-.sqlpage-progress-container label:after {
-  content: "…";
-}
-/* end progress container */
-</style>
-' AS html
-;

+ 0 - 5
util-sqlpage/sqlpage/alert.sql

@@ -1,5 +0,0 @@
-SELECT 'alert' AS component
-, $icon AS icon
-, $title AS title
-, $description AS description
-;

+ 0 - 5
util-sqlpage/sqlpage/link.sql

@@ -1,5 +0,0 @@
-SET ":url" = 'https://shandan.one/'||:tool||'/normalize?hash='||:hash;
-SET ":api_results" = sqlpage.fetch(:url);
-SET ":hash" = :api_results->>'o';
-SET ":link" = COALESCE('https://shandan.one/'||:tool||'/'||:hash, NULL);
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/Link.sql') AS properties;

+ 0 - 7
util-sqlpage/sqlpage/migrations/000_clip.sql

@@ -1,7 +0,0 @@
-DROP TABLE IF EXISTS clip;
-CREATE TABLE IF NOT EXISTS clip(
-  hash text PRIMARY KEY,
-  content text,
-  qr text,
-  created timestamp
-);

+ 0 - 7
util-sqlpage/sqlpage/migrations/001_goto.sql

@@ -1,7 +0,0 @@
-DROP TABLE IF EXISTS goto;
-CREATE TABLE IF NOT EXISTS goto(
-  hash text PRIMARY KEY,
-  content text,
-  qr text,
-  created timestamp
-);

+ 0 - 7
util-sqlpage/sqlpage/migrations/003_upload.sql

@@ -1,7 +0,0 @@
-DROP TABLE IF EXISTS upload;
-CREATE TABLE IF NOT EXISTS upload(
-  hash text PRIMARY KEY,
-  content BLOB,
-  qr text,
-  created timestamp
-);

+ 0 - 8
util-sqlpage/sqlpage/migrations/004_upload.sql

@@ -1,8 +0,0 @@
-DROP TABLE IF EXISTS upload;
-CREATE TABLE IF NOT EXISTS upload(
-  hash text PRIMARY KEY,
-  content text,
-  mime text,
-  qr text,
-  created timestamp
-);

+ 0 - 9
util-sqlpage/sqlpage/migrations/005_upload.sql

@@ -1,9 +0,0 @@
-DROP TABLE IF EXISTS upload;
-CREATE TABLE IF NOT EXISTS upload(
-  hash text PRIMARY KEY,
-  content text,
-  name text,
-  mime text,
-  qr text,
-  created timestamp
-);

+ 0 - 7
util-sqlpage/sqlpage/migrations/006_code.sql

@@ -1,7 +0,0 @@
-DROP TABLE IF EXISTS code;
-CREATE TABLE IF NOT EXISTS code(
-  hash text PRIMARY KEY,
-  content text,
-  svg text,
-  created timestamp
-);

+ 0 - 9
util-sqlpage/sqlpage/migrations/007_code_detail.sql

@@ -1,9 +0,0 @@
-DROP TABLE IF EXISTS code_detail;
-CREATE TABLE IF NOT EXISTS code_detail(
-  hash text PRIMARY KEY,
-  type text,
-  store text,
-  value numeric,
-  expiry timestamp,
-  used bool
-);

+ 0 - 6
util-sqlpage/sqlpage/migrations/008_upload_temp.sql

@@ -1,6 +0,0 @@
-DROP TABLE IF EXISTS upload_temp;
-CREATE TABLE IF NOT EXISTS upload_temp(
-  name text,
-  mime text,
-  content text
-);

+ 0 - 1
util-sqlpage/sqlpage/migrations/009_goto.sql

@@ -1 +0,0 @@
-ALTER TABLE goto ADD COLUMN preview JSON;

+ 0 - 17
util-sqlpage/sqlpage/save.sql

@@ -1,17 +0,0 @@
-SET ":request" = json_object(
-    'method', 'POST',
-    'url', 'https://shandan.one/'||:tool||'/meta',
-    'headers', json_object(),
-    'timeout_ms', 15000,
-    'body', json_object(
-        'data', CASE :tool
-            WHEN 'code' THEN json(:content)
-            ELSE :content
-        END
-    )
-);
-SET ":meta" = json(sqlpage.fetch(:request));
-SET ":qr" = :meta->>'qr';
-SET ":hash" = :meta->>'hash';
-SET ":preview" = :meta->>'preview';
-SELECT 'dynamic' AS component, sqlpage.run_sql(:tool||'/save.sql') AS properties;

+ 0 - 6
util-sqlpage/sqlpage/sqlpage.json

@@ -1,6 +0,0 @@
-{
-  "max_uploaded_file_size": 5242880000,
-  "max_database_pool_connections": 16,
-  "database_url": "sqlite://./util.db",
-  "compress_responses": false
-}

+ 0 - 6
util-sqlpage/sqlpage/templates/loader-start.handlebars

@@ -1,6 +0,0 @@
-<div {{#if id}}id="{{id}}"{{/if}} class="sqlpage-loader-start">
-  <div class="sqlpage-loader-container">
-    <span class="{{default spinner "spinner-border"}}
-      {{#if size}}spinner-border-{{size}}{{/if}}
-      {{#if color}}text-{{color}}{{/if}}">
-    </span>

+ 0 - 3
util-sqlpage/sqlpage/templates/loader-stop.handlebars

@@ -1,3 +0,0 @@
-  </div>
-</div>
-<div class="sqlpage-loader-stop"></div>

+ 0 - 42
util-sqlpage/sqlpage/templates/progress.handlebars

@@ -1,42 +0,0 @@
-<div class="sqlpage-progress-container">
-  <div class="progress {{~#if size}} progress-{{size}}{{/if}}">
-    <div
-      id="sqlpage-loading-{{default stage "progress"}}"
-      class="progress-bar
-      {{~#if (not percent)}} progress-bar-indeterminate{{/if}}
-      {{~#if color}} bg-{{color}}{{/if}}"
-      role="progressbar"
-      aria-valuenow="{{percent}}"
-      {{~#if percent}}style="width: {{percent}}%; display: block"{{/if}} 
-      aria-valuemin="0" aria-valuemax="100"
-      {{~#if stage}}aria-label="{{stage}}"{{/if}}>
-    </div>
-  </div>
-  {{~#if stage}}
-    <br>
-    <label for="sqlpage-loading-{{default stage "progress"}}">{{stage}}</label>
-  {{/if}}
-</div>
-{{#each_row}}
-{{#if (or percent stage)}}
-  <div class="sqlpage-progress-container">
-    <div class="progress {{~#if ../size}} progress-{{../size}}{{/if}}">
-      <div
-        id="sqlpage-loading-{{default stage "progress"}}"
-        class="progress-bar
-        {{~#if (not percent)}} progress-bar-indeterminate{{/if}}
-        {{~#if ../color}} bg-{{../color}}{{/if}}"
-        role="progressbar"
-        aria-valuenow="{{percent}}"
-        {{~#if percent}}style="width: {{percent}}%; display: block"{{/if}} 
-        aria-valuemin="0" aria-valuemax="100"
-        {{~#if stage}}aria-label="{{stage}}"{{/if}}>
-      </div>
-    </div>
-    {{~#if stage}}
-      <br>
-      <label for="sqlpage-loading-{{default stage "progress"}}">{{stage}}</label>
-    {{/if}}
-  </div>
-{{/if}}
-{{/each_row}}

+ 0 - 2
util-sqlpage/sqlpage/templates/spinner-start.handlebars

@@ -1,2 +0,0 @@
-<div class="spinner-start">
-  <span class="{{class}}"></span>

+ 0 - 2
util-sqlpage/sqlpage/templates/spinner-stop.handlebars

@@ -1,2 +0,0 @@
-</div>
-<div class="spinner-stop" />

+ 0 - 46
util-sqlpage/sqlpage/theme.sql

@@ -1,46 +0,0 @@
-SELECT 'shell' AS component
-, 'dark' AS theme
-, :title AS title
-, :link AS link
-, :image AS image
-, :favicon AS favicon
-, :manifest AS manifest
-, 'https://cdn.jsdelivr.net/npm/purecss@2.1.0/build/pure-min.css' AS css
-, 'https://shandan.one/css/'||:tool||'.css' AS css
-, json('{
-    "title": "Home", "link": "/", "icon": "home"
-  }') AS menu_item
-, json(CASE :tool
-  WHEN 'clip' THEN NULL
-  ELSE '{
-            "link": "/clip",
-            "title": "Clip and Paste",
-            "icon": "clipboard"
-        }'
-  END) AS menu_item
-, json(CASE :tool
-  WHEN 'goto' THEN NULL
-  ELSE '{
-            "link": "/goto",
-            "title": "GotTo Tiny URL",
-            "icon": "link"
-        }'
-  END) AS menu_item
-, json(CASE :tool
-  WHEN 'upload' THEN NULL
-  ELSE '{
-            "link": "/upload",
-            "title": "Share File",
-            "icon": "cloud-share"
-        }'
-  END) AS menu_item
-, json(CASE :tool
-  WHEN 'code' THEN NULL
-  ELSE '{
-            "link": "/code",
-            "title": "Vouchers",
-            "icon": "barcode"
-        }'
-  END) AS menu_item
-;
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/Style.sql') AS properties;

+ 0 - 45
util-sqlpage/sqlpage/validate.sql

@@ -1,45 +0,0 @@
-SET ":content" = (
-  SELECT content
-  FROM clip
-  WHERE hash = :hash AND :tool = 'clip'
-  UNION
-  SELECT content
-  FROM goto
-  WHERE hash = :hash AND :tool = 'goto'
-  UNION
-  SELECT content
-  FROM upload
-  WHERE hash = :hash AND :tool = 'upload'
-);
-
-SET ":qr" = (
-  SELECT qr
-  FROM clip
-  WHERE hash = :hash AND :tool = 'clip'
-  UNION
-  SELECT qr
-  FROM goto
-  WHERE hash = :hash AND :tool = 'goto'
-  UNION
-  SELECT qr
-  FROM upload
-  WHERE hash = :hash AND :tool = 'upload'
-);
-
-SET ":preview" = (SELECT preview FROM goto WHERE hash = :hash AND :tool = 'goto');
-SET ":file_name" = (SELECT name FROM upload WHERE hash = :hash AND :tool = 'upload');
-SET ":mime_type" = (SELECT mime FROM upload WHERE hash = :hash AND :tool = 'upload');
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/QR.sql') AS properties
-WHERE COALESCE (:hash, '') <> '';
-
-SET ":inner" = CASE COALESCE(:content,'')
-  WHEN '' THEN 'sqlpage/alert.sql'
-  ELSE :tool||'/form.sql'
-END;
-
-SET ":icon" = 'error-404';
-SET ":status" = '404';
-SET ":title" = :status||' - Not found';
-SET ":description" = 'No such '||:tool||': '||:hash;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 8
util-sqlpage/upload/Index.sql

@@ -1,8 +0,0 @@
-SET ":inner" = CASE COALESCE($rowid, '') <> ''
-  WHEN TRUE THEN 'sqlpage/save.sql'
-  ELSE CASE COALESCE(:hash, '')
-    WHEN '' THEN 'sqlpage/Link.sql'
-    ELSE 'sqlpage/link.sql'
-  END
-END;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 57
util-sqlpage/upload/form.sql

@@ -1,57 +0,0 @@
-SET ":view" = COALESCE(:content, '') <> '';
-
-SELECT 'button' AS component;
-SELECT 'Open' AS title
-, 1 AS width
-, '/upload?action=open' AS link
-;
-SELECT 'New' AS title
-, 1 AS width
-, 'gray-500' AS color
-, '/upload' AS link
-;
-SELECT 'Download' AS title
-, 2 AS width
-, 'gray-500' AS color
-, '/upload/'||:hash AS link
-WHERE :hash <> ''
-;
-
-SELECT 'form' AS component
-, '/upload/' AS action
-, 'Upload' AS validate
-, 'post' AS method
-, :tabler_color AS validate_color
-WHERE NOT :view
-;
-SELECT 'Upload' AS value
-, '' AS label
-, 'hidden' AS type
-, 'action' AS name
-WHERE NOT :view
-;
-SELECT 'content' AS name
-, CASE :view WHEN FALSE THEN 'file' ELSE 'hidden' END AS type
-, '' AS label
---, :view AS disabled
-, CASE COALESCE(:action, '')
-  WHEN 'New' THEN NULL
-  ELSE :content
-END AS value
-, 8 AS width
-WHERE NOT :view
-;
-SELECT 'card' as component
-, 2 as columns
-WHERE :view
-;
-SELECT 'Preview' as title
-, :tabler_color AS color
-, CASE WHEN substr(:mime_type, 0, instr(:mime_type, '/')) = 'image' THEN :content ELSE NULL END AS top_image
-, '
-Uploaded file type: ' || COALESCE(:mime_type, 'null') ||'
-
-Uploaded file name: ' || COALESCE(:file_name, 'null') ||'
-' AS description_md
-WHERE :view
-;

+ 0 - 43
util-sqlpage/upload/index.sql

@@ -1,43 +0,0 @@
-SET ":tool" = 'upload';
-SET ":hash" = COALESCE($hash, '');
-SET ":hash" = sqlpage.url_encode(:hash);
-SET ":title" = 'Upload';
-SET ":color" = '#f59f00';
-SET ":tabler_color" = 'yellow';
-SET ":image" = '/static/upload/upload-favicon_square.svg';
-SET ":favicon" = :image;
-SET ":manifest" = '/static/upload/manifest.json';
-SET ":action" = COALESCE($action, :action, '');
-SET ":inner" = (CASE :action
-  WHEN 'open' THEN 'sqlpage/Open.sql'
-  ELSE 'upload/Index.sql'
-END);
-SET ":file_name" = sqlpage.uploaded_file_name('content');
-SET ":mime_type" = sqlpage.uploaded_file_mime_type('content');
--- although using a variable works, docs say to pass the function as first argument
--- https://sql.ophir.dev/functions.sql?function=read_file_as_data_url#function
-SET ":content" = sqlpage.read_file_as_data_url(sqlpage.uploaded_file_path('content'));
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('upload/temp.sql') AS properties
-WHERE :content IS NOT NULL AND $rowid IS NULL;
-
-SET ":file_name" = (SELECT name FROM upload_temp WHERE rowid = $rowid);
-SET ":mime_type" = (SELECT mime FROM upload_temp WHERE rowid = $rowid);
-SET ":content" = CAST($rowid AS INTEGER);
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/theme.sql') AS properties
---WHERE $rowid IS NULL
-;
-SET ":spinner" = COALESCE(:hash, '') <> '' OR :content IS NOT NULL OR $rowid IS NOT NULL; --COALESCE($rowid, '') <> '';
-SELECT 'loader-start' AS component
-, 'lg' AS size
-, :tabler_color AS color
-, '' AS spinner
-WHERE :spinner
-;
-SELECT 'progress' AS component
-, 'lg' AS size
-, :tabler_color AS color
-WHERE :spinner
-;
-SELECT 'dynamic' AS component, sqlpage.run_sql(:inner) AS properties;

+ 0 - 20
util-sqlpage/upload/save.sql

@@ -1,20 +0,0 @@
-SET ":rowid" = :content;
-SET ":content" = (SELECT content FROM upload_temp WHERE rowid = :rowid);
-
-INSERT INTO upload (hash, content, name, mime, qr, created)
-VALUES (:hash, :content, :file_name, :mime_type, :qr, CURRENT_TIMESTAMP)
-ON CONFLICT DO
-UPDATE SET
-  content = excluded.content,
-  name = excluded.name,
-  mime = excluded.mime,
-  created = excluded.created,
-  qr = excluded.qr
-WHERE excluded.created > upload.created;
-
-DELETE FROM upload_temp WHERE rowid = :rowid;
-
---SELECT 'redirect' AS component, '/upload?hash='||:hash AS link
---WHERE :rowid IS NOT NULL;
-
-SELECT 'dynamic' AS component, sqlpage.run_sql('sqlpage/link.sql') AS properties;

+ 0 - 3
util-sqlpage/upload/temp.sql

@@ -1,3 +0,0 @@
-DELETE FROM upload_temp WHERE name IS NULL OR mime IS NULL;
-INSERT INTO upload_temp(name, mime, content) VALUES (:file_name, :mime_type, :content)
-RETURNING 'redirect' AS component, '/upload?rowid='||rowid AS link;

Some files were not shown because too many files changed in this diff