added prefix option
This commit is contained in:
@@ -32,7 +32,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
md2html = ['static/*.html', 'static/*.css', 'static/*.js']
|
||||
md2html = ['static/*']
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/woggioni/md2html"
|
||||
|
@@ -1,20 +1,25 @@
|
||||
import sys
|
||||
from os.path import dirname, join, relpath
|
||||
from time import time
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
import markdown
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import StrOrBytesPath
|
||||
|
||||
STATIC_RESOURCES: set[str] = {
|
||||
'/github-markdown.css',
|
||||
'/custom.css',
|
||||
'/hot-reload.js',
|
||||
'/pygment.css',
|
||||
'/markdown.svg'
|
||||
}
|
||||
STATIC_CACHE: dict[str, tuple[str, float]] = {}
|
||||
|
||||
MARDOWN_EXTENSIONS = ['extra', 'smarty', 'tables', 'codehilite']
|
||||
|
||||
|
||||
def load_from_cache(path) -> tuple[str, float]:
|
||||
global STATIC_CACHE
|
||||
if path not in STATIC_CACHE:
|
||||
@@ -24,7 +29,8 @@ def load_from_cache(path) -> tuple[str, float]:
|
||||
|
||||
|
||||
def compile_html(url_path,
|
||||
mdfile=None,
|
||||
mdfile: 'StrOrBytesPath',
|
||||
prefix: Optional['StrOrBytesPath'] = None,
|
||||
extensions: Optional[list[str]] = None,
|
||||
raw: bool = False) -> str:
|
||||
with mdfile and open(mdfile, 'r') or sys.stdin as instream:
|
||||
@@ -33,9 +39,9 @@ def compile_html(url_path,
|
||||
doc = html
|
||||
else:
|
||||
parent = dirname(url_path)
|
||||
prefix = relpath('/', start=parent)
|
||||
prefix = prefix or relpath('/', start=parent)
|
||||
script = f'<script src="{prefix}/hot-reload.js", type="text/javascript" defer="true"></script>'
|
||||
css = ''
|
||||
css = f'<link rel="icon" type="image/x-icon" href="{prefix}/markdown.svg">'
|
||||
for css_file in ('github-markdown.css', 'pygment.css', 'custom.css'):
|
||||
css += f' <link rel="stylesheet" href="{prefix}/{css_file}">'
|
||||
doc = load_from_cache('/template.html')[0].format(content=html, script=script, css=css)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from os import getcwd, listdir
|
||||
from os.path import exists, splitext, isfile, join, relpath, isdir, basename, getmtime, dirname
|
||||
from os.path import exists, splitext, isfile, join, relpath, isdir, basename, getmtime, dirname, normpath
|
||||
from mimetypes import init as mimeinit, guess_type
|
||||
import hashlib
|
||||
from .md2html import compile_html, load_from_cache, STATIC_RESOURCES, MARDOWN_EXTENSIONS
|
||||
@@ -33,17 +33,20 @@ def is_dotfile(filepath):
|
||||
|
||||
class Server:
|
||||
|
||||
def __init__(self, root_dir: 'StrOrBytesPath' = getcwd()):
|
||||
def __init__(self, root_dir: 'StrOrBytesPath' = getcwd(), prefix: Optional['StrOrBytesPath'] = None):
|
||||
self.root_dir = root_dir
|
||||
self.cache = dict['StrOrBytesPath', tuple[str, float]]()
|
||||
self.file_watcher = FileWatcher(cwd)
|
||||
self.logger = logging.getLogger(Server.__name__)
|
||||
self.prefix = prefix and normpath(f'{prefix.decode()}')
|
||||
|
||||
def handle_request(self, method: str, url_path: str, etag: Optional[str], query_string: Optional[str], start_response):
|
||||
if method != 'GET':
|
||||
start_response('405', [])
|
||||
return []
|
||||
path: 'StrOrBytesPath' = join(self.root_dir, relpath(url_path, '/'))
|
||||
relative_path = relpath(url_path, start=self.prefix or '/')
|
||||
url_path: 'StrOrBytesPath' = normpath(join('/', relative_path))
|
||||
path: 'StrOrBytesPath' = join(self.root_dir, relative_path)
|
||||
if url_path in STATIC_RESOURCES:
|
||||
content, mtime = load_from_cache(url_path)
|
||||
content = content.encode()
|
||||
@@ -186,14 +189,15 @@ class Server:
|
||||
etag = Server.parse_etag(etag_header)
|
||||
return etag, digest
|
||||
|
||||
@staticmethod
|
||||
def render_markdown(url_path: 'StrOrBytesPath',
|
||||
def render_markdown(self,
|
||||
url_path: 'StrOrBytesPath',
|
||||
path: str,
|
||||
raw: bool,
|
||||
digest: str,
|
||||
start_response) -> list[bytes]:
|
||||
body = compile_html(url_path,
|
||||
path,
|
||||
self.prefix,
|
||||
MARDOWN_EXTENSIONS,
|
||||
raw=raw).encode()
|
||||
start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'),
|
||||
@@ -214,10 +218,11 @@ class Server:
|
||||
start_response('404 NOT_FOUND', [])
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def directory_listing(path_info, path) -> str:
|
||||
def directory_listing(self, path_info, path) -> str:
|
||||
icon_path = join(self.prefix or '', 'markdown.svg')
|
||||
title = "Directory listing for %s" % path_info
|
||||
result = "<!DOCTYPE html><html><head>"
|
||||
result += f'<link rel="icon" type="image/x-icon" href="{icon_path}">'
|
||||
result += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
|
||||
result += "<title>" + title + "</title></head>"
|
||||
result += "<body><h1>" + title + "</h1><hr>"
|
||||
|
1
src/md2html/static/markdown.svg
Normal file
1
src/md2html/static/markdown.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><rect fill="#fff" height="512" rx="15%" width="512"/><path d="m410 366h-308c-14 0-26-12-26-26v-170c0-14 12-26 26-26h307c14 0 26 12 26 26v170c0 14-11 26-25 26zm-308-204c-4 0-9 4-9 9v170c0 5 4 9 9 9h307c5 0 9-4 9-9v-171c0-5-4-9-9-9h-307zm26 153v-119h34l34 43 34-43h35v118h-34v-68l-34 43-34-43v68zm216 0-52-57h34v-61h34v60h34z"/></svg>
|
After Width: | Height: | Size: 394 B |
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from .server import Server
|
||||
from uwsgi import log
|
||||
from uwsgi import log, opt
|
||||
class UwsgiHandler(logging.Handler):
|
||||
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
@@ -13,7 +13,7 @@ logging.basicConfig(
|
||||
handlers=[UwsgiHandler()]
|
||||
)
|
||||
|
||||
server = Server()
|
||||
server = Server(prefix=opt.get('prefix', None))
|
||||
|
||||
def application(env, start_response):
|
||||
return server.handle_request(
|
||||
|
Reference in New Issue
Block a user