Progress.

This commit is contained in:
retoor 2025-04-03 08:34:25 +02:00
parent 81479e7058
commit d10768403d
11 changed files with 80 additions and 93 deletions

View File

@ -1,3 +1 @@

View File

@ -1,5 +1,6 @@
from aiohttp import web
from aiohttp import web
from snek.app import Application
if __name__ == '__main__':
web.run_app(Application(), port=8081,host='0.0.0.0')
if __name__ == "__main__":
web.run_app(Application(), port=8081, host="0.0.0.0")

View File

@ -3,6 +3,7 @@ import logging
import pathlib
import time
import uuid
from snek.view.threads import ThreadsView
logging.basicConfig(level=logging.DEBUG)
@ -24,7 +25,7 @@ from snek.service import get_services
from snek.system import http
from snek.system.cache import Cache
from snek.system.markdown import MarkdownExtension
from snek.system.middleware import cors_middleware
from snek.system.middleware import auth_middleware, cors_middleware
from snek.system.profiler import profiler_handler
from snek.system.template import EmojiExtension, LinkifyExtension, PythonExtension
from snek.view.about import AboutHTMLView, AboutMDView
@ -37,12 +38,13 @@ from snek.view.logout import LogoutView
from snek.view.register import RegisterView
from snek.view.rpc import RPCView
from snek.view.search_user import SearchUserView
from snek.view.settings.index import SettingsIndexView
from snek.view.settings.profile import SettingsProfileView
from snek.view.status import StatusView
from snek.view.terminal import TerminalSocketView, TerminalView
from snek.view.upload import UploadView
from snek.view.web import WebView
from snek.webdav import WebdavApplication
from snek.view.settings import SettingsView
SESSION_KEY = b"c79a0c5fda4b424189c427d28c9f7c34"
@ -76,6 +78,7 @@ class Application(BaseApplication):
session_setup(self, EncryptedCookieStorage(SESSION_KEY))
self.tasks = asyncio.Queue()
self._middlewares.append(session_middleware)
self._middlewares.append(auth_middleware)
self.jinja2_env.add_extension(MarkdownExtension)
self.jinja2_env.add_extension(LinkifyExtension)
self.jinja2_env.add_extension(PythonExtension)
@ -138,7 +141,9 @@ class Application(BaseApplication):
self.router.add_view("/docs.html", DocsHTMLView)
self.router.add_view("/docs.md", DocsMDView)
self.router.add_view("/status.json", StatusView)
self.router.add_view("/settings.html", SettingsView)
self.router.add_view("/settings/index.html", SettingsIndexView)
self.router.add_view("/settings/profile.html", SettingsProfileView)
self.router.add_view("/settings/profile.json", SettingsProfileView)
self.router.add_view("/web.html", WebView)
self.router.add_view("/login.html", LoginView)
self.router.add_view("/login.json", LoginView)
@ -189,8 +194,8 @@ class Application(BaseApplication):
channels = []
if not context:
context = {}
context['rid'] = str(uuid.uuid4())
context["rid"] = str(uuid.uuid4())
if request.session.get("uid"):
async for subscribed_channel in self.services.channel_member.find(
user_uid=request.session.get("uid"), deleted_at=None, is_banned=False

View File

@ -7,6 +7,7 @@ from snek.mapper.drive import DriveMapper
from snek.mapper.drive_item import DriveItemMapper
from snek.mapper.notification import NotificationMapper
from snek.mapper.user import UserMapper
from snek.mapper.user_property import UserPropertyMapper
from snek.system.object import Object
@ -21,6 +22,7 @@ def get_mappers(app=None):
"notification": NotificationMapper(app=app),
"drive_item": DriveItemMapper(app=app),
"drive": DriveMapper(app=app),
"user_property": UserPropertyMapper(app=app),
}
)

View File

@ -5,7 +5,11 @@ from snek.model.channel_member import ChannelMemberModel
# from snek.model.channel_message import ChannelMessageModel
from snek.model.channel_message import ChannelMessageModel
from snek.model.drive import DriveModel
from snek.model.drive_item import DriveItemModel
from snek.model.notification import NotificationModel
from snek.model.user import UserModel
from snek.model.user_property import UserPropertyModel
from snek.system.object import Object
@ -17,6 +21,10 @@ def get_models():
"channel_member": ChannelMemberModel,
"channel": ChannelModel,
"channel_message": ChannelMessageModel,
"drive_item": DriveItemModel,
"drive": DriveModel,
"notification": NotificationModel,
"user_property": UserPropertyModel,
}
)

View File

@ -28,6 +28,16 @@ async def cors_allow_middleware(request, handler):
return response
@web.middleware
async def auth_middleware(request, handler):
request["user"] = None
if request.session.get("uid") and request.session.get("logged_in"):
request["user"] = await request.app.services.user.get(
uid=request.app.session.get("uid")
)
return await handler(request)
@web.middleware
async def cors_middleware(request, handler):
if request.headers.get("Allow"):

View File

@ -31,7 +31,7 @@
<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>
<a class="no-select" href="#">⚙️</a>
<a class="no-select" href="/settings/index.html">⚙️</a>
<a class="no-select" href="/logout.html">🔒</a>
</nav>

View File

@ -13,24 +13,14 @@
{% endblock %}
{% block main %}
{% block logo %}
<h1>Setting page</h1>
<div id="profile_description"></div>
{% endblock %}
{% block main %}
<script type="module">
require.config({ paths: { 'vs': 'https://cdn.bootcdn.net/ajax/libs/monaco-editor/0.20.0/min/vs' } });
require(['vs/editor/editor.main'], function () {
var editor = monaco.editor.create(document.getElementById('profile_description'), {
value: phpCode,
language: 'php'
});
})
</script>
{% endblock main %}

View File

@ -1,14 +0,0 @@
<style>
.channel-list-item-highlight {
/* xxx */
}
</style>
<aside class="sidebar" id="channelSidebar">
<h2>Settings</h2>
<ul>
<li><a class="no-select" href="/settings.html">Profile</a></li>
<li><a class="no-select" href="/settings-notifications.html">Notifications</a></li>
<li><a class="no-select" href="/settings-privacy.html">Privacy</a></li>
</ul>
</aside>

View File

@ -1,8 +0,0 @@
from snek.system.view import BaseView
class SettingsView(BaseView):
login_required = True
async def get(self):
return await self.render_template('settings.html')

View File

@ -1,9 +1,8 @@
import logging
import pathlib
logging.basicConfig(level=logging.DEBUG)
import asyncio
import base64
import datetime
import mimetypes
@ -21,7 +20,7 @@ class WebdavApplication(aiohttp.web.Application):
def __init__(self, parent, *args, **kwargs):
super().__init__(*args, **kwargs)
self.locks = {}
self.router.add_route("OPTIONS", "/{filename:.*}", self.handle_options)
self.router.add_route("GET", "/{filename:.*}", self.handle_get)
self.router.add_route("PUT", "/{filename:.*}", self.handle_put)
@ -31,22 +30,21 @@ class WebdavApplication(aiohttp.web.Application):
self.router.add_route("COPY", "/{filename:.*}", self.handle_copy)
self.router.add_route("PROPFIND", "/{filename:.*}", self.handle_propfind)
self.router.add_route("PROPPATCH", "/{filename:.*}", self.handle_proppatch)
#self.router.add_route("LOCK", "/{filename:.*}", self.handle_lock)
#self.router.add_route("UNLOCK", "/{filename:.*}", self.handle_unlock)
# self.router.add_route("LOCK", "/{filename:.*}", self.handle_lock)
# self.router.add_route("UNLOCK", "/{filename:.*}", self.handle_unlock)
self.parent = parent
@property
@property
def db(self):
return self.parent.db
@property
@property
def services(self):
return self.parent.services
return self.parent.services
async def authenticate(self, request):
#session = request.session
#if session.get('uid'):
# session = request.session
# if session.get('uid'):
# request['user'] = await self.services.user.get(uid=session['uid'])
# try:
# request['home'] = await self.services.user.get_home_folder(user_uid=request['user']['uid'])
@ -60,13 +58,17 @@ class WebdavApplication(aiohttp.web.Application):
encoded_creds = auth_header.split("Basic ")[1]
decoded_creds = base64.b64decode(encoded_creds).decode()
username, password = decoded_creds.split(":", 1)
request['user'] = await self.services.user.authenticate(username=username, password=password)
request["user"] = await self.services.user.authenticate(
username=username, password=password
)
try:
request['home'] = await self.services.user.get_home_folder(request['user']['uid'])
request["home"] = await self.services.user.get_home_folder(
request["user"]["uid"]
)
except Exception as ex:
print(ex)
pass
return request['user']
return request["user"]
async def handle_get(self, request):
if not await self.authenticate(request):
@ -75,7 +77,7 @@ class WebdavApplication(aiohttp.web.Application):
)
requested_path = request.match_info.get("filename", "")
abs_path = request['home'] / requested_path
abs_path = request["home"] / requested_path
if not abs_path.exists():
return aiohttp.web.Response(status=404, text="File not found")
@ -95,7 +97,7 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
file_path = request['home'] / request.match_info["filename"]
file_path = request["home"] / request.match_info["filename"]
file_path.parent.mkdir(parents=True, exist_ok=True)
async with aiofiles.open(file_path, "wb") as f:
while chunk := await request.content.read(1024):
@ -107,7 +109,7 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
file_path = request['home'] / request.match_info["filename"]
file_path = request["home"] / request.match_info["filename"]
if file_path.is_file():
file_path.unlink()
return aiohttp.web.Response(status=204)
@ -121,7 +123,7 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
dir_path = request['home'] / request.match_info["filename"]
dir_path = request["home"] / request.match_info["filename"]
if dir_path.exists():
return aiohttp.web.Response(status=405, text="Directory already exists")
dir_path.mkdir(parents=True, exist_ok=True)
@ -132,8 +134,8 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
src_path = request['home'] / request.match_info["filename"]
dest_path = request['home'] / request.headers.get("Destination", "").replace(
src_path = request["home"] / request.match_info["filename"]
dest_path = request["home"] / request.headers.get("Destination", "").replace(
"http://localhost:8080/", ""
)
if not src_path.exists():
@ -146,8 +148,8 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
src_path = request['home'] / request.match_info["filename"]
dest_path = request['home'] / request.headers.get("Destination", "").replace(
src_path = request["home"] / request.match_info["filename"]
dest_path = request["home"] / request.headers.get("Destination", "").replace(
"http://localhost:8080/", ""
)
if not src_path.exists():
@ -188,14 +190,13 @@ class WebdavApplication(aiohttp.web.Application):
statvfs = os.statvfs(path)
return statvfs.f_bavail * statvfs.f_frsize
async def create_node(self, request, response_xml, full_path, depth):
requested_path = request.match_info.get("filename", "")
request.match_info.get("filename", "")
abs_path = pathlib.Path(full_path)
relative_path = str(full_path.relative_to(request['home']))
relative_path = str(full_path.relative_to(request["home"]))
href_path = f"{relative_path}".strip("/")
#href_path = href_path.replace("./","/")
# href_path = href_path.replace("./","/")
href_path = href_path.replace("//", "/")
response = etree.SubElement(response_xml, "{DAV:}response")
href = etree.SubElement(response, "{DAV:}href")
@ -213,7 +214,7 @@ class WebdavApplication(aiohttp.web.Application):
else self.get_directory_size(full_path)
)
etree.SubElement(prop, "{DAV:}quota-available-bytes").text = str(
self.get_disk_free_space(request['home'])
self.get_disk_free_space(request["home"])
)
etree.SubElement(prop, "{DAV:}getlastmodified").text = last_modified
etree.SubElement(prop, "{DAV:}displayname").text = full_path.name
@ -222,10 +223,10 @@ class WebdavApplication(aiohttp.web.Application):
if full_path.is_file():
etree.SubElement(prop, "{DAV:}contenttype").text = mimetype
etree.SubElement(prop, "{DAV:}getcontentlength").text = str(
full_path.stat().st_size
if full_path.is_file()
else self.get_directory_size(full_path)
)
full_path.stat().st_size
if full_path.is_file()
else self.get_directory_size(full_path)
)
supported_lock = etree.SubElement(prop, "{DAV:}supportedlock")
lock_entry_1 = etree.SubElement(supported_lock, "{DAV:}lockentry")
lock_scope_1 = etree.SubElement(lock_entry_1, "{DAV:}lockscope")
@ -238,13 +239,10 @@ class WebdavApplication(aiohttp.web.Application):
lock_type_2 = etree.SubElement(lock_entry_2, "{DAV:}locktype")
etree.SubElement(lock_type_2, "{DAV:}write")
etree.SubElement(propstat, "{DAV:}status").text = "HTTP/1.1 200 OK"
if abs_path.is_dir() and depth != -1:
for item in abs_path.iterdir():
await self.create_node(request,response_xml, item, depth - 1)
await self.create_node(request, response_xml, item, depth - 1)
async def handle_propfind(self, request):
if not await self.authenticate(request):
@ -257,14 +255,13 @@ class WebdavApplication(aiohttp.web.Application):
depth = int(request.headers.get("Depth", "0"))
except ValueError:
pass
requested_path = request.match_info.get("filename", "")
abs_path = request['home'] / requested_path
requested_path = request.match_info.get("filename", "")
abs_path = request["home"] / requested_path
if not abs_path.exists():
return aiohttp.web.Response(status=404, text="Directory not found")
nsmap = {"D": "DAV:"}
response_xml = etree.Element("{DAV:}multistatus", nsmap=nsmap)
directories = [requested_path]
await self.create_node(request, response_xml, abs_path, depth)
xml_output = etree.tostring(
@ -286,9 +283,9 @@ class WebdavApplication(aiohttp.web.Application):
return aiohttp.web.Response(
status=401, headers={"WWW-Authenticate": 'Basic realm="WebDAV"'}
)
resource = request.match_info.get("filename", "/")
request.match_info.get("filename", "/")
lock_id = str(uuid.uuid4())
#self.locks[resource] = lock_id
# self.locks[resource] = lock_id
xml_response = self.generate_lock_response(lock_id)
headers = {
"Lock-Token": f"opaquelocktoken:{lock_id}",
@ -341,8 +338,8 @@ class WebdavApplication(aiohttp.web.Application):
)
requested_path = request.match_info.get("filename", "")
print(requested_path)
abs_path = request['home'] / requested_path
print(requested_path)
abs_path = request["home"] / requested_path
if not abs_path.exists():
return aiohttp.web.Response(status=404, text="File not found")
@ -363,5 +360,3 @@ class WebdavApplication(aiohttp.web.Application):
}
return aiohttp.web.Response(status=200, headers=headers)