Branding.

This commit is contained in:
retoor 2025-02-09 02:25:06 +01:00
parent e2a8efe5ca
commit a3cec5bce0
13 changed files with 345 additions and 136 deletions

View File

@ -1,12 +1,36 @@
from snek.system.view import BaseView
# Written by retoor@molodetz.nl
# This source code defines two classes, `AboutHTMLView` and `AboutMDView`, both inheriting from `BaseView`. They asynchronously return rendered templates for HTML and Markdown respectively.
# External Import: `BaseView` from `snek.system.view`
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from snek.system.view import BaseView
class AboutHTMLView(BaseView):
async def get(self):
return await self.render_template("about.html")
class AboutMDView(BaseView):
async def get(self):

View File

@ -1,12 +1,34 @@
from snek.system.view import BaseView
# Written by retoor@molodetz.nl
# This code defines two classes, DocsHTMLView and DocsMDView, which are intended to asynchronously render HTML and Markdown templates respectively. Both classes inherit from the BaseView class.
# Dependencies: BaseView is imported from the "snek.system.view" package.
# MIT License
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from snek.system.view import BaseView
class DocsHTMLView(BaseView):
async def get(self):
return await self.render_template("docs.html")
class DocsMDView(BaseView):
async def get(self):

View File

@ -1,7 +1,17 @@
# Written by retoor@molodetz.nl
# This code defines an asynchronous IndexView class inheriting from BaseView with a method to render an HTML template.
# External imports: BaseView from snek.system.view
# MIT License
from snek.system.view import BaseView
class IndexView(BaseView):
async def get(self):
return await self.render_template("index.html")

View File

@ -1,9 +1,15 @@
from aiohttp import web
# Written by retoor@molodetz.nl
# This source code defines a LoginView class that inherits from BaseFormView and handles user authentication. It checks if a user is logged in, provides a JSON response or renders a login HTML template as needed, and processes form submissions to authenticate users.
# The code imports the LoginForm from snek.form.login and BaseFormView from snek.system.view, both of which are likely custom modules in the system, as well as web from aiohttp for handling HTTP responses.
# MIT License
from aiohttp import web
from snek.form.login import LoginForm
from snek.system.view import BaseFormView
class LoginView(BaseFormView):
form = LoginForm
@ -15,13 +21,14 @@ class LoginView(BaseFormView):
return await self.render_template("login.html")
async def submit(self, form):
if await form.is_valid:
if await form.is_valid():
user = await self.services.user.get(username=form['username'], deleted_at=None)
# Migrate data
await self.services.user.save(user)
self.session["logged_in"] = True
self.session["username"] = user['username']
self.session["uid"] = user["uid"]
self.session["color"] = user["color"]
self.session.update({
"logged_in": True,
"username": user['username'],
"uid": user["uid"],
"color": user["color"]
})
return {"redirect_url": "/web.html"}
return {"is_valid": False}

View File

@ -1,3 +1,12 @@
# Written by retoor@molodetz.nl
# This code defines an asynchronous view for handling a login form. It checks if the form is valid, sets session variables for a logged-in user, and provides a redirect URL if successful.
# Imports: LoginForm from snek.form.login and BaseFormView from snek.system.view
# MIT License
from snek.form.login import LoginForm
from snek.system.view import BaseFormView
@ -6,7 +15,7 @@ class LoginFormView(BaseFormView):
form = LoginForm
async def submit(self, form):
if await form.is_valid:
if await form.is_valid():
self.session["logged_in"] = True
self.session["username"] = form.username.value
self.session["uid"] = form.uid.value

View File

@ -1,10 +1,38 @@
from aiohttp import web
# Written by retoor@molodetz.nl
# This code provides a view for logging out users. It handles GET and POST requests, deletes session information, and redirects the user after logging out.
# This code imports 'web' from the 'aiohttp' library to handle HTTP operations and imports 'BaseView' from 'snek.system.view' to extend a base class for creating views.
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from aiohttp import web
from snek.system.view import BaseView
class LogoutView(BaseView):
redirect_url = "/"
login_required = True

View File

@ -1,11 +1,16 @@
from aiohttp import web
# Written by retoor@molodetz.nl
# This module defines a web view for user registration. It handles GET requests and form submissions for the registration process.
# The code makes use of 'RegisterForm' from 'snek.form.register' for handling registration forms and 'BaseFormView' from 'snek.system.view' for basic view functionalities.
# MIT License
from aiohttp import web
from snek.form.register import RegisterForm
from snek.system.view import BaseFormView
class RegisterView(BaseFormView):
form = RegisterForm
async def get(self):

View File

@ -1,3 +1,30 @@
# Written by retoor@molodetz.nl
# This code defines a `RegisterFormView` class that handles the user registration process by using a form object, a view parent class, and asynchronously submitting the form data to register a user. It then stores the user's session details and provides a redirect URL to a specific page.
# Imports used but not part of the language:
# snek.form.register.RegisterForm, snek.system.view.BaseFormView
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from snek.form.register import RegisterForm
from snek.system.view import BaseFormView
@ -10,7 +37,7 @@ class RegisterFormView(BaseFormView):
form.email.value, form.username.value, form.password.value
)
self.request.session["uid"] = result["uid"]
self.request.session["username"] = result["usernmae"]
self.request.session["username"] = result["username"]
self.request.session["logged_in"] = True
return {"redirect_url": "/web.html"}

View File

@ -1,3 +1,12 @@
# Written by retoor@molodetz.nl
# This source code implements a WebSocket-based RPC (Remote Procedure Call) view that uses asynchronous methods to facilitate real-time communication and services for an authenticated user session in a web application. The class handles WebSocket events, user authentication, and various RPC interactions such as login, message retrieval, and more.
# External imports are used from the aiohttp library for the WebSocket response handling and the snek.system view for the BaseView class.
# MIT License: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions.
from aiohttp import web
from snek.system.view import BaseView
import traceback
@ -16,7 +25,6 @@ class RPCView(BaseView):
def user_uid(self):
return self.view.session.get("uid")
@property
def request(self):
return self.view.request
@ -44,7 +52,6 @@ class RPCView(BaseView):
await self.services.socket.add(self.ws)
async for subscription in self.services.channel_member.find(user_uid=self.view.request.session.get("uid"), deleted_at=None, is_banned=False):
await self.services.socket.subscribe(self.ws, subscription["channel_uid"])
return record
async def search_user(self, query):
@ -59,43 +66,41 @@ class RPCView(BaseView):
record = user.record
del record['password']
del record['deleted_at']
if not user_uid == user["uid"]:
if user_uid != user["uid"]:
del record['email']
return record
async def get_messages(self, channel_uid, offset=0):
self._require_login()
messages = []
async for message in self.services.channel_message.query("SELECT * FROM channel_message ORDER BY created_at DESC LIMIT 60"): #"SELECT uid, channel_uid, user_uid, message, created_at FROM channel_message WHERE channel_uid = :channel_uid ORDER BY created_at DESC LIMIT 30 OFFSET :offset",{"channel_uid":channel_uid,"offset":int(offset)}):
async for message in self.services.channel_message.query("SELECT * FROM channel_message ORDER BY created_at DESC LIMIT 60"):
user = await self.services.user.get(uid=message["user_uid"])
if not user:
print("User not found!", flush=True)
continue
messages.insert(0,dict(
uid=message["uid"],
color=user['color'],
user_uid=message["user_uid"],
channel_uid=message["channel_uid"],
user_nick=user['nick'],
message=message["message"],
created_at=message["created_at"],
html=message['html'],
username=user['username']
))
print("Response messages:",messages,flush=True)
messages.insert(0, {
"uid": message["uid"],
"color": user['color'],
"user_uid": message["user_uid"],
"channel_uid": message["channel_uid"],
"user_nick": user['nick'],
"message": message["message"],
"created_at": message["created_at"],
"html": message['html'],
"username": user['username']
})
return messages
async def get_channels(self):
self._require_login()
channels = []
async for subscription in self.services.channel_member.find(user_uid=self.user_uid, is_banned=False):
channels.append(dict(
name=subscription["label"],
uid=subscription["channel_uid"],
is_moderator=subscription["is_moderator"],
is_read_only=subscription["is_read_only"]
))
channels.append({
"name": subscription["label"],
"uid": subscription["channel_uid"],
"is_moderator": subscription["is_moderator"],
"is_read_only": subscription["is_read_only"]
})
return channels
async def send_message(self, room, message):
@ -103,22 +108,17 @@ class RPCView(BaseView):
await self.services.chat.send(self.user_uid, room, message)
return True
async def echo(self, *args):
self._require_login()
return args
async def query(self, *args):
self._require_login()
print(args,flush=True)
query = args[0]
lowercase = query.lower()
if any(["drop" in lowercase, "alter" in lowercase,"update" in lowercase, "delete" in lowercase, 'replace' in lowercase , 'insert' in lowercase , 'truncate' in lowercase , 'select' not in lowercase]):
if any(keyword in lowercase for keyword in ["drop", "alter", "update", "delete", "replace", "insert", "truncate"]) and 'select' not in lowercase:
raise Exception("Not allowed")
records = [dict(record) async for record in self.services.channel.query(args[0])]
return records
return [dict(record) async for record in self.services.channel.query(args[0])]
async def __call__(self, data):
try:
@ -126,7 +126,7 @@ class RPCView(BaseView):
method_name = data.get("method")
if method_name.startswith("_"):
raise Exception("Not allowed")
args = data.get("args")
args = data.get("args") or []
if hasattr(super(), method_name) or not hasattr(self, method_name):
return await self._send_json({"callId": call_id, "data": "Not allowed"})
method = getattr(self, method_name.replace(".", "_"), None)
@ -136,10 +136,8 @@ class RPCView(BaseView):
try:
result = await method(*args)
except Exception as ex:
result = dict({"exception":str(ex),"traceback":traceback.format_exc()})
result = {"exception": str(ex), "traceback": traceback.format_exc()}
success = False
print(result,flush=True)
#dict(error=ex=str(ex),traceback=traceback.format_exc())
await self._send_json({"callId": call_id, "success": success, "data": result})
except Exception as ex:
await self._send_json({"callId": call_id, "success": False, "data": str(ex)})
@ -150,34 +148,23 @@ class RPCView(BaseView):
async def call_ping(self, callId, *args):
return {"pong": args}
async def get(self):
ws = web.WebSocketResponse()
await ws.prepare(self.request)
if self.request.session.get("logged_in") is True:
if self.request.session.get("logged_in"):
await self.services.socket.add(ws)
async for subscription in self.services.channel_member.find(user_uid=self.request.session.get("uid"), deleted_at=None, is_banned=False):
await self.services.socket.subscribe(ws, subscription["channel_uid"])
#print("Subscribed for: ", subscription["label"],flush=True)
rpc = RPCView.RPCApi(self, ws)
async for msg in ws:
print(msg,flush=True)
if msg.type == web.WSMsgType.TEXT:
try:
await rpc(msg.json())
except Exception as ex:
print(ex,flush=True)
print(traceback.format_exc(),flush=True)
await self.services.socket.delete(ws)
break
elif msg.type == web.WSMsgType.ERROR:
print(f"WebSocket exception {ws.exception()}")
pass
#await self.services.socket.delete(ws)
elif msg.type == web.WSMsgType.CLOSE:
pass
#await self.services.socket.delete(ws)
print("WebSocket connection closed")
return ws

View File

@ -1,5 +1,34 @@
from aiohttp import web
# Written by retoor@molodetz.nl
# This code implements a web view feature for searching users. It handles GET requests to retrieve user data based on a search query and also processes form submissions.
# Imports used that are not part of the Python language:
# - aiohttp: Used to handle asynchronous web server functionalities.
# - snek.form.search_user: Contains the definition for SearchUserForm, a form specific to searching users.
# - snek.system.view: Provides the BaseFormView class to facilitate form-related operations in a web context.
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from aiohttp import web
from snek.form.search_user import SearchUserForm
from snek.system.view import BaseFormView
@ -8,8 +37,6 @@ class SearchUserView(BaseFormView):
form = SearchUserForm
async def get(self):
#if self.session.get("logged_in"):
# return web.HTTPFound("/web.html")
users = []
query = self.request.query.get("query")
if query:
@ -18,10 +45,11 @@ class SearchUserView(BaseFormView):
if self.request.path.endswith(".json"):
return await super().get()
return await self.render_template("search_user.html",dict(users=users,query=query or ''))
return await self.render_template("search_user.html", {"users": users, "query": query or ''})
async def submit(self, form):
if await form.is_valid:
print("YEAAAH\n")
print("YES\n")
return {"redirect_url": "/search-user.html?query=" + form['username']}
return {"is_valid": False}

View File

@ -1,18 +1,44 @@
from snek.system.view import BaseView
# Written by retoor@molodetz.nl
# This code defines an async class-based view called StatusView for handling HTTP GET requests. It fetches user details and their associated channel memberships from a database and returns a JSON response with user information if the user is logged in.
# The code uses an imported module `BaseView`. There are dependencies on the `snek.system.view` module which provides the BaseView class.
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from snek.system.view import BaseView
class StatusView(BaseView):
async def get(self):
memberships = []
user = {}
if self.session.get("uid"):
user = await self.app.services.user.get(uid=self.session.get("uid"))
user_id = self.session.get("uid")
if user_id:
user = await self.app.services.user.get(uid=user_id)
if not user:
return await self.json_response({"error": "User not found"}, status=404)
async for model in self.app.services.channel_member.find(
user_uid=self.session.get("uid"), deleted_at=None, is_banned=False
user_uid=user_id, deleted_at=None, is_banned=False
):
channel = await self.app.services.channel.get(uid=model["channel_uid"])
memberships.append(
@ -33,7 +59,7 @@ class StatusView(BaseView):
"email": user["email"],
"nick": user["nick"],
"uid": user["uid"],
"color": user['color'],
"color": user["color"],
"memberships": memberships,
}

View File

@ -1,3 +1,15 @@
# Written by retoor@molodetz.nl
# This code defines a web application for uploading and retrieving files.
# It includes functionality to upload files through a POST request and retrieve them via a GET request.
# The code uses the following non-standard imports:
# - snek.system.view.BaseView: For extending view functionalities.
# - aiofiles: For asynchronous file operations.
# - aiohttp: For managing web server requests and responses.
# MIT License: This software is licensed under the MIT License, a permissive free software license.
from snek.system.view import BaseView
import aiofiles
import pathlib
@ -28,7 +40,6 @@ class UploadView(BaseView):
print(str(drive), flush=True)
while field := await reader.next():
if field.name == "channel_uid":
channel_uid = await field.text()
continue
@ -44,13 +55,13 @@ class UploadView(BaseView):
while chunk := await field.read_chunk():
await f.write(chunk)
drive_item = await self.services.drive_item.create(
drive["uid"], filename, str(file_path.absolute()), file_path.stat().st_size, file_path.suffix
)
drive_item = await self.services.drive_item.create(drive["uid"],filename,str(file_path.absolute()),file_path.stat().st_size,file_path.suffix)
await self.services.chat.send(self.request.session.get("uid"),channel_uid,f"![{filename}](/drive.bin/{drive_item['uid']})")
await self.services.chat.send(
self.request.session.get("uid"), channel_uid, f"![{filename}](/drive.bin/{drive_item['uid']})"
)
print(drive_item, flush=True)
return web.json_response({"message": "Files uploaded successfully", "files": [str(file) for file in files], "channel_uid": channel_uid})

View File

@ -1,8 +1,33 @@
# Written by retoor@molodetz.nl
# This code defines a WebView class that inherits from BaseView and includes a method for rendering a web template, requiring login access for its usage.
# The code imports the BaseView class from the `snek.system.view` module.
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from snek.system.view import BaseView
class WebView(BaseView):
login_required = True
async def get(self):