Formatting.
This commit is contained in:
		
							parent
							
								
									18b76ebd5e
								
							
						
					
					
						commit
						9b93403a93
					
				| @ -1,9 +1,10 @@ | ||||
| import pathlib | ||||
| from types import SimpleNamespace | ||||
| 
 | ||||
| from aiohttp import web | ||||
| from app.app import Application as BaseApplication | ||||
| 
 | ||||
| from snek.docs.app import Application as DocsApplication | ||||
| from app.cache import time_cache_async | ||||
| from snek.mapper import get_mappers | ||||
| from snek.service import get_services | ||||
| from snek.system import http | ||||
| @ -17,7 +18,6 @@ from snek.view.login_form import LoginFormView | ||||
| from snek.view.register import RegisterView | ||||
| from snek.view.register_form import RegisterFormView | ||||
| from snek.view.web import WebView | ||||
| from types import SimpleNamespace | ||||
| 
 | ||||
| 
 | ||||
| class Application(BaseApplication): | ||||
| @ -60,7 +60,10 @@ class Application(BaseApplication): | ||||
|         self.router.add_get("/http-get", self.handle_http_get) | ||||
|         self.router.add_get("/http-photo", self.handle_http_photo) | ||||
| 
 | ||||
|         self.add_subapp("/docs", DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs"))) | ||||
|         self.add_subapp( | ||||
|             "/docs", | ||||
|             DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")), | ||||
|         ) | ||||
| 
 | ||||
|     async def handle_test(self, request): | ||||
| 
 | ||||
| @ -80,8 +83,7 @@ class Application(BaseApplication): | ||||
|             body=path.read_bytes(), headers={"Content-Type": "image/png"} | ||||
|         ) | ||||
| 
 | ||||
|      | ||||
|     #@time_cache_async(60) | ||||
|     # @time_cache_async(60) | ||||
|     async def render_template(self, template, request, context=None): | ||||
|         return await super().render_template(template, request, context) | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| from snek.system.form import Form, HTMLElement,FormInputElement,FormButtonElement | ||||
| from snek.system.form import Form, FormButtonElement, FormInputElement, HTMLElement | ||||
| 
 | ||||
| 
 | ||||
| class LoginForm(Form): | ||||
| 
 | ||||
| @ -11,14 +12,16 @@ class LoginForm(Form): | ||||
|         max_length=20, | ||||
|         regex=r"^[a-zA-Z0-9_]+$", | ||||
|         place_holder="Username", | ||||
|         type="text" | ||||
|         type="text", | ||||
|     ) | ||||
|     password = FormInputElement( | ||||
|         name="password", | ||||
|         required=True, | ||||
|         regex=r"^[a-zA-Z0-9_.+-]{6,}", | ||||
|         type="password", | ||||
|         place_holder="Password", | ||||
|     ) | ||||
|     password = FormInputElement(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}",type="password",place_holder="Password") | ||||
| 
 | ||||
|     action = FormButtonElement( | ||||
|         name="action", | ||||
|         value="submit", | ||||
|         text="Login", | ||||
|         type="button" | ||||
|         name="action", value="submit", text="Login", type="button" | ||||
|     ) | ||||
|     | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| from snek.system.form import Form, HTMLElement,FormInputElement,FormButtonElement | ||||
| from snek.system.form import Form, FormButtonElement, FormInputElement, HTMLElement | ||||
| 
 | ||||
| 
 | ||||
| class UsernameField(FormInputElement): | ||||
| 
 | ||||
| @ -9,6 +10,7 @@ class UsernameField(FormInputElement): | ||||
|             result.append("Username is not available.") | ||||
|         return result | ||||
| 
 | ||||
| 
 | ||||
| class RegisterForm(Form): | ||||
| 
 | ||||
|     title = HTMLElement(tag="h1", text="Register") | ||||
| @ -20,21 +22,23 @@ class RegisterForm(Form): | ||||
|         max_length=20, | ||||
|         regex=r"^[a-zA-Z0-9_]+$", | ||||
|         place_holder="Username", | ||||
|         type="text" | ||||
|         type="text", | ||||
|     ) | ||||
|     email = FormInputElement( | ||||
|         name="email", | ||||
|         required=False, | ||||
|         regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", | ||||
|         place_holder="Email address", | ||||
|         type="email" | ||||
|         type="email", | ||||
|     ) | ||||
|     password = FormInputElement( | ||||
|         name="password", | ||||
|         required=True, | ||||
|         regex=r"^[a-zA-Z0-9_.+-]{6,}", | ||||
|         type="password", | ||||
|         place_holder="Password", | ||||
|     ) | ||||
|     password = FormInputElement(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}",type="password",place_holder="Password") | ||||
| 
 | ||||
|     action = FormButtonElement( | ||||
|         name="action", | ||||
|         value="submit", | ||||
|         text="Register", | ||||
|         type="button" | ||||
|         name="action", value="submit", text="Register", type="button" | ||||
|     ) | ||||
| 
 | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| import functools | ||||
| 
 | ||||
| from snek.mapper.user import UserMapper | ||||
| 
 | ||||
| 
 | ||||
| @functools.cache | ||||
| def get_mappers(app=None): | ||||
|     return dict( | ||||
|         user=UserMapper(app=app) | ||||
|     return {"user": UserMapper(app=app)} | ||||
| 
 | ||||
|     ) | ||||
| 
 | ||||
| def get_mapper(name, app=None): | ||||
|     return get_mappers(app=app)[name] | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.mapper import BaseMapper | ||||
| from snek.model.user import UserModel | ||||
| from snek.system.mapper import BaseMapper | ||||
| 
 | ||||
| 
 | ||||
| class UserMapper(BaseMapper): | ||||
|     table_name = "user" | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| from snek.model.user import UserModel  | ||||
| import functools | ||||
| 
 | ||||
| from snek.model.user import UserModel | ||||
| 
 | ||||
| 
 | ||||
| @functools.cache | ||||
| def get_models(): | ||||
|     return dict( | ||||
|         user=UserModel | ||||
|     return {"user": UserModel} | ||||
| 
 | ||||
|     ) | ||||
| 
 | ||||
| def get_model(name): | ||||
|     return get_models()[name] | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| from snek.system.model import BaseModel,ModelField | ||||
| from snek.system.model import BaseModel, ModelField | ||||
| 
 | ||||
| 
 | ||||
| class UserModel(BaseModel): | ||||
| 
 | ||||
| @ -12,8 +13,6 @@ class UserModel(BaseModel): | ||||
|     email = ModelField( | ||||
|         name="email", | ||||
|         required=False, | ||||
|         regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$" | ||||
|         regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", | ||||
|     ) | ||||
|     password = ModelField(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}") | ||||
| 
 | ||||
| 
 | ||||
|     password = ModelField(name="password", required=True, regex=r"^[a-zA-Z0-9_.+-]{6,}") | ||||
|  | ||||
| @ -1,12 +1,13 @@ | ||||
| from snek.service.user import UserService  | ||||
| import functools | ||||
| 
 | ||||
| from snek.service.user import UserService | ||||
| 
 | ||||
| 
 | ||||
| @functools.cache | ||||
| def get_services(app): | ||||
| 
 | ||||
|     return dict( | ||||
|         user = UserService(app=app) | ||||
|     return {"user": UserService(app=app)} | ||||
| 
 | ||||
| 
 | ||||
|     ) | ||||
| def get_service(name, app=None): | ||||
|     return get_services(app=app)[name] | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.service import BaseService  | ||||
| from snek.system import security | ||||
| from snek.system.service import BaseService | ||||
| 
 | ||||
| 
 | ||||
| class UserService(BaseService): | ||||
|     mapper_name = "user" | ||||
| @ -14,4 +15,3 @@ class UserService(BaseService): | ||||
|         if await self.save(model): | ||||
|             return model | ||||
|         raise Exception(f"Failed to create user: {model.errors}.") | ||||
|          | ||||
| @ -1,8 +1,8 @@ | ||||
| 
 | ||||
| import functools | ||||
| 
 | ||||
| cache = functools.cache | ||||
| 
 | ||||
| 
 | ||||
| def async_cache(func): | ||||
|     cache = {} | ||||
| 
 | ||||
|  | ||||
| @ -26,8 +26,19 @@ | ||||
| 
 | ||||
| from snek.system import model | ||||
| 
 | ||||
| 
 | ||||
| class HTMLElement(model.ModelField): | ||||
|     def __init__(self, id=None, tag="div", name=None, html=None, class_name=None, text=None, *args, **kwargs): | ||||
|     def __init__( | ||||
|         self, | ||||
|         id=None, | ||||
|         tag="div", | ||||
|         name=None, | ||||
|         html=None, | ||||
|         class_name=None, | ||||
|         text=None, | ||||
|         *args, | ||||
|         **kwargs, | ||||
|     ): | ||||
|         self.tag = tag | ||||
|         self.text = text | ||||
|         self.id = id | ||||
| @ -37,16 +48,18 @@ class HTMLElement(model.ModelField): | ||||
| 
 | ||||
|     async def to_json(self): | ||||
|         result = await super().to_json() | ||||
|         result['text'] = self.text | ||||
|         result['id'] = self.id | ||||
|         result['html'] = self.html | ||||
|         result['class_name'] = self.class_name | ||||
|         result['tag'] = self.tag | ||||
|         result["text"] = self.text | ||||
|         result["id"] = self.id | ||||
|         result["html"] = self.html | ||||
|         result["class_name"] = self.class_name | ||||
|         result["tag"] = self.tag | ||||
|         return result | ||||
| 
 | ||||
| 
 | ||||
| class FormElement(HTMLElement): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| class FormInputElement(FormElement): | ||||
|     def __init__(self, type="text", place_holder=None, *args, **kwargs): | ||||
|         super().__init__(tag="input", *args, **kwargs) | ||||
| @ -59,23 +72,25 @@ class FormInputElement(FormElement): | ||||
|         data["type"] = self.type | ||||
|         return data | ||||
| 
 | ||||
| 
 | ||||
| class FormButtonElement(FormElement): | ||||
|     def __init__(self, tag="button", *args, **kwargs): | ||||
|         super().__init__(tag=tag, *args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| class Form(model.BaseModel): | ||||
|     @property | ||||
|     def html_elements(self): | ||||
|         return [element for element in self.fields if isinstance(element, HTMLElement)] | ||||
| 
 | ||||
|     def set_user_data(self, data): | ||||
|         return super().set_user_data(data.get('fields')) | ||||
|         return super().set_user_data(data.get("fields")) | ||||
| 
 | ||||
|     async def to_json(self, encode=False): | ||||
|         elements = await super().to_json() | ||||
|         html_elements = {} | ||||
|         for element in elements.keys(): | ||||
|             if element == 'is_valid': | ||||
|             if element == "is_valid": | ||||
|                 # is_valid is async get property so we can't do getattr on it | ||||
|                 continue | ||||
|             field = getattr(self, element) | ||||
| @ -85,8 +100,12 @@ class Form(model.BaseModel): | ||||
|                 except KeyError: | ||||
|                     pass | ||||
| 
 | ||||
|         is_valid = all(field['is_valid'] for field in html_elements.values()) | ||||
|         return dict(fields=html_elements, is_valid=is_valid, errors=await self.errors) | ||||
|         is_valid = all(field["is_valid"] for field in html_elements.values()) | ||||
|         return { | ||||
|             "fields": html_elements, | ||||
|             "is_valid": is_valid, | ||||
|             "errors": await self.errors, | ||||
|         } | ||||
| 
 | ||||
|     @property | ||||
|     async def errors(self): | ||||
|  | ||||
| @ -24,17 +24,17 @@ | ||||
| # SOFTWARE. | ||||
| 
 | ||||
| 
 | ||||
| from aiohttp import web | ||||
| import aiohttp | ||||
| from app.cache import time_cache_async | ||||
| from bs4 import BeautifulSoup | ||||
| from urllib.parse import urljoin | ||||
| import asyncio | ||||
| import pathlib | ||||
| import uuid | ||||
| import imgkit | ||||
| import asyncio | ||||
| import zlib | ||||
| import io | ||||
| from urllib.parse import urljoin | ||||
| 
 | ||||
| import aiohttp | ||||
| import imgkit | ||||
| from app.cache import time_cache_async | ||||
| from bs4 import BeautifulSoup | ||||
| 
 | ||||
| 
 | ||||
| async def crc32(data): | ||||
|     try: | ||||
| @ -43,6 +43,7 @@ async def crc32(data): | ||||
|         pass | ||||
|     return "crc32" + str(zlib.crc32(data)) | ||||
| 
 | ||||
| 
 | ||||
| async def get_file(name, suffix=".cache"): | ||||
|     name = await crc32(name) | ||||
|     path = pathlib.Path(".").joinpath("cache") | ||||
| @ -50,11 +51,13 @@ async def get_file(name, suffix=".cache"): | ||||
|         path.mkdir(parents=True, exist_ok=True) | ||||
|     return path.joinpath(name + suffix) | ||||
| 
 | ||||
| 
 | ||||
| async def public_touch(name=None): | ||||
|     path = pathlib.Path(".").joinpath(str(uuid.uuid4()) + name) | ||||
|     path.open("wb").close() | ||||
|     return path | ||||
| 
 | ||||
| 
 | ||||
| async def create_site_photo(url): | ||||
|     loop = asyncio.get_event_loop() | ||||
|     if not url.startswith("https"): | ||||
| @ -71,21 +74,23 @@ async def create_site_photo(url): | ||||
| 
 | ||||
|     return await loop.run_in_executor(None, make_photo) | ||||
| 
 | ||||
| 
 | ||||
| async def repair_links(base_url, html_content): | ||||
|     soup = BeautifulSoup(html_content, "html.parser") | ||||
|     for tag in soup.find_all(['a', 'img', 'link']): | ||||
|         if tag.has_attr('href') and not tag['href'].startswith("http"): | ||||
|             tag['href'] = urljoin(base_url, tag['href']) | ||||
|         if tag.has_attr('src') and not tag['src'].startswith("http"): | ||||
|             tag['src'] = urljoin(base_url, tag['src']) | ||||
|     for tag in soup.find_all(["a", "img", "link"]): | ||||
|         if tag.has_attr("href") and not tag["href"].startswith("http"): | ||||
|             tag["href"] = urljoin(base_url, tag["href"]) | ||||
|         if tag.has_attr("src") and not tag["src"].startswith("http"): | ||||
|             tag["src"] = urljoin(base_url, tag["src"]) | ||||
|     return soup.prettify() | ||||
| 
 | ||||
| 
 | ||||
| async def is_html_content(content: bytes): | ||||
|     try: | ||||
|         content = content.decode(errors='ignore') | ||||
|         content = content.decode(errors="ignore") | ||||
|     except: | ||||
|         pass | ||||
|     marks = ['<html', '<img', '<p', '<span', '<div'] | ||||
|     marks = ["<html", "<img", "<p", "<span", "<div"] | ||||
|     try: | ||||
|         content = content.lower() | ||||
|         for mark in marks: | ||||
| @ -95,6 +100,7 @@ async def is_html_content(content: bytes): | ||||
|         print(ex) | ||||
|     return False | ||||
| 
 | ||||
| 
 | ||||
| @time_cache_async(120) | ||||
| async def get(url): | ||||
|     async with aiohttp.ClientSession() as session: | ||||
|  | ||||
| @ -1,15 +1,14 @@ | ||||
| 
 | ||||
| DEFAULT_LIMIT = 30 | ||||
| import typing | ||||
| 
 | ||||
| from snek.system.model import BaseModel | ||||
| 
 | ||||
| import types | ||||
| 
 | ||||
| class BaseMapper: | ||||
| 
 | ||||
|     model_class:BaseModel = None  | ||||
|     default_limit:int = DEFAULT_LIMIT | ||||
|     table_name:str = None  | ||||
|     model_class: BaseModel = None | ||||
|     default_limit: int = DEFAULT_LIMIT | ||||
|     table_name: str = None | ||||
| 
 | ||||
|     def __init__(self, app): | ||||
|         self.app = app | ||||
| @ -27,12 +26,12 @@ class BaseMapper: | ||||
|     def table(self): | ||||
|         return self.db[self.table_name] | ||||
| 
 | ||||
|     async def get(self, uid:str=None, **kwargs) -> BaseModel: | ||||
|     async def get(self, uid: str = None, **kwargs) -> BaseModel: | ||||
|         if uid: | ||||
|             kwargs['uid'] = uid  | ||||
|         model = self.new() | ||||
|             kwargs["uid"] = uid | ||||
|         self.new() | ||||
|         record = self.table.find_one(**kwargs) | ||||
|         return await self.model_class.from_record(mapper=self,record=record) | ||||
|         return await self.model_class.from_record(mapper=self, record=record) | ||||
| 
 | ||||
|     async def exists(self, **kwargs): | ||||
|         return self.table.exists(**kwargs) | ||||
| @ -40,19 +39,19 @@ class BaseMapper: | ||||
|     async def count(self, **kwargs) -> int: | ||||
|         return self.table.count(**kwargs) | ||||
| 
 | ||||
|     async def save(self, model:BaseModel) -> bool: | ||||
|     async def save(self, model: BaseModel) -> bool: | ||||
|         record = await model.record | ||||
|         if not record.get('uid'): | ||||
|         if not record.get("uid"): | ||||
|             raise Exception(f"Attempt to save without uid: {record}.") | ||||
|         return self.table.upsert(record,['uid']) | ||||
|         return self.table.upsert(record, ["uid"]) | ||||
| 
 | ||||
|     async def find(self, **kwargs) -> typing.AsyncGenerator: | ||||
|         if not kwargs.get("_limit"): | ||||
|             kwargs["_limit"] = self.default_limit | ||||
|         for record in self.table.find(**kwargs): | ||||
|             yield await self.model_class.from_record(mapper=self,record=record) | ||||
|             yield await self.model_class.from_record(mapper=self, record=record) | ||||
| 
 | ||||
|     async def delete(self, kwargs=None)-> int:  | ||||
|     async def delete(self, kwargs=None) -> int: | ||||
|         if not kwargs or not isinstance(kwargs, dict): | ||||
|             raise Exception("Can't execute delete with no filter.") | ||||
|         return self.table.delete(**kwargs) | ||||
|  | ||||
| @ -1,62 +1,65 @@ | ||||
| 
 | ||||
| # Original source: https://brandonjay.dev/posts/2021/render-markdown-html-in-python-with-jinja2 | ||||
| 
 | ||||
| from types import SimpleNamespace | ||||
| from mistune import escape | ||||
| from mistune import Markdown | ||||
| from mistune import HTMLRenderer | ||||
| from pygments import highlight | ||||
| from pygments.lexers import get_lexer_by_name | ||||
| from pygments.formatters import html | ||||
| from pygments.styles import get_style_by_name | ||||
| import functools | ||||
| 
 | ||||
| from app.cache import time_cache_async | ||||
| from mistune import HTMLRenderer, Markdown | ||||
| from pygments import highlight | ||||
| from pygments.formatters import html | ||||
| from pygments.lexers import get_lexer_by_name | ||||
| 
 | ||||
| 
 | ||||
| class MarkdownRenderer(HTMLRenderer): | ||||
| 
 | ||||
|     _allow_harmful_protocols = True | ||||
|     def __init__(self, app, template): | ||||
|          self.template = template | ||||
| 
 | ||||
|          self.app = app | ||||
|          self.env = self.app.jinja2_env | ||||
|          formatter = html.HtmlFormatter() | ||||
|          self.env.globals['highlight_styles'] = formatter.get_style_defs() | ||||
|     def _escape(self,str): | ||||
|         return str ##escape(str) | ||||
|     def block_code(self, code, lang=None,info=None): | ||||
|     def __init__(self, app, template): | ||||
|         self.template = template | ||||
| 
 | ||||
|         self.app = app | ||||
|         self.env = self.app.jinja2_env | ||||
|         formatter = html.HtmlFormatter() | ||||
|         self.env.globals["highlight_styles"] = formatter.get_style_defs() | ||||
| 
 | ||||
|     def _escape(self, str): | ||||
|         return str  ##escape(str) | ||||
| 
 | ||||
|     def block_code(self, code, lang=None, info=None): | ||||
|         if not lang: | ||||
|             lang = info | ||||
|         if not lang: | ||||
|             return f"<div>{code}</div>" | ||||
|             #return '\n<pre><code>%s</code></pre>\n' % escape(code) | ||||
|             # return '\n<pre><code>%s</code></pre>\n' % escape(code) | ||||
|         lexer = get_lexer_by_name(lang, stripall=True) | ||||
|         formatter = html.HtmlFormatter(lineseparator="<br>") | ||||
|         return highlight(code, lexer, formatter) | ||||
| 
 | ||||
|     def render(self): | ||||
|         markdown_string = self.app.template_path.joinpath(self.template).read_text() | ||||
|         renderer = MarkdownRenderer(self.app,self.template) | ||||
|         renderer = MarkdownRenderer(self.app, self.template) | ||||
|         markdown = Markdown(renderer=renderer) | ||||
|         return markdown(markdown_string) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def render_markdown_sync(app, markdown_string): | ||||
|     renderer = MarkdownRenderer(app,None) | ||||
|     renderer = MarkdownRenderer(app, None) | ||||
|     markdown = Markdown(renderer=renderer) | ||||
|     return markdown(markdown_string) | ||||
| 
 | ||||
| 
 | ||||
| @time_cache_async(120) | ||||
| async def render_markdown(app, markdown_string): | ||||
|      return render_markdown_sync(app,markdown_string) | ||||
|     return render_markdown_sync(app, markdown_string) | ||||
| 
 | ||||
| from jinja2 import nodes, TemplateSyntaxError | ||||
| 
 | ||||
| from jinja2 import TemplateSyntaxError, nodes | ||||
| from jinja2.ext import Extension | ||||
| from jinja2.nodes import Const | ||||
| 
 | ||||
| 
 | ||||
| # Source: https://ron.sh/how-to-write-a-jinja2-extension/ | ||||
| class MarkdownExtension(Extension): | ||||
|     tags = {'markdown'} | ||||
|     tags = {"markdown"} | ||||
| 
 | ||||
|     def __init__(self, environment): | ||||
|         self.app = SimpleNamespace(jinja2_env=environment) | ||||
| @ -64,13 +67,15 @@ class MarkdownExtension(Extension): | ||||
| 
 | ||||
|     def parse(self, parser): | ||||
|         line_number = next(parser.stream).lineno | ||||
|         md_file = [Const('')] | ||||
|         body = '' | ||||
|         md_file = [Const("")] | ||||
|         body = "" | ||||
|         try: | ||||
|             md_file = [parser.parse_expression()] | ||||
|         except TemplateSyntaxError: | ||||
|             body = parser.parse_statements(['name:endmarkdown'], drop_needle=True) | ||||
|         return nodes.CallBlock(self.call_method('_to_html', md_file), [], [], body).set_lineno(line_number) | ||||
|             body = parser.parse_statements(["name:endmarkdown"], drop_needle=True) | ||||
|         return nodes.CallBlock( | ||||
|             self.call_method("_to_html", md_file), [], [], body | ||||
|         ).set_lineno(line_number) | ||||
| 
 | ||||
|     def _to_html(self, md_file, caller): | ||||
|         return render_markdown_sync(self.app,caller()) | ||||
|         return render_markdown_sync(self.app, caller()) | ||||
|  | ||||
| @ -8,12 +8,14 @@ | ||||
| 
 | ||||
| from aiohttp import web | ||||
| 
 | ||||
| 
 | ||||
| @web.middleware | ||||
| async def no_cors_middleware(request, handler): | ||||
|     response = await handler(request) | ||||
|     response.headers.pop("Access-Control-Allow-Origin", None) | ||||
|     return response | ||||
| 
 | ||||
| 
 | ||||
| @web.middleware | ||||
| async def cors_allow_middleware(request, handler): | ||||
|     response = await handler(request) | ||||
| @ -22,12 +24,15 @@ async def cors_allow_middleware(request, handler): | ||||
|     response.headers["Access-Control-Allow-Headers"] = "*" | ||||
|     return response | ||||
| 
 | ||||
| 
 | ||||
| @web.middleware | ||||
| async def cors_middleware(request, handler): | ||||
|     if request.method == "OPTIONS": | ||||
|         response = web.Response() | ||||
|         response.headers["Access-Control-Allow-Origin"] = "*" | ||||
|         response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS" | ||||
|         response.headers["Access-Control-Allow-Methods"] = ( | ||||
|             "GET, POST, PUT, DELETE, OPTIONS" | ||||
|         ) | ||||
|         response.headers["Access-Control-Allow-Headers"] = "*" | ||||
|         return response | ||||
| 
 | ||||
|  | ||||
| @ -25,12 +25,12 @@ | ||||
| # SOFTWARE. | ||||
| 
 | ||||
| 
 | ||||
| import copy | ||||
| import json | ||||
| import re | ||||
| import uuid | ||||
| import json | ||||
| from datetime import datetime, timezone | ||||
| from collections import OrderedDict | ||||
| import copy | ||||
| from datetime import datetime, timezone | ||||
| 
 | ||||
| TIMESTAMP_REGEX = r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}\+\d{2}:\d{2}$" | ||||
| 
 | ||||
| @ -44,12 +44,21 @@ def add_attrs(**kwargs): | ||||
|         for key, value in kwargs.items(): | ||||
|             setattr(func, key, value) | ||||
|         return func | ||||
| 
 | ||||
|     return decorator | ||||
| 
 | ||||
| 
 | ||||
| def validate_attrs(required=False, min_length=None, max_length=None, regex=None, **kwargs): | ||||
| def validate_attrs( | ||||
|     required=False, min_length=None, max_length=None, regex=None, **kwargs | ||||
| ): | ||||
|     def decorator(func): | ||||
|         return add_attrs(required=required, min_length=min_length, max_length=max_length, regex=regex, **kwargs)(func) | ||||
|         return add_attrs( | ||||
|             required=required, | ||||
|             min_length=min_length, | ||||
|             max_length=max_length, | ||||
|             regex=regex, | ||||
|             **kwargs, | ||||
|         )(func) | ||||
| 
 | ||||
| 
 | ||||
| class Validator: | ||||
| @ -70,7 +79,21 @@ class Validator: | ||||
|     def custom_validation(self): | ||||
|         return True | ||||
| 
 | ||||
|     def __init__(self, required=False, min_num=None, max_num=None, min_length=None, max_length=None, regex=None, value=None, kind=None, help_text=None, app=None, model=None, **kwargs): | ||||
|     def __init__( | ||||
|         self, | ||||
|         required=False, | ||||
|         min_num=None, | ||||
|         max_num=None, | ||||
|         min_length=None, | ||||
|         max_length=None, | ||||
|         regex=None, | ||||
|         value=None, | ||||
|         kind=None, | ||||
|         help_text=None, | ||||
|         app=None, | ||||
|         model=None, | ||||
|         **kwargs, | ||||
|     ): | ||||
|         self.index = Validator._index | ||||
|         Validator._index += 1 | ||||
|         self.app = app | ||||
| @ -103,9 +126,13 @@ class Validator: | ||||
|             if self.max_num is not None and self.value > self.max_num: | ||||
|                 error_list.append(f"Field should be maximal {self.max_num}.") | ||||
|         if self.min_length is not None and len(self.value) < self.min_length: | ||||
|             error_list.append(f"Field should be minimal {self.min_length} characters long.") | ||||
|             error_list.append( | ||||
|                 f"Field should be minimal {self.min_length} characters long." | ||||
|             ) | ||||
|         if self.max_length is not None and len(self.value) > self.max_length: | ||||
|             error_list.append(f"Field should be maximal {self.max_length} characters long.") | ||||
|             error_list.append( | ||||
|                 f"Field should be maximal {self.max_length} characters long." | ||||
|             ) | ||||
|         if self.regex and self.value and not re.match(self.regex, self.value): | ||||
|             error_list.append("Invalid value.") | ||||
|         if self.kind and not isinstance(self.value, self.kind): | ||||
| @ -141,7 +168,7 @@ class Validator: | ||||
|             "help_text": self.help_text, | ||||
|             "errors": errors, | ||||
|             "is_valid": is_valid, | ||||
|             "index": self.index | ||||
|             "index": self.index, | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| @ -156,7 +183,7 @@ class ModelField(Validator): | ||||
| 
 | ||||
|     async def to_json(self): | ||||
|         result = await super().to_json() | ||||
|         result['name'] = self.name | ||||
|         result["name"] = self.name | ||||
|         return result | ||||
| 
 | ||||
| 
 | ||||
| @ -193,9 +220,18 @@ class UUIDField(ModelField): | ||||
| class BaseModel: | ||||
| 
 | ||||
|     uid = UUIDField(name="uid", required=True) | ||||
|     created_at = CreatedField(name="created_at", required=True, regex=TIMESTAMP_REGEX, place_holder="Created at") | ||||
|     updated_at = UpdatedField(name="updated_at", regex=TIMESTAMP_REGEX, place_holder="Updated at") | ||||
|     deleted_at = DeletedField(name="deleted_at", regex=TIMESTAMP_REGEX, place_holder="Deleted at") | ||||
|     created_at = CreatedField( | ||||
|         name="created_at", | ||||
|         required=True, | ||||
|         regex=TIMESTAMP_REGEX, | ||||
|         place_holder="Created at", | ||||
|     ) | ||||
|     updated_at = UpdatedField( | ||||
|         name="updated_at", regex=TIMESTAMP_REGEX, place_holder="Updated at" | ||||
|     ) | ||||
|     deleted_at = DeletedField( | ||||
|         name="deleted_at", regex=TIMESTAMP_REGEX, place_holder="Deleted at" | ||||
|     ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     async def from_record(cls, record, mapper): | ||||
| @ -233,10 +269,12 @@ class BaseModel: | ||||
| 
 | ||||
|             if isinstance(obj, Validator): | ||||
|                 self.__dict__[key] = copy.deepcopy(obj) | ||||
|                 self.__dict__[key].value = kwargs.pop(key, self.__dict__[key].initial_value) | ||||
|                 self.__dict__[key].value = kwargs.pop( | ||||
|                     key, self.__dict__[key].initial_value | ||||
|                 ) | ||||
|                 self.fields[key] = self.__dict__[key] | ||||
|                 self.fields[key].model = self | ||||
|                 self.fields[key].app = kwargs.get('app') | ||||
|                 self.fields[key].app = kwargs.get("app") | ||||
| 
 | ||||
|     def __setitem__(self, key, value): | ||||
|         obj = self.__dict__.get(key) | ||||
| @ -254,17 +292,14 @@ class BaseModel: | ||||
|             field = self.fields.get(key) | ||||
|             if not field: | ||||
|                 continue | ||||
|             if value.get('name'): | ||||
|                 value = value.get('value') | ||||
|             if value.get("name"): | ||||
|                 value = value.get("value") | ||||
|             field.value = value | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|     @property | ||||
|     async def is_valid(self): | ||||
|         return all([await field.is_valid for field in self.fields.values()]) | ||||
| 
 | ||||
| 
 | ||||
|     def __getitem__(self, key): | ||||
|         obj = self.__dict__.get(key) | ||||
|         if isinstance(obj, Validator): | ||||
| @ -282,20 +317,22 @@ class BaseModel: | ||||
|         obj = await self.to_json() | ||||
|         record = {} | ||||
|         for key, value in obj.items(): | ||||
|             if not isinstance(value, dict) or not 'value' in value: | ||||
|             if not isinstance(value, dict) or "value" not in value: | ||||
|                 continue | ||||
|             if getattr(self, key).save: | ||||
|                 record[key] = value.get('value') | ||||
|                 record[key] = value.get("value") | ||||
|         return record | ||||
| 
 | ||||
|     async def to_json(self, encode=False): | ||||
|         model_data = OrderedDict({ | ||||
|             "uid": self.uid.value, | ||||
|             "created_at": self.created_at.value, | ||||
|             "updated_at": self.updated_at.value, | ||||
|             "deleted_at": self.deleted_at.value, | ||||
|             "is_valid": await self.is_valid | ||||
|         }) | ||||
|         model_data = OrderedDict( | ||||
|             { | ||||
|                 "uid": self.uid.value, | ||||
|                 "created_at": self.created_at.value, | ||||
|                 "updated_at": self.updated_at.value, | ||||
|                 "deleted_at": self.deleted_at.value, | ||||
|                 "is_valid": await self.is_valid, | ||||
|             } | ||||
|         ) | ||||
| 
 | ||||
|         for key, value in self.fields.items(): | ||||
|             if key == "record": | ||||
|  | ||||
| @ -2,7 +2,8 @@ import hashlib | ||||
| 
 | ||||
| DEFAULT_SALT = b"snekker-de-snek-" | ||||
| 
 | ||||
| async def hash(data,salt=DEFAULT_SALT): | ||||
| 
 | ||||
| async def hash(data, salt=DEFAULT_SALT): | ||||
|     try: | ||||
|         data = data.encode(errors="ignore") | ||||
|     except AttributeError: | ||||
| @ -16,5 +17,6 @@ async def hash(data,salt=DEFAULT_SALT): | ||||
|     obj = hashlib.sha256(salted) | ||||
|     return obj.hexdigest() | ||||
| 
 | ||||
| async def verify(string:str, hashed:str): | ||||
| 
 | ||||
| async def verify(string: str, hashed: str): | ||||
|     return await hash(string) == hashed | ||||
|  | ||||
| @ -1,13 +1,11 @@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| from snek.mapper import get_mapper | ||||
| from snek.system.mapper import BaseMapper  | ||||
| from snek.model.user import UserModel | ||||
| from snek.system.mapper import BaseMapper | ||||
| 
 | ||||
| 
 | ||||
| class BaseService: | ||||
| 
 | ||||
|     mapper_name:BaseMapper = None | ||||
|     mapper_name: BaseMapper = None | ||||
| 
 | ||||
|     def __init__(self, app): | ||||
|         self.app = app | ||||
| @ -28,11 +26,10 @@ class BaseService: | ||||
|     async def get(self, **kwargs): | ||||
|         return await self.mapper.get(**kwargs) | ||||
| 
 | ||||
|     async def save(self, model:UserModel): | ||||
|     async def save(self, model: UserModel): | ||||
|         # if model.is_valid: You Know why not | ||||
|         return await self.mapper.save(model) and True | ||||
| 
 | ||||
|      | ||||
|     async def find(self, **kwargs): | ||||
|         return await self.mapper.find(**kwargs) | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ from aiohttp import web | ||||
| 
 | ||||
| from snek.system.markdown import render_markdown | ||||
| 
 | ||||
| 
 | ||||
| class BaseView(web.View): | ||||
| 
 | ||||
|     @property | ||||
| @ -17,10 +18,15 @@ class BaseView(web.View): | ||||
| 
 | ||||
|     async def render_template(self, template_name, context=None): | ||||
|         if template_name.endswith(".md"): | ||||
|             response = await self.request.app.render_template(template_name,self.request,context) | ||||
|             response = await self.request.app.render_template( | ||||
|                 template_name, self.request, context | ||||
|             ) | ||||
|             body = await render_markdown(self.app, response.body.decode()) | ||||
|             return web.Response(body=body,content_type="text/html") | ||||
|         return await self.request.app.render_template(template_name, self.request,context) | ||||
|             return web.Response(body=body, content_type="text/html") | ||||
|         return await self.request.app.render_template( | ||||
|             template_name, self.request, context | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| class BaseFormView(BaseView): | ||||
| 
 | ||||
| @ -34,14 +40,14 @@ class BaseFormView(BaseView): | ||||
|     async def post(self): | ||||
|         form = self.form(app=self.app) | ||||
|         post = await self.request.json() | ||||
|         form.set_user_data(post['form']) | ||||
|         form.set_user_data(post["form"]) | ||||
|         result = await form.to_json() | ||||
|         if post.get('action') == 'validate': | ||||
|         if post.get("action") == "validate": | ||||
|             # Pass | ||||
|             pass | ||||
|         if post.get('action') == 'submit' and result['is_valid']: | ||||
|         if post.get("action") == "submit" and result["is_valid"]: | ||||
|             await self.submit(form) | ||||
|         return await self.json_response(result) | ||||
| 
 | ||||
|     async def submit(self,model=None): | ||||
|     async def submit(self, model=None): | ||||
|         print("Submit sucess") | ||||
|  | ||||
| @ -9,8 +9,7 @@ Currently only some details about the internal API are available. | ||||
| # of the snek.system.security module. | ||||
| 
 | ||||
| new_user_object = await app.service.user.register( | ||||
|     username="retoor",  | ||||
|     password="retoorded" | ||||
|     username="retoor", password="retoorded" | ||||
| ) | ||||
| ``` | ||||
| 
 | ||||
| @ -23,13 +22,14 @@ var1 = security.encrypt("data") | ||||
| var2 = security.encrypt(b"data") | ||||
| 
 | ||||
| # Is correct: | ||||
| assert(var1 == var2) | ||||
| assert var1 == var2 | ||||
| ``` | ||||
| 
 | ||||
| ## How to create a basic HTML / Markdown view | ||||
| ```python | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| class IndexView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
| @ -40,8 +40,9 @@ class IndexView(BaseView): | ||||
| ``` | ||||
| ## How to create a FormView | ||||
| ```python | ||||
| from snek.system.view import BaseFormView | ||||
| from snek.form.register import RegisterForm | ||||
| from snek.system.view import BaseFormView | ||||
| 
 | ||||
| 
 | ||||
| class RegisterFormView(BaseFormView): | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,3 @@ | ||||
| 
 | ||||
| 
 | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| @ -8,6 +6,7 @@ class AboutHTMLView(BaseView): | ||||
|     async def get(self): | ||||
|         return await self.render_template("about.html") | ||||
| 
 | ||||
| 
 | ||||
| class AboutMDView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|  | ||||
| @ -1,6 +1,3 @@ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| @ -9,6 +6,7 @@ class DocsHTMLView(BaseView): | ||||
|     async def get(self): | ||||
|         return await self.render_template("docs.html") | ||||
| 
 | ||||
| 
 | ||||
| class DocsMDView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| class IndexView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|  | ||||
| @ -1,13 +1,18 @@ | ||||
| from snek.form.register import RegisterForm | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| class LoginView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|         return await self.render_template("login.html")    #web.json_response({"form": RegisterForm().to_json()}) | ||||
|         return await self.render_template( | ||||
|             "login.html" | ||||
|         )  # web.json_response({"form": RegisterForm().to_json()}) | ||||
| 
 | ||||
|     async def post(self): | ||||
|         form = RegisterForm() | ||||
|         form.set_user_data(await self.request.post()) | ||||
|         print(form.is_valid()) | ||||
|         return await self.render_template("login.html", self.request)    #web.json_response({"form": RegisterForm().to_json()}) | ||||
|         return await self.render_template( | ||||
|             "login.html", self.request | ||||
|         )  # web.json_response({"form": RegisterForm().to_json()}) | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.view import BaseFormView | ||||
| from snek.form.login import LoginForm | ||||
| from snek.system.view import BaseFormView | ||||
| 
 | ||||
| 
 | ||||
| class LoginFormView(BaseFormView): | ||||
|     form = LoginForm | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| class RegisterView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|  | ||||
| @ -1,9 +1,12 @@ | ||||
| from snek.form.register import RegisterForm | ||||
| from snek.system.view import BaseFormView | ||||
| 
 | ||||
| 
 | ||||
| class RegisterFormView(BaseFormView): | ||||
|     form = RegisterForm | ||||
| 
 | ||||
|     async def submit(self, form): | ||||
|         result = await self.app.services.user.register(form.email.value,form.username.value,form.password.value) | ||||
|         print("SUBMITTED:",result) | ||||
|         result = await self.app.services.user.register( | ||||
|             form.email.value, form.username.value, form.password.value | ||||
|         ) | ||||
|         print("SUBMITTED:", result) | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| from snek.system.view import BaseView | ||||
| 
 | ||||
| 
 | ||||
| class WebView(BaseView): | ||||
| 
 | ||||
|     async def get(self): | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user