added prefix option

This commit is contained in:
2023-10-20 14:55:31 +08:00
parent 40bd2111bf
commit ef8da4e6cc
5 changed files with 26 additions and 14 deletions

View File

@@ -32,7 +32,7 @@ dependencies = [
] ]
[tool.setuptools.package-data] [tool.setuptools.package-data]
md2html = ['static/*.html', 'static/*.css', 'static/*.js'] md2html = ['static/*']
[project.urls] [project.urls]
"Homepage" = "https://github.com/woggioni/md2html" "Homepage" = "https://github.com/woggioni/md2html"

View File

@@ -1,20 +1,25 @@
import sys import sys
from os.path import dirname, join, relpath from os.path import dirname, join, relpath
from time import time from time import time
from typing import Optional from typing import Optional, TYPE_CHECKING
import markdown import markdown
if TYPE_CHECKING:
from _typeshed import StrOrBytesPath
STATIC_RESOURCES: set[str] = { STATIC_RESOURCES: set[str] = {
'/github-markdown.css', '/github-markdown.css',
'/custom.css', '/custom.css',
'/hot-reload.js', '/hot-reload.js',
'/pygment.css', '/pygment.css',
'/markdown.svg'
} }
STATIC_CACHE: dict[str, tuple[str, float]] = {} STATIC_CACHE: dict[str, tuple[str, float]] = {}
MARDOWN_EXTENSIONS = ['extra', 'smarty', 'tables', 'codehilite'] MARDOWN_EXTENSIONS = ['extra', 'smarty', 'tables', 'codehilite']
def load_from_cache(path) -> tuple[str, float]: def load_from_cache(path) -> tuple[str, float]:
global STATIC_CACHE global STATIC_CACHE
if path not in 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, def compile_html(url_path,
mdfile=None, mdfile: 'StrOrBytesPath',
prefix: Optional['StrOrBytesPath'] = None,
extensions: Optional[list[str]] = None, extensions: Optional[list[str]] = None,
raw: bool = False) -> str: raw: bool = False) -> str:
with mdfile and open(mdfile, 'r') or sys.stdin as instream: with mdfile and open(mdfile, 'r') or sys.stdin as instream:
@@ -33,9 +39,9 @@ def compile_html(url_path,
doc = html doc = html
else: else:
parent = dirname(url_path) 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>' 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'): for css_file in ('github-markdown.css', 'pygment.css', 'custom.css'):
css += f' <link rel="stylesheet" href="{prefix}/{css_file}">' css += f' <link rel="stylesheet" href="{prefix}/{css_file}">'
doc = load_from_cache('/template.html')[0].format(content=html, script=script, css=css) doc = load_from_cache('/template.html')[0].format(content=html, script=script, css=css)

View File

@@ -1,6 +1,6 @@
import logging import logging
from os import getcwd, listdir 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 from mimetypes import init as mimeinit, guess_type
import hashlib import hashlib
from .md2html import compile_html, load_from_cache, STATIC_RESOURCES, MARDOWN_EXTENSIONS from .md2html import compile_html, load_from_cache, STATIC_RESOURCES, MARDOWN_EXTENSIONS
@@ -33,17 +33,20 @@ def is_dotfile(filepath):
class Server: 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.root_dir = root_dir
self.cache = dict['StrOrBytesPath', tuple[str, float]]() self.cache = dict['StrOrBytesPath', tuple[str, float]]()
self.file_watcher = FileWatcher(cwd) self.file_watcher = FileWatcher(cwd)
self.logger = logging.getLogger(Server.__name__) 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): def handle_request(self, method: str, url_path: str, etag: Optional[str], query_string: Optional[str], start_response):
if method != 'GET': if method != 'GET':
start_response('405', []) start_response('405', [])
return [] 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: if url_path in STATIC_RESOURCES:
content, mtime = load_from_cache(url_path) content, mtime = load_from_cache(url_path)
content = content.encode() content = content.encode()
@@ -186,14 +189,15 @@ class Server:
etag = Server.parse_etag(etag_header) etag = Server.parse_etag(etag_header)
return etag, digest return etag, digest
@staticmethod def render_markdown(self,
def render_markdown(url_path: 'StrOrBytesPath', url_path: 'StrOrBytesPath',
path: str, path: str,
raw: bool, raw: bool,
digest: str, digest: str,
start_response) -> list[bytes]: start_response) -> list[bytes]:
body = compile_html(url_path, body = compile_html(url_path,
path, path,
self.prefix,
MARDOWN_EXTENSIONS, MARDOWN_EXTENSIONS,
raw=raw).encode() raw=raw).encode()
start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'), start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'),
@@ -214,10 +218,11 @@ class Server:
start_response('404 NOT_FOUND', []) start_response('404 NOT_FOUND', [])
return [] return []
@staticmethod def directory_listing(self, path_info, path) -> str:
def directory_listing(path_info, path) -> str: icon_path = join(self.prefix or '', 'markdown.svg')
title = "Directory listing for %s" % path_info title = "Directory listing for %s" % path_info
result = "<!DOCTYPE html><html><head>" 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 += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
result += "<title>" + title + "</title></head>" result += "<title>" + title + "</title></head>"
result += "<body><h1>" + title + "</h1><hr>" result += "<body><h1>" + title + "</h1><hr>"

View 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

View File

@@ -1,6 +1,6 @@
import logging import logging
from .server import Server from .server import Server
from uwsgi import log from uwsgi import log, opt
class UwsgiHandler(logging.Handler): class UwsgiHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None: def emit(self, record: logging.LogRecord) -> None:
@@ -13,7 +13,7 @@ logging.basicConfig(
handlers=[UwsgiHandler()] handlers=[UwsgiHandler()]
) )
server = Server() server = Server(prefix=opt.get('prefix', None))
def application(env, start_response): def application(env, start_response):
return server.handle_request( return server.handle_request(