This commit is contained in:
retoor 2025-05-10 15:03:50 +02:00
parent dd108c2004
commit f0591d4939
9 changed files with 72 additions and 120 deletions

View File

@ -33,6 +33,7 @@ from snek.view.about import AboutHTMLView, AboutMDView
from snek.view.avatar import AvatarView
from snek.view.docs import DocsHTMLView, DocsMDView
from snek.view.drive import DriveView
from snek.view.drive import DriveApiView
from snek.view.index import IndexView
from snek.view.login import LoginView
from snek.view.logout import LogoutView
@ -178,7 +179,8 @@ class Application(BaseApplication):
self.router.add_view("/threads.html", ThreadsView)
self.router.add_view("/terminal.ws", TerminalSocketView)
self.router.add_view("/terminal.html", TerminalView)
self.router.add_view("/drive.json", DriveView)
self.router.add_view("/drive.json", DriveApiView)
self.router.add_view("/drive.html", DriveView)
self.router.add_view("/drive/{drive}.json", DriveView)
self.router.add_view("/stats.json", StatsView)
self.router.add_view("/user/{user}.html", UserView)

View File

@ -9,6 +9,7 @@ class FileBrowser extends HTMLElement {
}
connectedCallback() {
this.path = this.getAttribute("path") || "";
this.renderShell();
this.load();
}
@ -19,11 +20,11 @@ class FileBrowser extends HTMLElement {
<style>
:host { display:block; font-family: system-ui, sans-serif; box-sizing: border-box; }
nav { display:flex; flex-wrap:wrap; gap:.5rem; margin:.5rem 0; align-items:center; }
button { padding:.35rem .65rem; border:none; border-radius:4px; background:#0074d9; color:#fff; cursor:pointer; font:inherit; }
button { padding:.35rem .65rem; border:none; border-radius:4px; background:#f05a28; color:#fff; cursor:pointer; font:inherit; }
button:disabled { background:#999; cursor:not-allowed; }
.crumb { font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(120px,1fr)); gap:1rem; }
.tile { border:1px solid #ddd; border-radius:8px; padding:.5rem; background:#fafafa; text-align:center; cursor:pointer; transition:box-shadow .2s ease; }
.tile { border:1px solid #f05a28; border-radius:8px; padding:.5rem; background:#000000; text-align:center; cursor:pointer; transition:box-shadow .2s ease; }
.tile:hover { box-shadow:0 2px 8px rgba(0,0,0,.1); }
img.thumb { width:100%; height:90px; object-fit:cover; border-radius:6px; }
.icon { font-size:48px; line-height:90px; }

View File

@ -35,6 +35,7 @@
<div class="logo no-select">{% block header_text %}{% endblock %}</div>
<nav class="no-select" style="overflow:hidden;scroll-behavior:smooth">
<a class="no-select" href="/web.html">🏠</a>
<a class="no-select" href="/drive.html">📂</a>
<a class="no-select" href="/search-user.html">🔍</a>
<a class="no-select" style="display:none" id="install-button" href="#">📥</a>
<a class="no-select" href="/threads.html">👥</a>

View File

@ -0,0 +1,9 @@
{% extends "app.html" %}
{% block header_text %}Drive{% endblock %}
{% block main %}
<div class="container">
<file-manager path="{{path}}" style="flex: 1"></file-manager>
</div>
{% endblock %}

View File

@ -3,43 +3,7 @@
{% block header_text %}<h1><i class="fa-solid fa-plus"></i> Create Repository</h1>{% endblock %}
{% block main %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
.container {
div,input,label,button{
padding-bottom: 15px;
}
}
form {
padding: 2rem;
border-radius: 10px;
}
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
input[type="text"] {
padding: 0.5rem;
border: 1px solid #ccc; border-radius: 5px;
font-size: 1rem;
}
button, a.button {
background: #198754; color: #fff; border: none; border-radius: 5px;
padding: 0.1rem 0.8rem; text-decoration: none; cursor: pointer;
transition: background 0.2s;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
.
.cancel {
background: #6c757d;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
form { padding: 1rem; }
}
</style>
</head>
<body>
{% include 'settings/repositories/form.html' %}
<div class="container">
<form action="/settings/repositories/create.html" method="post">
<div>
@ -52,8 +16,9 @@ button, a.button {
<i class="fa-solid fa-lock"></i> Private
</label>
</div>
<button type="submit"><i class="fa-solid fa-plus"></i> Create</button>
<button onclick="history.back()" class="cancel"><i class="fa-solid fa-arrow-left"></i> Back</button>
<button type="submit"><i class="fa-solid fa-pen"></i> Update</button>
<button onclick="history.back()" class="cancel"><i class="fa-solid fa-arrow-left"></i>Cancel</button>
</form>
</div>
{% endblock %}

View File

@ -3,33 +3,8 @@
{% block header_text %}<h1><i class="fa-solid fa-trash-can"></i> Delete Repository</h1>{% endblock %}
{% block main %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
.repo-name {
font-weight: bold;
font-size: 1.2rem;
margin: 1rem 0;
color: #dc3545;
}
.actions {
display: flex; gap: 1rem; justify-content: left; margin-top: 1.5rem;
}
button {
background: #dc3545; color: #fff;
border: none; border-radius: 5px; padding: 0.6rem 1.2rem;
font-size: 1rem; cursor: pointer;
display: flex; align-items: center; gap: 0.5rem; text-decoration: none; justify-content: center;
transition: background 0.2s;
}
.cancel {
background: #6c757d;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
.confirm-box { padding: 1rem; }
}
</style>
<div class="container">
{% include "settings/repositories/form.html" %}
<div class="container">
<p>Are you sure you want to <strong>delete</strong> the following repository?</p>
<div class="repo-name"><i class="fa-solid fa-book"></i> {{ repository.name }}</div>
<form method="post" style="margin-top:1.5rem;">

View File

@ -0,0 +1,28 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
form {
padding: 2rem;
border-radius: 10px;
div {
padding: 10px;
padding-bottom: 15px
}
}
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
button {
background: #0d6efd; color: #fff;
border: none; border-radius: 5px; padding: 0.6rem 1rem;
cursor: pointer;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
.cancel {
background: #6c757d;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
form { padding: 1rem; }
}
</style>

View File

@ -3,28 +3,8 @@
{% block header_text %}<h1><i class="fa-solid fa-pen"></i> Update Repository</h1>{% endblock %}
{% block main %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
<style>
form {
padding: 2rem;
border-radius: 10px;
}
label { font-weight: bold; display: flex; align-items: center; gap: 0.5rem;}
button {
background: #0d6efd; color: #fff;
border: none; border-radius: 5px; padding: 0.6rem 1rem;
cursor: pointer;
font-size: 1rem; display: inline-flex; align-items: center; gap: 0.4rem;
}
.cancel {
background: #6c757d;
}
@media (max-width: 600px) {
.container { max-width: 98vw; }
form { padding: 1rem; }
}
</style>
<div class="container">
{% include "settings/repositories/form.html" %}
<div class="container">
<form method="post">
<!-- Assume hidden id for backend use -->
<input type="hidden" name="id" value="{{ repository.id }}">

View File

@ -11,47 +11,42 @@ from datetime import datetime
"""Run with: python server.py (Python  3.9)
Visit http://localhost:8080 to try the demo.
"""
from aiohttp import web
from pathlib import Path
import mimetypes, urllib.parse
# ---------- Configuration --------------------------------------------------
BASE_DIR = Path(__file__).parent.resolve()
ROOT_DIR = (BASE_DIR / "storage").resolve() # files shown to the outside world
ASSETS_DIR = (BASE_DIR / "assets").resolve() # JS & demo HTML
ROOT_DIR.mkdir(exist_ok=True)
ASSETS_DIR.mkdir(exist_ok=True)
# ---------- Helpers --------------------------------------------------------
def safe_resolve_path(rel: str) -> Path:
"""Return *absolute* path inside ROOT_DIR or raise FileNotFoundError."""
target = (ROOT_DIR / rel.lstrip("/")).resolve()
if target == ROOT_DIR or ROOT_DIR in target.parents:
return target
raise FileNotFoundError("Unsafe path")
# ---------- API view -------------------------------------------------------
class DriveView(BaseView):
async def get(self):
target = await self.services.user.get_home_folder(self.session.get("uid"))
rel_path = self.request.match_info.get("rel_path", "")
if rel_path:
target = target.joinpath(rel_path)
if not target.exists():
return web.HTTPNotFound(reason="Path not found")
if target.is_dir():
return await self.render_template("drive.html",{"path": rel_path})
if target.is_file():
return web.FileResponse(target)
class DriveApiView(BaseView):
async def get(self):
target = await self.services.user.get_home_folder(self.session.get("uid"))
rel = self.request.query.get("path", "")
offset = int(self.request.query.get("offset", 0))
limit = int(self.request.query.get("limit", 20))
target = await self.services.user.get_home_folder(self.session.get("uid"))
if rel:
target.joinpath(rel)
target = target.joinpath(rel)
if not target.exists():
return web.json_response({"error": "Not found"}, status=404)
# ---- Directory listing -------------------------------------------
if target.is_dir():
entries = []
# Directories first, then files both alphabetical (caseinsensitive)
for p in sorted(target.iterdir(), key=lambda p: (p.is_file(), p.name.lower())):
item_path = (Path(rel) / p.name).as_posix()
mime = mimetypes.guess_type(p.name)[0] if p.is_file() else "inode/directory"
@ -73,10 +68,6 @@ class DriveView(BaseView):
"pagination": {"offset": offset, "limit": limit, "total": total}
})
with open(target, "rb") as f:
content = f.read()
return web.Response(body=content, content_type=mimetypes.guess_type(target.name)[0])
# ---- Single file metadata ----------------------------------------
url = self.request.url.with_path(f"/drive/{urllib.parse.quote(rel)}")
return web.json_response({
"name": target.name,