diff --git a/src/snek/__init__.py b/src/snek/__init__.py index b28b04f..8b13789 100644 --- a/src/snek/__init__.py +++ b/src/snek/__init__.py @@ -1,3 +1 @@ - - diff --git a/src/snek/__main__.py b/src/snek/__main__.py index 30f0209..692ad68 100644 --- a/src/snek/__main__.py +++ b/src/snek/__main__.py @@ -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") diff --git a/src/snek/app.py b/src/snek/app.py index c6c2e2f..25913b9 100644 --- a/src/snek/app.py +++ b/src/snek/app.py @@ -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 diff --git a/src/snek/mapper/__init__.py b/src/snek/mapper/__init__.py index 96053ea..2d4b12c 100644 --- a/src/snek/mapper/__init__.py +++ b/src/snek/mapper/__init__.py @@ -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), } ) diff --git a/src/snek/model/__init__.py b/src/snek/model/__init__.py index c87d39c..a1009a5 100644 --- a/src/snek/model/__init__.py +++ b/src/snek/model/__init__.py @@ -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, } ) diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py index 1a6c7e6..3a9a055 100644 --- a/src/snek/system/middleware.py +++ b/src/snek/system/middleware.py @@ -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"): diff --git a/src/snek/templates/app.html b/src/snek/templates/app.html index 5ab68c4..b05eb21 100644 --- a/src/snek/templates/app.html +++ b/src/snek/templates/app.html @@ -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> diff --git a/src/snek/templates/settings.html b/src/snek/templates/settings.html index cfb186c..1ceb629 100644 --- a/src/snek/templates/settings.html +++ b/src/snek/templates/settings.html @@ -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 %} diff --git a/src/snek/templates/sidebar_settings.html b/src/snek/templates/sidebar_settings.html deleted file mode 100644 index 8e18412..0000000 --- a/src/snek/templates/sidebar_settings.html +++ /dev/null @@ -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> diff --git a/src/snek/view/settings.py b/src/snek/view/settings.py deleted file mode 100644 index fe181f2..0000000 --- a/src/snek/view/settings.py +++ /dev/null @@ -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') diff --git a/src/snek/webdav.py b/src/snek/webdav.py index 9a2b9e4..0068fcc 100755 --- a/src/snek/webdav.py +++ b/src/snek/webdav.py @@ -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) - -