Compare commits
No commits in common. "3c3c30a81933be2478d02eb333e76ac21b2167a6" and "b508f872710865c7a235af92d9c88fe8c50c7638" have entirely different histories.
3c3c30a819
...
b508f87271
10 changed files with 76 additions and 151 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
|
|
@ -5,5 +6,5 @@ dist/
|
|||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
public/fonts
|
||||
|
|
|
|||
82
app.py
82
app.py
|
|
@ -5,17 +5,21 @@ import os
|
|||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from flask import Flask, abort, jsonify, render_template, request, send_from_directory
|
||||
from flask import Flask, jsonify, render_template, send_from_directory, abort, request
|
||||
from pygments import highlight
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from pygments.lexers import TextLexer, get_lexer_for_filename
|
||||
from pygments.util import ClassNotFound
|
||||
|
||||
app = Flask(__name__, static_folder=None)
|
||||
|
||||
git_repo_path = str(Path.home() / "dev")
|
||||
gist_path = str(Path.home() / "gists")
|
||||
export_marker = "readme.md"
|
||||
GIT_REPO_PATH = str(Path.home() / "dev")
|
||||
GIST_PATH = str(Path.home() / "gists")
|
||||
EXPORT_MARKER = "readme.md"
|
||||
if getpass.getuser() == "apache":
|
||||
git_repo_path = "/srv/git"
|
||||
gist_path = "/srv/gists"
|
||||
export_marker = "git-daemon-export-ok"
|
||||
GIT_REPO_PATH = "/srv/git"
|
||||
GIST_PATH = "/srv/gists"
|
||||
EXPORT_MARKER = "git-daemon-export-ok"
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -29,11 +33,11 @@ class Repository:
|
|||
def get_repositories():
|
||||
repositories = []
|
||||
|
||||
if not os.path.exists(git_repo_path):
|
||||
if not os.path.exists(GIT_REPO_PATH):
|
||||
return repositories
|
||||
|
||||
for item in os.listdir(git_repo_path):
|
||||
repo_path = os.path.join(git_repo_path, item)
|
||||
for item in os.listdir(GIT_REPO_PATH):
|
||||
repo_path = os.path.join(GIT_REPO_PATH, item)
|
||||
|
||||
if not os.path.isdir(repo_path):
|
||||
continue
|
||||
|
|
@ -44,7 +48,7 @@ def get_repositories():
|
|||
):
|
||||
continue
|
||||
|
||||
exported = os.path.exists(os.path.join(repo_path, export_marker))
|
||||
exported = os.path.exists(os.path.join(repo_path, EXPORT_MARKER))
|
||||
|
||||
description = "No description available"
|
||||
description_file = os.path.join(repo_path, "description")
|
||||
|
|
@ -78,24 +82,6 @@ def get_repositories():
|
|||
return repositories
|
||||
|
||||
|
||||
@app.route("/gist/<path:filename>")
|
||||
def serve_gist(filename):
|
||||
base = Path(gist_path).resolve()
|
||||
target = (base / filename).resolve()
|
||||
if base not in target.parents and base != target:
|
||||
abort(404)
|
||||
if not target.exists() or not target.is_file():
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
content = target.read_text(encoding="utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return "Binary file cannot be displayed", 400
|
||||
|
||||
ext = target.suffix.lstrip(".") or "plaintext"
|
||||
return render_template("gist.html", filename=filename, code=content, lang=ext)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
repositories = [repo for repo in get_repositories() if repo.exported]
|
||||
|
|
@ -112,6 +98,43 @@ def serve_scripts(filename):
|
|||
return send_from_directory("scripts", filename)
|
||||
|
||||
|
||||
@app.route("/gist/<path:filename>")
|
||||
def serve_gist(filename):
|
||||
base = Path(GIST_PATH).resolve()
|
||||
target = (base / filename).resolve()
|
||||
if base not in target.parents and base != target:
|
||||
abort(404)
|
||||
|
||||
if not target.exists() or not target.is_file():
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
content = target.read_text(encoding="utf-8")
|
||||
except UnicodeDecodeError:
|
||||
return "Binary file cannot be displayed", 400
|
||||
except OSError:
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
lexer = get_lexer_for_filename(target.name)
|
||||
except ClassNotFound:
|
||||
lexer = TextLexer()
|
||||
|
||||
formatter = HtmlFormatter(
|
||||
style="default",
|
||||
cssclass="highlight",
|
||||
linenos=False,
|
||||
noclasses=True,
|
||||
cssstyles="padding: 20px; font-size: 18px; background-color: #f8f8f8;",
|
||||
)
|
||||
highlighted = highlight(content, lexer, formatter)
|
||||
highlighted = highlighted.replace(
|
||||
'<td class="code">', '<td class="code" style="padding-left: 20px;">'
|
||||
)
|
||||
|
||||
return render_template("gist.html", filename=filename, highlighted_code=highlighted)
|
||||
|
||||
|
||||
@app.route("/<path:filename>")
|
||||
def serve_gist_root(filename):
|
||||
head = filename.split("/", 1)[0]
|
||||
|
|
@ -143,3 +166,4 @@ def get_repo(repo_id):
|
|||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||
|
||||
|
|
|
|||
0
file
Normal file
0
file
Normal file
|
|
@ -7,4 +7,5 @@ requires-python = ">=3.11"
|
|||
dependencies = [
|
||||
"flask>=3.1.1",
|
||||
"gunicorn>=23.0.0",
|
||||
"pygments>=2.19.1",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
# git server ui
|
||||
|
||||
the ui for my git server.
|
||||
|
||||
## Deprecation Notice
|
||||
|
||||
Go to barrettruth.com/git instead.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ function renderRepoDescription(repoLink) {
|
|||
|
||||
const cloneUrl = document.createElement("div");
|
||||
cloneUrl.style.marginTop = "15px";
|
||||
cloneUrl.innerHTML = `<code>git clone git@git.barrettruth.com:${repoName}.git</code>`;
|
||||
cloneUrl.innerHTML = `<code>git clone https://git.barrettruth.com/git/${repoName}.git</code>`;
|
||||
|
||||
post.appendChild(cloneUrl);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +1,4 @@
|
|||
@font-face {
|
||||
font-family: "Apercu Mono Pro";
|
||||
src: url("/public/fonts/apercu-mono/ApercuMonoProRegular.ttf")
|
||||
format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono Pro";
|
||||
src: url("/public/fonts/apercu-mono/ApercuMonoProMedium.ttf")
|
||||
format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono Pro";
|
||||
src: url("/public/fonts/apercu-mono/ApercuMonoProLight.ttf")
|
||||
format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Apercu Mono Pro";
|
||||
src: url("/public/fonts/apercu-mono/ApercuMonoProBold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
/* Fonts will be loaded from system */
|
||||
|
||||
html,
|
||||
body {
|
||||
|
|
@ -79,10 +50,11 @@ li {
|
|||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
/* Code styling */
|
||||
:not(pre) > code {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
margin: 0 5px;
|
||||
font-size: 0.8em;
|
||||
font-size: 0.8em; /* Match the font size in barrettruth.com */
|
||||
white-space: nowrap;
|
||||
border: 1px solid #e1e1e1;
|
||||
background-color: #f4f4f4;
|
||||
|
|
@ -92,44 +64,3 @@ li {
|
|||
max-width: 100%;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: "Apercu Mono Pro", "Courier New", monospace;
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.5;
|
||||
background: transparent;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-ln-numbers {
|
||||
text-align: right;
|
||||
color: #999;
|
||||
border-right: 1em solid #ffffff;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
main h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.gist-outer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gist-box {
|
||||
display: inline-block;
|
||||
min-width: 60vw;
|
||||
max-width: 80vw;
|
||||
width: max-content;
|
||||
border: 1px solid #000;
|
||||
border-radius: 0;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hljs-ln-numbers {
|
||||
text-align: right;
|
||||
color: #999;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,4 +109,3 @@ a {
|
|||
margin-top: 10px;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,55 +7,17 @@
|
|||
<link rel="stylesheet" href="/styles/common.css" />
|
||||
<link rel="stylesheet" href="/styles/index.css" />
|
||||
<link rel="icon" type="image/webp" href="/public/logo.webp" />
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"
|
||||
/>
|
||||
|
||||
<title>{{ filename }}</title>
|
||||
</head>
|
||||
<body class="graph-background">
|
||||
<site-header></site-header>
|
||||
|
||||
<main class="main" style="flex-direction: column; align-items: center">
|
||||
<h2
|
||||
style="
|
||||
font-weight: normal;
|
||||
font-size: 3em;
|
||||
align-self: flex-start;
|
||||
margin-left: 40px;
|
||||
margin-bottom: 10px;
|
||||
"
|
||||
>
|
||||
{{ filename }}
|
||||
</h2>
|
||||
|
||||
<div class="gist-outer">
|
||||
<div class="gist-box">
|
||||
<pre
|
||||
class="line-numbers"
|
||||
><code class="language-{{ lang }}">{{ code | e }}</code></pre>
|
||||
</div>
|
||||
<main class="main" style="flex-direction: column; align-items: center;">
|
||||
<h2 style="font-weight: normal; font-size: 3em; align-self: flex-start; margin-left: 40px;">{{ filename }}</h2>
|
||||
<div style="padding: 40px;">
|
||||
{{ highlighted_code|safe }}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<site-footer></site-footer>
|
||||
|
||||
<script src="/scripts/common.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/styles/line-numbers.min.css"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js"></script>
|
||||
<script>
|
||||
hljs.highlightAll();
|
||||
hljs.initLineNumbersOnLoad();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
13
uv.lock
generated
13
uv.lock
generated
|
|
@ -1,5 +1,5 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
revision = 2
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -56,12 +56,14 @@ source = { virtual = "." }
|
|||
dependencies = [
|
||||
{ name = "flask" },
|
||||
{ name = "gunicorn" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "flask", specifier = ">=3.1.1" },
|
||||
{ name = "gunicorn", specifier = ">=23.0.0" },
|
||||
{ name = "pygments", specifier = ">=2.19.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -154,6 +156,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.1.3"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue