added prefix option
This commit is contained in:
@@ -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"
|
||||||
|
@@ -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)
|
||||||
|
@@ -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>"
|
||||||
|
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
|
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(
|
||||||
|
Reference in New Issue
Block a user