From 5c69e14d7cfae8da4efab776165cc8e466edcc41 Mon Sep 17 00:00:00 2001
From: retoor <retoor@molodetz.nl>
Date: Sat, 25 Jan 2025 03:46:33 +0100
Subject: [PATCH] Added session support.

---
 cache/crc321300331366.cache                   |   0
 cache/crc322507170282.cache                   |   0
 pyproject.toml                                |   5 +-
 src/snek/app.py                               |  20 +++++++-
 src/snek/docs/__pycache__/app.cpython-312.pyc | Bin 2268 -> 2249 bytes
 src/snek/static/app.js                        |  43 +++++++++++++++++-
 src/snek/static/generic-form.js               |   9 ++--
 src/snek/system/model.py                      |   8 ++++
 src/snek/system/view.py                       |  14 +++++-
 src/snek/templates/base.html                  |   1 +
 src/snek/templates/web.html                   |   5 +-
 src/snek/view/register_form.py                |   6 ++-
 src/snek/view/web.py                          |   2 +
 13 files changed, 100 insertions(+), 13 deletions(-)
 delete mode 100644 cache/crc321300331366.cache
 delete mode 100644 cache/crc322507170282.cache

diff --git a/cache/crc321300331366.cache b/cache/crc321300331366.cache
deleted file mode 100644
index e69de29..0000000
diff --git a/cache/crc322507170282.cache b/cache/crc322507170282.cache
deleted file mode 100644
index e69de29..0000000
diff --git a/pyproject.toml b/pyproject.toml
index cc36846..71e90a7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -21,7 +21,8 @@ dependencies = [
     "gunicorn",
     "imgkit",
     "wkhtmltopdf",
-    "jinja-markdown2",
-    "mistune"
+    "mistune",
+    "aiohttp-session",
+    "cryptography"
 ]
 
diff --git a/src/snek/app.py b/src/snek/app.py
index ab19f42..0abf24b 100644
--- a/src/snek/app.py
+++ b/src/snek/app.py
@@ -17,8 +17,23 @@ from snek.view.login import LoginView
 from snek.view.login_form import LoginFormView
 from snek.view.register import RegisterView
 from snek.view.register_form import RegisterFormView
+from snek.view.status import StatusView
 from snek.view.web import WebView
 
+from aiohttp import web
+from aiohttp_session import setup as session_setup, get_session as session_get, session_middleware
+from aiohttp_session.cookie_storage import EncryptedCookieStorage
+import base64
+
+#base64.urlsafe_b64encode(
+SESSION_KEY = b'c79a0c5fda4b424189c427d28c9f7c34'
+
+@web.middleware
+async def session_middleware(request, handler):
+    setattr(request,"session", await session_get(request))
+    response = await handler(request)
+    return response
+
 
 class Application(BaseApplication):
 
@@ -31,10 +46,13 @@ class Application(BaseApplication):
         super().__init__(
             middlewares=middlewares, template_path=self.template_path, *args, **kwargs
         )
+        session_setup(self,EncryptedCookieStorage(SESSION_KEY))
+        self._middlewares.append(session_middleware)
         self.jinja2_env.add_extension(MarkdownExtension)
         self.setup_router()
         self.setup_services()
 
+    
     def setup_services(self):
         self.services = SimpleNamespace(**get_services(app=self))
         self.mappers = SimpleNamespace(**get_mappers(app=self))
@@ -51,7 +69,7 @@ class Application(BaseApplication):
         self.router.add_view("/about.md", AboutMDView)
         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("/web.html", WebView)
         self.router.add_view("/login.html", LoginView)
         self.router.add_view("/login.json", LoginFormView)
diff --git a/src/snek/docs/__pycache__/app.cpython-312.pyc b/src/snek/docs/__pycache__/app.cpython-312.pyc
index 087bb64a5ffd1d887e3c44a479e90e0aff6c268a..6f355a637d6bd726ec89d969757d5d74527408fc 100644
GIT binary patch
delta 602
zcmZuuOKa3n6h1ePo0yr*qoyV^P!Z`S)s9fK3m?-5SXVxF!YqW?glU{)CfpmDb~T$W
zx@=9szu;g&p?^UHaWxj1A=YI<cO$myM)2Mg!G#y@`ObIG`3@iF9+uyg_4m520Xr+^
zZu8lWUg@nc@*G@nQ2-5&LV!Y{At0WMffz~+Im@L0he|_1Aiy1P<xOz0C%+W=Fo1@%
zfo$a~Pw&|%6}#SJ2Dzmu3j9?k_B(Ax%Xb~pay#Aj?e*AeQ}(zDJ8zbdw2#pqGIC6u
zN)Bn#6t}t@iJH6>=8>NK7Ah#0nBpUCdN@-b%+x=s3;R8BSFkdX`lRExBPU)<vA6Ei
zm=ZC$gm0j;NfR$%lg%X)$sWFd4VKa`$p`$<8kyD6<gL+^eXK}EVGNR97;BKPB-fS8
z_y1SSL-qWyGCQcu9_EYN`mkgVO7@HCS9ae%C|y0sUwhsCE(%rcCkTeNrH(O_tgYf$
zVJzt@%UD0P@lBcRo#QqT3yUN(C-D*wXLzB&!mmv@kra6m{g^Oii&%dfY?bhyRB`;y
zS{z4&@1L<Ks<E5RNQ=u3_5Rs>YS6aVs?iM^dtohf$b;+?h|HvYIGxRHr0UJE<30_%
lI+<sb`=e7Vj75adCny|&`5ESpVD1=SMAr6|6JU_Z{{Z4NoHGCb

delta 634
zcmY*W&ui2`6rRbCNxI$avhKnrpisO8+_mgML2awGqNV7;gDp9jjhnHJn`FaGWZP3O
zJ@g`2Qt%{*7ZH0<`cGIqxXW6$M5|szy!BwEM`uz69hmQZ@5g-e=FLv&Q%QTLX*s~w
z7j<*-!iHw7-e%4d-~t!=Ab^$tITw7<5}6f!DUdBWi%C8T6idmt%(!Z)5D4HpaM2oY
z6)e0GxiA10ToFw52d5@XN3TSY@3oxR>x8$MFLv=gW=c05(sn!Dli{`H7>AT)S+q!S
z=wf0A$H|T7Gf;f<eG69dOe~cgvPe_Z?s7{;k{7}`SV%q#2FxW#!rfeDXioIaiCuMa
zyD4r8dUi;C(DA~^iI-A@mpvL&A|-ir5mu5LXbR~}mz0x7XbhDZrMt;CnlC-0z43+K
zee$79dwIGyswbzFGgtqQel<=FjauKRZM9z4hK;#?WA5$D``Mk@LF3M#KHpog2S#&P
zZmz3)#p-(TKm-*BCQp^=)k1bBe~@O|E#INEg?-<)O-QP|2gu+u$vWc`B^*?q&=?%8
zfkR1=r+%H#WStm)^X5pZI8jt*JF8EN(+<V|jGpjcXEk1jEX8p|B<@ydh}(6#LSr1%
z|I9ojG8aV*UV`Hbcq^%TDd@NleLP9dGne;B#~2)l5W;=X_y`L7pvuTMF#c20q5k;n
JAz&s`_zibgv3>vm

diff --git a/src/snek/static/app.js b/src/snek/static/app.js
index f06ee24..d8d3a8f 100644
--- a/src/snek/static/app.js
+++ b/src/snek/static/app.js
@@ -67,7 +67,7 @@ class Room {
 class InlineAppElement extends HTMLElement {
     
     constructor(){
-        this.
+       // this.
     }
 
 }
@@ -77,6 +77,45 @@ class Page {
 
 }
 
+class RESTClient {
+    debug = true 
+    
+    async get(url, params){
+        params = params ? params : {} 
+        const encodedParams = new URLSearchParams(params);
+        if(encodedParams)
+            url += '?' + encodedParams
+        const response = await fetch(url,{
+            method: 'GET',
+            headers: {
+              'Content-Type': 'application/json'
+            }
+          });
+          const result =  await response.json()
+          if(this.debug){
+            console.debug({url:url,params:params,result:result})
+          }
+        return result 
+    }
+    async post(url, data) {
+    const response = await fetch(url,{
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(data)
+      });
+
+      const result =  await response.json()
+          if(this.debug){
+            console.debug({url:url,params:params,result:result})
+          }
+        return result
+    }
+}
+
+const rest = new RESTClient()
+
 class App {
     rooms = []
     constructor() {
@@ -84,7 +123,9 @@ class App {
 
 
     }
+    async post(url, data){
 
+    }
 
 
 }
\ No newline at end of file
diff --git a/src/snek/static/generic-form.js b/src/snek/static/generic-form.js
index a47c6db..e775bf4 100644
--- a/src/snek/static/generic-form.js
+++ b/src/snek/static/generic-form.js
@@ -282,8 +282,10 @@ class GenericForm extends HTMLElement {
               {
                 const isValid = await me.validate()
                 if(isValid){
-                  const isProcessed = await me.submit()
-                  console.info({processed:isProcessed})
+                  const saveResult = await me.submit()
+                  if(saveResult.redirect_url){
+                    window.location.pathname = saveResult.redirect_url
+                  }
                 }
               }
             }
@@ -315,7 +317,6 @@ class GenericForm extends HTMLElement {
         if(!field.is_valid){
           me.fields[field.name].setInvalid()
           me.fields[field.name].setErrors(field.errors)
-          console.info(field.name,"is invalid")
         }else{
           me.fields[field.name].setValid()
         }
@@ -323,10 +324,8 @@ class GenericForm extends HTMLElement {
         me.fields[field.name].updateAttributes()
       })
       Object.values(form.fields).forEach(field=>{
-        console.info(field.errors)
         me.fields[field.name].setErrors(field.errors)
       })
-      console.info({XX:form})
       return form['is_valid']
     }
     async submit(){
diff --git a/src/snek/system/model.py b/src/snek/system/model.py
index b41f4ba..98729b2 100644
--- a/src/snek/system/model.py
+++ b/src/snek/system/model.py
@@ -212,6 +212,14 @@ class DeletedField(ModelField):
 
 class UUIDField(ModelField):
 
+    @property
+    def value(self):
+        return str(self._value)
+    
+    @value.setter
+    def value(self,val):
+         self._value = str(val)
+
     @property
     def initial_value(self):
         return str(uuid.uuid4())
diff --git a/src/snek/system/view.py b/src/snek/system/view.py
index 1cf5329..8eebcb0 100644
--- a/src/snek/system/view.py
+++ b/src/snek/system/view.py
@@ -5,6 +5,13 @@ from snek.system.markdown import render_markdown
 
 class BaseView(web.View):
 
+    login_required = False
+
+    async def _iter(self):
+        if self.login_required and not self.session.get("logged_in"):
+            return web.HTTPFound("/")
+        return await super()._iter()
+
     @property
     def app(self):
         return self.request.app
@@ -16,6 +23,10 @@ class BaseView(web.View):
     async def json_response(self, data):
         return web.json_response(data)
 
+    @property 
+    def session(self):
+        return self.request.session
+
     async def render_template(self, template_name, context=None):
         if template_name.endswith(".md"):
             response = await self.request.app.render_template(
@@ -46,7 +57,8 @@ class BaseFormView(BaseView):
             # Pass
             pass
         if post.get("action") == "submit" and result["is_valid"]:
-            await self.submit(form)
+            result = await self.submit(form)
+            return await self.json_response(result)
         return await self.json_response(result)
 
     async def submit(self, model=None):
diff --git a/src/snek/templates/base.html b/src/snek/templates/base.html
index e00c886..cb8fc5d 100644
--- a/src/snek/templates/base.html
+++ b/src/snek/templates/base.html
@@ -4,6 +4,7 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>{% block title %}{% endblock %}</title>
+  <script src="/app.js"></script>
   <style>{{ highlight_styles }}</style>
   <link rel="stylesheet" href="/style.css">
    <script src="/fancy-button.js"></script>
diff --git a/src/snek/templates/web.html b/src/snek/templates/web.html
index 0403e1b..5a636f0 100644
--- a/src/snek/templates/web.html
+++ b/src/snek/templates/web.html
@@ -3,12 +3,13 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>Dark Themed Chat Application</title>
+  <title>Snek</title>
+  <script src="/app.js"></script>
   <link rel="stylesheet" href="base.css">
 </head>
 <body>
   <header>
-    <div class="logo">Molodetz Chat</div>
+    <div class="logo">Snek</div>
     <nav>
       <a href="#">Home</a>
       <a href="#">Rooms</a>
diff --git a/src/snek/view/register_form.py b/src/snek/view/register_form.py
index 4c30169..761c7c1 100644
--- a/src/snek/view/register_form.py
+++ b/src/snek/view/register_form.py
@@ -9,4 +9,8 @@ class RegisterFormView(BaseFormView):
         result = await self.app.services.user.register(
             form.email.value, form.username.value, form.password.value
         )
-        print("SUBMITTED:", result)
+        self.request.session["uid"] = result['uid']
+        self.request.session["username"] = result['usernmae']
+        self.request.session["logged_in"] = True
+
+        return dict(redirect_url="/web.html")
diff --git a/src/snek/view/web.py b/src/snek/view/web.py
index d42fcec..63bacab 100644
--- a/src/snek/view/web.py
+++ b/src/snek/view/web.py
@@ -3,5 +3,7 @@ from snek.system.view import BaseView
 
 class WebView(BaseView):
 
+    login_required = True
+
     async def get(self):
         return await self.render_template("web.html")