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)
-
-