From bfcc49dd8d224914839ea7dc8427f1bcea5741ef Mon Sep 17 00:00:00 2001
From: David Carmichael
Date: Fri, 16 Aug 2024 15:09:07 +0100
Subject: [PATCH] feat: docs
---
docs/__init__.py | 60 ++
.../v1/CLI Commands-quart-imp blueprint.md | 101 ++
docs/_md/v1/CLI Commands-quart-imp init.md | 215 ++++
docs/_md/v1/Imp-Introduction.md | 84 ++
docs/_md/v1/Imp-import_app_resources.md | 106 ++
docs/_md/v1/Imp-import_blueprint.md | 121 +++
docs/_md/v1/Imp-import_blueprints.md | 50 +
docs/_md/v1/Imp-init_app-init.md | 22 +
docs/_md/v1/Imp-init_session.md | 48 +
docs/_md/v1/ImpBlueprint-Introduction.md | 68 ++
.../ImpBlueprint-import_nested_blueprint.md | 78 ++
.../ImpBlueprint-import_nested_blueprints.md | 60 ++
docs/_md/v1/ImpBlueprint-import_resources.md | 57 ++
docs/_md/v1/ImpBlueprint-init.md | 17 +
docs/_md/v1/ImpBlueprint-tmpl.md | 44 +
docs/_md/v1/__index__.md | 121 +++
docs/_md/v1/__menu__.md | 43 +
.../quart_imp_auth-authenticate_password.md | 55 ++
.../_md/v1/quart_imp_auth-encrypt_password.md | 53 +
...mp_auth-generate_alphanumeric_validator.md | 24 +
.../v1/quart_imp_auth-generate_csrf_token.md | 26 +
...quart_imp_auth-generate_email_validator.md | 27 +
...art_imp_auth-generate_numeric_validator.md | 27 +
.../v1/quart_imp_auth-generate_password.md | 26 +
.../v1/quart_imp_auth-generate_private_key.md | 38 +
docs/_md/v1/quart_imp_auth-generate_salt.md | 46 +
.../quart_imp_auth-is_email_address_valid.md | 46 +
.../v1/quart_imp_auth-is_username_valid.md | 58 ++
.../v1/quart_imp_config-impblueprintconfig.md | 33 +
docs/_md/v1/quart_imp_config-impconfig.md | 32 +
docs/_md/v1/quart_imp_config-quartconfig.md | 59 ++
.../v1/quart_imp_security-api_login_check.md | 47 +
.../_md/v1/quart_imp_security-include_csrf.md | 48 +
docs/_md/v1/quart_imp_security-login_check.md | 66 ++
.../quart_imp_security-pass_function_check.md | 114 +++
.../v1/quart_imp_security-permission_check.md | 57 ++
docs/_ssg/__init__.py | 3 +
docs/_ssg/compiler.py | 132 +++
docs/_ssg/exceptions.py | 38 +
docs/_ssg/helpers.py | 53 +
docs/_ssg/render_engines.py | 18 +
docs/_templates/__main__.html | 26 +
docs/_templates/__menu__.html | 31 +
docs/_templates/index.html | 26 +
docs/_templates/main_index.html | 15 +
docs/config.py | 7 +
docs/index.html | 15 +
docs/v1/cli_commands-quart-imp_blueprint.html | 304 ++++++
docs/v1/cli_commands-quart-imp_init.html | 398 ++++++++
docs/v1/imp-import_app_resources.html | 304 ++++++
docs/v1/imp-import_blueprint.html | 324 ++++++
docs/v1/imp-import_blueprints.html | 271 +++++
docs/v1/imp-init_app-init.html | 248 +++++
docs/v1/imp-init_session.html | 263 +++++
docs/v1/imp-introduction.html | 297 ++++++
.../impblueprint-import_nested_blueprint.html | 291 ++++++
...impblueprint-import_nested_blueprints.html | 277 ++++++
docs/v1/impblueprint-import_resources.html | 276 ++++++
docs/v1/impblueprint-init.html | 242 +++++
docs/v1/impblueprint-introduction.html | 285 ++++++
docs/v1/impblueprint-tmpl.html | 264 +++++
docs/v1/index.html | 317 ++++++
.../quart_imp_auth-authenticate_password.html | 272 +++++
docs/v1/quart_imp_auth-encrypt_password.html | 269 +++++
..._auth-generate_alphanumeric_validator.html | 245 +++++
.../quart_imp_auth-generate_csrf_token.html | 246 +++++
...art_imp_auth-generate_email_validator.html | 248 +++++
...t_imp_auth-generate_numeric_validator.html | 246 +++++
docs/v1/quart_imp_auth-generate_password.html | 246 +++++
.../quart_imp_auth-generate_private_key.html | 260 +++++
docs/v1/quart_imp_auth-generate_salt.html | 263 +++++
...quart_imp_auth-is_email_address_valid.html | 260 +++++
docs/v1/quart_imp_auth-is_username_valid.html | 269 +++++
.../quart_imp_config-impblueprintconfig.html | 255 +++++
docs/v1/quart_imp_config-impconfig.html | 255 +++++
docs/v1/quart_imp_config-quartconfig.html | 280 ++++++
.../quart_imp_security-api_login_check.html | 261 +++++
docs/v1/quart_imp_security-include_csrf.html | 265 +++++
docs/v1/quart_imp_security-login_check.html | 278 ++++++
...uart_imp_security-pass_function_check.html | 326 ++++++
.../quart_imp_security-permission_check.html | 271 +++++
docs/v1/static/Flask-Imp-Medium.png | Bin 0 -> 7987 bytes
docs/v1/static/android-chrome-192x192.png | Bin 0 -> 15163 bytes
docs/v1/static/android-chrome-256x256.png | Bin 0 -> 20999 bytes
docs/v1/static/android-chrome-96x96.png | Bin 0 -> 6119 bytes
docs/v1/static/apple-touch-icon.png | Bin 0 -> 13882 bytes
docs/v1/static/browserconfig.xml | 9 +
docs/v1/static/favicon-16x16.png | Bin 0 -> 1210 bytes
docs/v1/static/favicon-32x32.png | Bin 0 -> 1873 bytes
docs/v1/static/favicon.ico | Bin 0 -> 15086 bytes
docs/v1/static/mstile-150x150.png | Bin 0 -> 9755 bytes
docs/v1/static/pygments.emacs-dull.css | 69 ++
docs/v1/static/safari-pinned-tab.svg | 17 +
docs/v1/static/site.webmanifest | 19 +
docs/v1/static/water.css | 929 ++++++++++++++++++
95 files changed, 12960 insertions(+)
create mode 100644 docs/__init__.py
create mode 100644 docs/_md/v1/CLI Commands-quart-imp blueprint.md
create mode 100644 docs/_md/v1/CLI Commands-quart-imp init.md
create mode 100644 docs/_md/v1/Imp-Introduction.md
create mode 100644 docs/_md/v1/Imp-import_app_resources.md
create mode 100644 docs/_md/v1/Imp-import_blueprint.md
create mode 100644 docs/_md/v1/Imp-import_blueprints.md
create mode 100644 docs/_md/v1/Imp-init_app-init.md
create mode 100644 docs/_md/v1/Imp-init_session.md
create mode 100644 docs/_md/v1/ImpBlueprint-Introduction.md
create mode 100644 docs/_md/v1/ImpBlueprint-import_nested_blueprint.md
create mode 100644 docs/_md/v1/ImpBlueprint-import_nested_blueprints.md
create mode 100644 docs/_md/v1/ImpBlueprint-import_resources.md
create mode 100644 docs/_md/v1/ImpBlueprint-init.md
create mode 100644 docs/_md/v1/ImpBlueprint-tmpl.md
create mode 100644 docs/_md/v1/__index__.md
create mode 100644 docs/_md/v1/__menu__.md
create mode 100644 docs/_md/v1/quart_imp_auth-authenticate_password.md
create mode 100644 docs/_md/v1/quart_imp_auth-encrypt_password.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_alphanumeric_validator.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_csrf_token.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_email_validator.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_numeric_validator.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_password.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_private_key.md
create mode 100644 docs/_md/v1/quart_imp_auth-generate_salt.md
create mode 100644 docs/_md/v1/quart_imp_auth-is_email_address_valid.md
create mode 100644 docs/_md/v1/quart_imp_auth-is_username_valid.md
create mode 100644 docs/_md/v1/quart_imp_config-impblueprintconfig.md
create mode 100644 docs/_md/v1/quart_imp_config-impconfig.md
create mode 100644 docs/_md/v1/quart_imp_config-quartconfig.md
create mode 100644 docs/_md/v1/quart_imp_security-api_login_check.md
create mode 100644 docs/_md/v1/quart_imp_security-include_csrf.md
create mode 100644 docs/_md/v1/quart_imp_security-login_check.md
create mode 100644 docs/_md/v1/quart_imp_security-pass_function_check.md
create mode 100644 docs/_md/v1/quart_imp_security-permission_check.md
create mode 100644 docs/_ssg/__init__.py
create mode 100644 docs/_ssg/compiler.py
create mode 100644 docs/_ssg/exceptions.py
create mode 100644 docs/_ssg/helpers.py
create mode 100644 docs/_ssg/render_engines.py
create mode 100644 docs/_templates/__main__.html
create mode 100644 docs/_templates/__menu__.html
create mode 100644 docs/_templates/index.html
create mode 100644 docs/_templates/main_index.html
create mode 100644 docs/config.py
create mode 100644 docs/index.html
create mode 100644 docs/v1/cli_commands-quart-imp_blueprint.html
create mode 100644 docs/v1/cli_commands-quart-imp_init.html
create mode 100644 docs/v1/imp-import_app_resources.html
create mode 100644 docs/v1/imp-import_blueprint.html
create mode 100644 docs/v1/imp-import_blueprints.html
create mode 100644 docs/v1/imp-init_app-init.html
create mode 100644 docs/v1/imp-init_session.html
create mode 100644 docs/v1/imp-introduction.html
create mode 100644 docs/v1/impblueprint-import_nested_blueprint.html
create mode 100644 docs/v1/impblueprint-import_nested_blueprints.html
create mode 100644 docs/v1/impblueprint-import_resources.html
create mode 100644 docs/v1/impblueprint-init.html
create mode 100644 docs/v1/impblueprint-introduction.html
create mode 100644 docs/v1/impblueprint-tmpl.html
create mode 100644 docs/v1/index.html
create mode 100644 docs/v1/quart_imp_auth-authenticate_password.html
create mode 100644 docs/v1/quart_imp_auth-encrypt_password.html
create mode 100644 docs/v1/quart_imp_auth-generate_alphanumeric_validator.html
create mode 100644 docs/v1/quart_imp_auth-generate_csrf_token.html
create mode 100644 docs/v1/quart_imp_auth-generate_email_validator.html
create mode 100644 docs/v1/quart_imp_auth-generate_numeric_validator.html
create mode 100644 docs/v1/quart_imp_auth-generate_password.html
create mode 100644 docs/v1/quart_imp_auth-generate_private_key.html
create mode 100644 docs/v1/quart_imp_auth-generate_salt.html
create mode 100644 docs/v1/quart_imp_auth-is_email_address_valid.html
create mode 100644 docs/v1/quart_imp_auth-is_username_valid.html
create mode 100644 docs/v1/quart_imp_config-impblueprintconfig.html
create mode 100644 docs/v1/quart_imp_config-impconfig.html
create mode 100644 docs/v1/quart_imp_config-quartconfig.html
create mode 100644 docs/v1/quart_imp_security-api_login_check.html
create mode 100644 docs/v1/quart_imp_security-include_csrf.html
create mode 100644 docs/v1/quart_imp_security-login_check.html
create mode 100644 docs/v1/quart_imp_security-pass_function_check.html
create mode 100644 docs/v1/quart_imp_security-permission_check.html
create mode 100644 docs/v1/static/Flask-Imp-Medium.png
create mode 100644 docs/v1/static/android-chrome-192x192.png
create mode 100644 docs/v1/static/android-chrome-256x256.png
create mode 100644 docs/v1/static/android-chrome-96x96.png
create mode 100644 docs/v1/static/apple-touch-icon.png
create mode 100644 docs/v1/static/browserconfig.xml
create mode 100644 docs/v1/static/favicon-16x16.png
create mode 100644 docs/v1/static/favicon-32x32.png
create mode 100644 docs/v1/static/favicon.ico
create mode 100644 docs/v1/static/mstile-150x150.png
create mode 100644 docs/v1/static/pygments.emacs-dull.css
create mode 100644 docs/v1/static/safari-pinned-tab.svg
create mode 100644 docs/v1/static/site.webmanifest
create mode 100644 docs/v1/static/water.css
diff --git a/docs/__init__.py b/docs/__init__.py
new file mode 100644
index 0000000..3e4c634
--- /dev/null
+++ b/docs/__init__.py
@@ -0,0 +1,60 @@
+from pathlib import Path
+from time import sleep
+
+import click
+from flask import Flask
+
+from .config import Config
+from ._ssg import compiler
+
+cwd = Path(__file__).parent
+
+
+def create_app():
+ app = Flask(__name__)
+ app.template_folder = "_templates"
+
+ doc_path = Path(cwd / Config.latest)
+ markdown_path = Path(cwd / "_md" / Config.latest)
+
+ @app.cli.command("compile")
+ @click.option("--watch", is_flag=True, help="Watch for file changes")
+ def compile_site(watch):
+ if watch:
+ watching_files = {}
+
+ def change_loop():
+ change = False
+ updated = []
+ for file in markdown_path.glob("**/*.md"):
+ if file not in watching_files:
+ watching_files[file] = file.stat().st_mtime
+ updated.append(file)
+ change = True
+ else:
+ if file.stat().st_mtime > watching_files[file]:
+ watching_files[file] = file.stat().st_mtime
+ updated.append(file)
+ change = True
+
+ if change:
+ print("Update detected, recompiling...")
+ for file in updated:
+ print(f" - {file}")
+
+ compiler(doc_path, markdown_path)
+
+ print("Watching for changes...")
+
+ while True:
+ change_loop()
+ sleep(1)
+
+ else:
+ compiler(doc_path, markdown_path)
+
+ @app.route("/")
+ def index():
+ return "To use run the following command: flask --app gdocs compile"
+
+ return app
diff --git a/docs/_md/v1/CLI Commands-quart-imp blueprint.md b/docs/_md/v1/CLI Commands-quart-imp blueprint.md
new file mode 100644
index 0000000..a1b465d
--- /dev/null
+++ b/docs/_md/v1/CLI Commands-quart-imp blueprint.md
@@ -0,0 +1,101 @@
+```
+Menu = CLI Commands/quart-imp blueprint
+Title = Generate a Quart-Imp Blueprint
+```
+
+Quart-Imp has its own type of blueprint. It comes with some methods to auto import routes, and nested blueprints etc...
+see [ImpBlueprint / Introduction](impblueprint-introduction.html) for more information.
+
+You have the option to generate a regular template rendering blueprint, or a API blueprint that returns a JSON response.
+
+```bash
+quart-imp blueprint --help
+```
+or
+```bash
+quart-imp api-blueprint --help
+```
+
+To generate a Quart-Imp blueprint, run the following command:
+
+```bash
+quart-imp blueprint
+```
+or
+```bash
+quart-imp api-blueprint
+```
+
+After running this command, you will be prompted to enter the location of where you want to create your blueprint:
+
+```text
+~ $ quart-imp blueprint
+(Creation is relative to the current working directory)
+Folder to create blueprint in [Current Working Directory]:
+```
+
+As detailed in the prompt, the creation of the blueprint is relative to the current working directory. So to create a
+blueprint in the folder `app/blueprints`, you would enter `app/blueprints` in the prompt.
+
+```text
+~ $ quart-imp blueprint
+(Creation is relative to the current working directory)
+Folder to create blueprint in [Current Working Directory]: app/blueprints
+```
+
+You will then be prompted to enter a name for your blueprint:
+
+```text
+~ $ quart-imp blueprint
+...
+Name of the blueprint to create [my_new_blueprint]:
+```
+
+The default name is 'my_new_blueprint', we will change this to 'admin'
+
+```text
+~ $ quart-imp blueprint
+...
+Name of the blueprint to create [my_new_blueprint]: admin
+```
+
+After creating your blueprint, the folder structure will look like this:
+
+```text
+app/
+├── blueprints
+│ └── admin
+│ ├── routes
+│ │ └── index.py
+│ │
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ │
+│ ├── templates
+│ │ └── www
+│ │ ├── extends
+│ │ │ └── main.html
+│ │ ├── includes
+│ │ │ ├── footer.html
+│ │ │ └── header.html
+│ │ └── index.html
+│ │
+│ └── __init__.py
+│
+...
+```
+
+This is a self-contained blueprint, so it has its own static, templates and routes folders.
+You can now navigate '/admin'
+
+You can streamline this process by specifying the name of the blueprint, the folder to
+create it in and the configuration to use, like so:
+
+```bash
+quart-imp blueprint -n admin -f app/blueprints
+```
\ No newline at end of file
diff --git a/docs/_md/v1/CLI Commands-quart-imp init.md b/docs/_md/v1/CLI Commands-quart-imp init.md
new file mode 100644
index 0000000..dd2653b
--- /dev/null
+++ b/docs/_md/v1/CLI Commands-quart-imp init.md
@@ -0,0 +1,215 @@
+```
+Menu = CLI Commands/quart-imp init
+Title = Initialising a Quart-Imp Project
+```
+
+Quart-Imp has a cli command that deploys a new ready-to-go project.
+This project is structured in a way to give you the best idea of
+how to use Quart-Imp.
+
+```bash
+quart-imp init --help
+```
+
+## Create a new project
+
+Make sure you are in the virtual environment, and at the root of your
+project folder, then run the following command:
+
+```bash
+quart-imp init
+```
+
+After running this command, you will be prompted to choose what type of
+app you want to deploy:
+
+```text
+~ $ quart-imp init
+What type of app would you like to create? (minimal, slim, full) [minimal]:
+```
+
+See below for the differences between the app types.
+
+After this, you will be prompted to enter a name for your app:
+
+```text
+~ $ quart-imp init
+...
+What would you like to call your app? [app]:
+```
+
+'app' is the default name, so if you just press enter, your app will be
+called 'app'. You will then see this output:
+
+```text
+~ FILES CREATED WILL LOOP OUT HERE ~
+
+===================
+Quart app deployed!
+===================
+
+Your app has the default name of 'app'
+Quart will automatically look for this!
+Run: quart run --debug
+
+```
+
+If you called your app something other than 'app', like 'new' for example, you will see:
+
+```text
+~ FILES CREATED WILL LOOP OUT HERE ~
+
+===================
+Quart app deployed!
+===================
+
+Your app has the name of 'new'
+Run: quart --app new run --debug
+
+```
+
+As you can see from the output, it gives you instructions on how to start your app,
+depending on the name you gave it.
+
+You should see a new folder that has been given the name you specified in
+the `quart-imp init` command.
+
+### Additional options
+
+You can also specify a name for your app in the command itself, like so:
+
+```bash
+quart-imp init -n my_app
+```
+
+This will create a new app called 'my_app'.
+The default will be a minimal app, this has no blueprints.
+
+You can also deploy a slim app, that will have one blueprint, like so:
+
+```bash
+quart-imp init -n my_app --slim
+```
+
+You can also deploy a full app that is setup for multiple blueprints, like so:
+
+```bash
+quart-imp init -n my_app --full
+```
+
+## init Folder structures
+
+### Minimal app (default)
+
+`quart-imp init --minimal`:
+
+```text
+app/
+├── resources
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── favicon.ico
+│ ├── templates
+│ │ └── index.html
+│ └── routes.py
+│
+└── __init__.py
+```
+
+### Slim app
+
+`quart-imp init --slim`:
+
+```text
+app/
+├── extensions
+│ └── __init__.py
+│
+├── resources
+│ ├── cli
+│ │ └── cli.py
+│ ├── error_handlers
+│ │ └── error_handlers.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── favicon.ico
+│ └── templates
+│ └── error.html
+│
+├── www
+│ ├── __init__.py
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ └── templates
+│ └── www
+│ ├── extends
+│ │ └── main.html
+│ ├── includes
+│ │ ├── footer.html
+│ │ └── header.html
+│ └── index.html
+│
+└── __init__.py
+```
+
+### Full app
+
+`quart-imp init --full`:
+
+```text
+app/
+├── blueprints
+│ └── www
+│ ├── __init__.py
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ └── templates
+│ └── www
+│ ├── extends
+│ │ └── main.html
+│ ├── includes
+│ │ ├── footer.html
+│ │ └── header.html
+│ └── index.html
+│
+├── extensions
+│ └── __init__.py
+│
+├── resources
+│ ├── cli
+│ │ └── cli.py
+│ ├── context_processors
+│ │ └── context_processors.py
+│ ├── error_handlers
+│ │ └── error_handlers.py
+│ ├── filters
+│ │ └── filters.py
+│ ├── routes
+│ │ └── routes.py
+│ ├── static
+│ │ └── favicon.ico
+│ └── templates
+│ └── error.html
+│
+└── __init__.py
+```
diff --git a/docs/_md/v1/Imp-Introduction.md b/docs/_md/v1/Imp-Introduction.md
new file mode 100644
index 0000000..46144f6
--- /dev/null
+++ b/docs/_md/v1/Imp-Introduction.md
@@ -0,0 +1,84 @@
+```
+Menu = Imp/Introduction
+Title = Quart-Imp Introduction
+```
+
+Quart-Imp is a Quart extension that provides auto import methods for various Quart resources. It will import
+blueprints, and other resources. It uses the importlib module to achieve this.
+
+Quart-Imp favors the application factory pattern as a project structure, and is opinionated towards using
+Blueprints. However, you can use Quart-Imp without using Blueprints.
+
+Here's an example of a standard Quart-Imp project structure:
+
+```text
+app/
+├── blueprints/
+│ ├── admin/...
+│ ├── api/...
+│ └── www/...
+├── resources/
+│ ├── filters/...
+│ ├── context_processors/...
+│ ├── static/...
+│ └── templates/...
+└── __init__.py
+```
+
+Here's an example of the `app/__init__.py` file:
+
+```python
+from quart import Quart
+from quart_sqlalchemy import SQLAlchemy
+from quart_imp import Imp
+from quart_imp.config import QuartConfig, ImpConfig
+
+db = SQLAlchemy()
+imp = Imp()
+
+
+def create_app():
+ app = Quart(__name__)
+ QuartConfig(
+ secret_key="super_secret_key",
+ app_instance=app,
+ )
+
+ imp.init_app(app, config=ImpConfig(
+ init_session={"logged_in": False},
+ ))
+ imp.import_app_resources("resources")
+ imp.import_blueprints("blueprints")
+
+ db.init_app(app)
+
+ return app
+```
+
+The Quart configuration can be loaded from any standard Quart configuration method, or from the `QuartConfig` class
+shown above.
+
+This class contains the standard Quart configuration options found in the Quart documentation.
+
+The `ImpConfig` class is used to configure the `Imp` instance.
+
+The `init_session` option of the `ImpConfig` class is used to set the initial session variables for the Quart app.
+This happens before the request is processed.
+
+`ImpConfig` also has the ability to set `SQLALCHEMY_DATABASE_URI` and `SQLALCHEMY_BINDS`
+
+For more information about the configuration setting see
+[quart_imp_config-impconfig.md](quart_imp_config-impconfig.html).
+
+`import_app_resources` will walk one level deep into the `resources` folder, and import
+all `.py` files as modules.
+It will also check for the existence of a `static` and `templates` folder, and register them with the Quart app.
+
+There is a couple of options for `import_app_resources` to control what
+is imported, see: [Imp / import_app_resources](imp-import_app_resources.html)
+
+`import_blueprints` expects a folder that contains many Blueprint as Python packages.
+It will check each blueprint folder's `__init__.py` file for an instance of a Quart Blueprint or a
+Quart-Imp Blueprint. That instant will then be registered with the Quart app.
+
+See more about how importing blueprints work here: [ImpBlueprint / Introduction](impblueprint-introduction.html)
diff --git a/docs/_md/v1/Imp-import_app_resources.md b/docs/_md/v1/Imp-import_app_resources.md
new file mode 100644
index 0000000..3b52423
--- /dev/null
+++ b/docs/_md/v1/Imp-import_app_resources.md
@@ -0,0 +1,106 @@
+```
+Menu = Imp/import_app_resources
+Title = Imp.import_app_resources
+```
+
+```python
+import_app_resources(
+ folder: str = "resources",
+ factories: Optional[List] = None,
+ static_folder: str = "static",
+ templates_folder: str = "templates",
+ files_to_import: Optional[List] = None,
+ folders_to_import: Optional[List] = None,
+ ) -> None
+```
+
+---
+
+Import standard app resources from the specified folder.
+
+This will import any resources that have been set to the Quart app.
+
+Routes, context processors, cli, etc.
+
+**Can only be called once.**
+
+If no static and or template folder is found, the static and or template folder will be set to None in the Quart app
+config.
+
+#### Small example of usage:
+
+```python
+imp.import_app_resources(folder="resources")
+# or
+imp.import_app_resources()
+# as the default folder is "resources"
+```
+
+Folder Structure: `resources`
+
+```text
+app
+├── resources
+│ ├── routes.py
+│ ├── app_fac.py
+│ ├── static
+│ │ └── css
+│ │ └── style.css
+│ └── templates
+│ └── index.html
+└── ...
+...
+```
+
+File: `routes.py`
+
+```python
+from quart import current_app as app
+from quart import render_template
+
+
+@app.route("/")
+async def index():
+ return await render_template("index.html")
+```
+
+#### How factories work
+
+Factories are functions that are called when importing the app resources. Here's an example:
+
+```python
+imp.import_app_resources(
+ folder="resources",
+ factories=["development_cli"]
+)
+```
+
+`["development_cli"]` => `development_cli(app)` function will be called, and the current app will be passed in.
+
+File: `app_fac.py`
+
+```python
+def development_cli(app):
+ @app.cli.command("dev")
+ def dev():
+ print("dev cli command")
+```
+
+#### Scoping imports
+
+By default, all files and folders will be imported.
+
+To disable this, set `files_to_import` and or
+`folders_to_import` to `[None]`.
+
+```python
+imp.import_app_resources(scope_import=[None], folders_to_import=[None])
+```
+
+To scope the imports, set the `files_to_import` and or `folders_to_import` to a list of files and or folders.
+
+`files_to_import=["cli.py", "routes.py"]` => will only import the files `resources/cli.py`
+and `resources/routes.py`
+
+`folders_to_import=["template_filters", "context_processors"]` => will import all files in the folders
+`resources/template_filters/*.py` and `resources/context_processors/*.py`
diff --git a/docs/_md/v1/Imp-import_blueprint.md b/docs/_md/v1/Imp-import_blueprint.md
new file mode 100644
index 0000000..b3d165b
--- /dev/null
+++ b/docs/_md/v1/Imp-import_blueprint.md
@@ -0,0 +1,121 @@
+```
+Menu = Imp/import_blueprint
+Title = Imp.import_blueprint
+```
+
+```python
+import_blueprint(self, blueprint: str) -> None
+```
+
+---
+
+Import a specified Quart-Imp or standard Quart Blueprint relative to the Quart app root.
+
+
+```text
+app
+├── my_blueprint
+│ ├── ...
+│ └── __init__.py
+├── ...
+└── __init__.py
+```
+
+File: `app/__init__.py`
+
+```python
+from quart import Quart
+
+from quart_imp import Imp
+
+imp = Imp()
+
+
+def create_app():
+ app = Quart(__name__)
+ imp.init_app(app)
+
+ imp.import_blueprint("my_blueprint")
+
+ return app
+```
+
+Quart-Imp Blueprints have the ability to auto import resources, and initialize session variables.
+
+For more information on how Quart-Imp Blueprints work, see the [ImpBlueprint / Introduction](impblueprint-introduction.html)
+
+##### Example of 'my_blueprint' as a Quart-Imp Blueprint:
+
+```text
+app
+├── my_blueprint
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ └── css
+│ │ └── style.css
+│ ├── templates
+│ │ └── my_blueprint
+│ │ └── index.html
+│ ├── __init__.py
+│ └── config.toml
+└── ...
+```
+
+File: `__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(
+ __name__,
+ ImpBlueprintConfig(
+ enabled=True,
+ url_prefix="/my-blueprint",
+ static_folder="static",
+ template_folder="templates",
+ static_url_path="/static/my_blueprint",
+ init_session={"my_blueprint": "session_value"},
+ ),
+)
+
+bp.import_resources("routes")
+```
+
+File: `routes / index.py`
+
+```python
+from .. import bp
+
+
+@bp.route("/")
+async def index():
+ return "regular_blueprint"
+```
+
+##### Example of 'my_blueprint' as a standard Quart Blueprint:
+
+```text
+app
+├── my_blueprint
+│ ├── ...
+│ └── __init__.py
+└── ...
+```
+
+File: `__init__.py`
+
+```python
+from quart import Blueprint
+
+bp = Blueprint("my_blueprint", __name__, url_prefix="/my-blueprint")
+
+
+@bp.route("/")
+async def index():
+ return "regular_blueprint"
+```
+
+Both of the above examples will work with `imp.import_blueprint("my_blueprint")`, they will be registered
+with the Quart app, and will be accessible via `url_for("my_blueprint.index")`.
\ No newline at end of file
diff --git a/docs/_md/v1/Imp-import_blueprints.md b/docs/_md/v1/Imp-import_blueprints.md
new file mode 100644
index 0000000..333da2e
--- /dev/null
+++ b/docs/_md/v1/Imp-import_blueprints.md
@@ -0,0 +1,50 @@
+```
+Menu = Imp/import_blueprints
+Title = Imp.import_blueprints
+```
+
+```python
+import_blueprints(self, folder: str) -> None
+```
+
+---
+
+Import all Quart-Imp or standard Quart Blueprints from a specified folder relative to the Quart app root.
+
+```text
+app/
+├── blueprints/
+│ ├── admin/
+│ │ ├── ...
+│ │ └── __init__.py
+│ ├── www/
+│ │ ├── ...
+│ │ └── __init__.py
+│ └── api/
+│ ├── ...
+│ └── __init__.py
+├── ...
+└── __init__.py
+```
+
+File: `app/__init__.py`
+
+```python
+from quart import Quart
+
+from quart_imp import Imp
+
+imp = Imp()
+
+
+def create_app():
+ app = Quart(__name__)
+ imp.init_app(app)
+
+ imp.import_blueprints("blueprints")
+
+ return app
+```
+
+This will import all Blueprints from the `blueprints` folder using the `Imp.import_blueprint` method.
+See [Imp / import_blueprint](imp-import_blueprint.html) for more information.
\ No newline at end of file
diff --git a/docs/_md/v1/Imp-init_app-init.md b/docs/_md/v1/Imp-init_app-init.md
new file mode 100644
index 0000000..7c334c0
--- /dev/null
+++ b/docs/_md/v1/Imp-init_app-init.md
@@ -0,0 +1,22 @@
+```
+Menu = Imp/init_app, __init__
+Title = Imp.init_app, __init__
+```
+
+```python
+def init_app(
+ app: Quart,
+ config: ImpConfig
+) -> None:
+# -or-
+Imp(
+ app: Quart,
+ config: ImpConfig
+)
+```
+
+---
+
+Initializes the quart app to work with quart-imp.
+
+See [quart_imp_config-impconfig.md](quart_imp_config-impconfig.html) for more information on the `ImpConfig` class.
diff --git a/docs/_md/v1/Imp-init_session.md b/docs/_md/v1/Imp-init_session.md
new file mode 100644
index 0000000..6c672c6
--- /dev/null
+++ b/docs/_md/v1/Imp-init_session.md
@@ -0,0 +1,48 @@
+```
+Menu = Imp/init_session
+Title = Imp.init_session
+```
+
+```python
+init_session() -> None
+```
+
+---
+
+Initialize the session variables found in the config. Commonly used in `app.before_request`.
+
+```python
+@app.before_request
+async def before_request():
+ imp._init_session()
+```
+
+File: `config.toml`
+
+```toml
+...
+[SESSION]
+logged_in = false
+...
+```
+
+`logged_in` is now available in the session.
+
+```python
+@app.route('/get-session-value')
+async def login():
+ print(session['logged_in'])
+ return "Check Terminal"
+```
+
+`Output: False`
+
+Can also be used to reset the values in the session. Here's an example:
+
+```python
+@app.route('/logout')
+async def logout():
+ session.clear()
+ imp._init_session()
+ return redirect(url_for('index'))
+```
\ No newline at end of file
diff --git a/docs/_md/v1/ImpBlueprint-Introduction.md b/docs/_md/v1/ImpBlueprint-Introduction.md
new file mode 100644
index 0000000..7c8247c
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-Introduction.md
@@ -0,0 +1,68 @@
+```
+Menu = ImpBlueprint/Introduction
+Title = Quart-Imp Blueprint Introduction
+```
+
+The Quart-Imp Blueprint inherits from the Quart Blueprint class, then adds some additional methods to allow for auto
+importing of resources and other nested blueprints.
+
+The Quart-Imp Blueprint requires you to provide the `ImpBlueprintConfig` class as the second argument to the Blueprint.
+
+Here's an example of a Quart-Imp Blueprint structure:
+
+```text
+www/
+├── nested_blueprints/
+│ ├── blueprint_one/
+│ │ ├── ...
+│ │ └── __init__.py
+│ └── blueprint_two/
+│ ├── ...
+│ └── __init__.py
+├── standalone_nested_blueprint/
+│ ├── ...
+│ └── __init__.py
+├── routes/
+│ └── index.py
+├── static/
+│ └── ...
+├── templates/
+│ └── www/
+│ └── index.html
+└── __init__.py
+```
+
+File: `__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(__name__, ImpBlueprintConfig(
+ enabled=True,
+ url_prefix="/www",
+ static_folder="static",
+ template_folder="templates",
+ init_session={"logged_in": False},
+))
+
+bp.import_resources("routes")
+bp.import_nested_blueprints("nested_blueprints")
+bp.import_nested_blueprint("standalone_nested_blueprint")
+```
+
+The `ImpBlueprintConfig` class is used to configure the Blueprint. It provides a little more flexibility than the
+standard Quart Blueprint configuration, like the ability to enable or disable the Blueprint.
+
+`ImpBlueprintConfig`'s `init_session` works the same as `ImpConfig`'s `init_session`, this will add the session data to
+the Quart app's session object on initialization of the Quart app.
+
+To see more about configuration see: [quart_imp.config / ImpBlueprintConfig](quart_imp_config-impblueprintconfig.html)
+
+`import_resources` method will walk one level deep into the `routes` folder, and import all `.py` files as modules.
+For more information see: [ImpBlueprint / import_resources](impblueprint-import_resources.html)
+
+`import_nested_blueprints` will do the same as `imp.import_blueprints`, but will register the blueprints found as
+nested to the current blueprint. For example `www.blueprint_one.index`
+
+`import_nested_blueprint` behaves the same as `import_nested_blueprints`, but will only import a single blueprint.
diff --git a/docs/_md/v1/ImpBlueprint-import_nested_blueprint.md b/docs/_md/v1/ImpBlueprint-import_nested_blueprint.md
new file mode 100644
index 0000000..0a69bbc
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-import_nested_blueprint.md
@@ -0,0 +1,78 @@
+```
+Menu = ImpBlueprint/import_nested_blueprint
+Title = ImpBlueprint.import_nested_blueprint
+```
+
+```python
+import_nested_blueprint(self, blueprint: str) -> None
+```
+
+---
+
+Import a specified Quart-Imp or standard Quart Blueprint relative to the Blueprint root.
+
+Works the same as [Imp / import_blueprint](imp-import_blueprint.html) but relative to the Blueprint root.
+
+Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
+
+`url_for('my_blueprint.my_nested_blueprint.index')`
+
+```text
+my_blueprint/
+├── routes/...
+├── static/...
+├── templates/...
+│
+├── my_nested_blueprint/
+│ ├── routes/
+│ │ └── index.py
+│ ├── static/...
+│ ├── templates/...
+│ ├── __init__.py
+│
+├── __init__.py
+```
+
+File: `my_blueprint/__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(__name__, ImpBlueprintConfig(
+ enabled=True,
+ static_folder="static",
+ template_folder="templates",
+))
+
+bp.import_resources("routes")
+bp.import_nested_blueprint("my_nested_blueprint")
+```
+
+File: `my_blueprint/my_nested_blueprint/__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(__name__, ImpBlueprintConfig(
+ enabled=True,
+ static_folder="static",
+ template_folder="templates",
+))
+
+bp.import_resources("routes")
+```
+
+File: `my_blueprint/my_nested_blueprint/routes/index.py`
+
+```python
+from quart import render_template
+
+from .. import bp
+
+
+@bp.route("/")
+async def index():
+ return await render_template(bp.tmpl("index.html"))
+```
\ No newline at end of file
diff --git a/docs/_md/v1/ImpBlueprint-import_nested_blueprints.md b/docs/_md/v1/ImpBlueprint-import_nested_blueprints.md
new file mode 100644
index 0000000..276938d
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-import_nested_blueprints.md
@@ -0,0 +1,60 @@
+```
+Menu = ImpBlueprint/import_nested_blueprints
+Title = ImpBlueprint.import_nested_blueprints
+```
+
+```python
+import_nested_blueprints(self, folder: str) -> None
+```
+
+---
+
+Will import all the Blueprints from the given folder relative to the Blueprint's root directory.
+
+Uses [Blueprint / import_nested_blueprint](blueprint-import_nested_blueprint.html) to import blueprints from
+the specified folder.
+
+Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
+
+`url_for('my_blueprint.nested_bp_one.index')`
+
+`url_for('my_blueprint.nested_bp_two.index')`
+
+`url_for('my_blueprint.nested_bp_three.index')`
+
+```text
+my_blueprint/
+├── routes/...
+├── static/...
+├── templates/...
+│
+├── nested_blueprints/
+│ │
+│ ├── nested_bp_one/
+│ │ ├── ...
+│ │ ├── __init__.py
+│ ├── nested_bp_two/
+│ │ ├── ...
+│ │ ├── __init__.py
+│ └── nested_bp_three/
+│ ├── ...
+│ ├── __init__.py
+│
+├── __init__.py
+```
+
+File: `my_blueprint/__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(__name__, ImpBlueprintConfig(
+ enabled=True,
+ static_folder="static",
+ template_folder="templates",
+))
+
+bp.import_resources("routes")
+bp.import_nested_blueprints("nested_blueprints")
+```
\ No newline at end of file
diff --git a/docs/_md/v1/ImpBlueprint-import_resources.md b/docs/_md/v1/ImpBlueprint-import_resources.md
new file mode 100644
index 0000000..65487c7
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-import_resources.md
@@ -0,0 +1,57 @@
+```
+Menu = ImpBlueprint/import_resources
+Title = ImpBlueprint.import_resources
+```
+
+```python
+import_resources(folder: str = "routes") -> None
+```
+
+---
+
+Will import all the resources (cli, routes, filters, context_processors...) from the given folder relative to the
+Blueprint's root directory.
+
+```text
+my_blueprint
+├── user_routes
+│ ├── user_dashboard.py
+│ └── user_settings.py
+├── car_routes
+│ ├── car_dashboard.py
+│ └── car_settings.py
+├── static/...
+├── templates/
+│ └── my_blueprint/
+│ ├── user_dashboard.html
+│ └── ...
+├── __init__.py
+```
+
+File: `my_blueprint/__init__.py`
+
+```python
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint(__name__, ImpBlueprintConfig(
+ enabled=True,
+ static_folder="static",
+ template_folder="templates",
+))
+
+bp.import_resources("user_routes")
+bp.import_resources("car_routes")
+```
+
+File: `my_blueprint/user_routes/user_dashboard.py`
+
+```python
+from quart import render_template
+
+from .. import bp
+
+@bp.route("/user-dashboard")
+async def user_dashboard():
+ return await render_template(bp.tmpl("user_dashboard.html"))
+```
diff --git a/docs/_md/v1/ImpBlueprint-init.md b/docs/_md/v1/ImpBlueprint-init.md
new file mode 100644
index 0000000..d5cb266
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-init.md
@@ -0,0 +1,17 @@
+```
+Menu = ImpBlueprint/__init__
+Title = Quart-Imp Blueprint __init__
+```
+
+```python
+ImpBlueprint(dunder_name: str, config: ImpBlueprintConfig) -> None
+```
+
+---
+
+Initializes the Quart-Imp Blueprint.
+
+`dunder_name` should always be set to `__name__`
+
+`config` is an instance of `ImpBlueprintConfig` that will be used to load the Blueprint's configuration.
+See [quart_imp.config / ImpBlueprintConfig](quart_imp_config-impblueprintconfig.html) for more information.
diff --git a/docs/_md/v1/ImpBlueprint-tmpl.md b/docs/_md/v1/ImpBlueprint-tmpl.md
new file mode 100644
index 0000000..d3e94d6
--- /dev/null
+++ b/docs/_md/v1/ImpBlueprint-tmpl.md
@@ -0,0 +1,44 @@
+```
+Menu = ImpBlueprint/tmpl
+Title = ImpBlueprint.tmpl
+```
+
+```python
+tmpl(template: str) -> str
+```
+
+---
+
+Scopes the template lookup to the name of the blueprint (this takes from the `__name__` attribute of the Blueprint).
+
+Due to the way Quart templating works, and to avoid template name collisions.
+It is standard practice to place the name of the Blueprint in the template path,
+then to place any templates under that folder.
+
+```text
+my_blueprint/
+├── routes/
+│ └── index.py
+├── static/...
+│
+├── templates/
+│ └── my_blueprint/
+│ └── index.html
+│
+├── __init__.py
+```
+
+File: `my_blueprint/routes/index.py`
+
+```python
+from quart import render_template
+
+from .. import bp
+
+
+@bp.route("/")
+async def index():
+ return await render_template(bp.tmpl("index.html"))
+```
+
+`bp.tmpl("index.html")` will output `"my_blueprint/index.html"`.
diff --git a/docs/_md/v1/__index__.md b/docs/_md/v1/__index__.md
new file mode 100644
index 0000000..7f496f5
--- /dev/null
+++ b/docs/_md/v1/__index__.md
@@ -0,0 +1,121 @@
+# Welcome to the Quart-Imp Documentation
+
+## What is Quart-Imp?
+
+Quart-Imp's main purpose is to help simplify the importing of blueprints, and resources. It has a few extra
+features built in to help with securing pages and password authentication.
+
+## Install Quart-Imp
+
+```bash
+pip install quart-imp
+```
+
+## Getting Started
+
+To get started right away, you can use the CLI commands to create a new Quart-Imp project.
+
+```bash
+quart-imp init
+```
+
+### Minimal Quart-Imp Setup
+
+Run the following command to create a minimal Quart-Imp project.
+
+```bash
+quart-imp init -n app --minimal
+```
+
+See [CLI Commands / quart-imp init](cli_commands-quart-imp_init.html) for more information.
+
+### The minimal structure
+
+#### Folder Structure
+
+```text
+app/
+├── resources/
+│ ├── static/...
+│ ├── templates/
+│ │ └── index.html
+│ └── index.py
+└── __init__.py
+```
+
+File: `app/__init__.py`
+
+```python
+from quart import Quart
+
+from quart_imp import Imp
+from quart_imp.config import QuartConfig, ImpConfig
+
+imp = Imp()
+
+
+def create_app():
+ app = Quart(__name__, static_url_path="/")
+ QuartConfig(
+ secret_key="secret_key",
+ app_instance=app
+ )
+
+ imp.init_app(app, ImpConfig())
+
+ imp.import_app_resources()
+ # Takes argument 'folder' default folder is 'resources'
+
+ return app
+```
+
+File: `app/resources/routes.py`
+
+```python
+from quart import current_app as app
+from quart import render_template
+
+
+@app.route("/")
+async def index():
+ return await render_template("index.html")
+```
+
+File: `app/resources/templates/index.html`
+
+```html
+
+
+
+
+ Quart-Imp
+
+
+Quart-Imp
+
+
+```
+
+---
+
+Setting up a virtual environment is recommended.
+
+**Linux / Darwin**
+
+```bash
+python3 -m venv venv
+```
+
+```bash
+source venv/bin/activate
+```
+
+**Windows**
+
+```bash
+python -m venv venv
+```
+
+```text
+.\venv\Scripts\activate
+```
\ No newline at end of file
diff --git a/docs/_md/v1/__menu__.md b/docs/_md/v1/__menu__.md
new file mode 100644
index 0000000..2aea2cd
--- /dev/null
+++ b/docs/_md/v1/__menu__.md
@@ -0,0 +1,43 @@
+- CLI Commands
+ - quart-imp init
+ - quart-imp blueprint
+- Imp
+ - Introduction
+ - init_app, __init__
+ - init_session
+ - import_app_resources
+ - import_blueprint
+ - import_blueprints
+- ImpBlueprint
+ - Introduction
+ - __init__
+ - init_session
+ - import_resources
+ - import_nested_blueprint
+ - import_nested_blueprints
+ - tmpl
+- quart_imp.config
+ - QuartConfig
+ - ImpConfig
+ - ImpBlueprintConfig
+ - DatabaseConfig
+ - SQLDatabaseConfig
+ - SQLiteDatabaseConfig
+- quart_imp.security
+ - login_check
+ - permission_check
+ - pass_function_check
+ - api_login_check
+ - include_csrf
+- quart_imp.auth
+ - encrypt_password
+ - authenticate_password
+ - generate_password
+ - generate_salt
+ - generate_csrf_token
+ - generate_private_key
+ - generate_email_validator
+ - generate_numeric_validator
+ - generate_alphanumeric_validator
+ - is_email_address_valid
+ - is_username_valid
diff --git a/docs/_md/v1/quart_imp_auth-authenticate_password.md b/docs/_md/v1/quart_imp_auth-authenticate_password.md
new file mode 100644
index 0000000..3efdadc
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-authenticate_password.md
@@ -0,0 +1,55 @@
+```
+Menu = quart_imp.auth/authenticate_password
+Title = authenticate_password - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import authenticate_password
+```
+
+```python
+authenticate_password(
+ input_password: str,
+ database_password: str,
+ database_salt: str,
+ encryption_level: int = 512,
+ pepper_length: int = 1,
+ pepper_position: t.Literal["start", "end"] = "end"
+) -> bool
+```
+
+---
+
+For use in password hashing.
+
+To be used alongside the [quart_imp.auth / encrypt_password](quart_imp_auth-encrypt_password.html) function.
+
+Takes the plain input password, the stored hashed password along with the stored salt
+and will try every possible combination of pepper values to find a match.
+
+**Note:**
+
+- You must know the pepper length used to hash the password.
+- You must know the position of the pepper used to hash the password.
+- You must know the encryption level used to hash the password.
+
+#### Authentication Scenario:
+
+```
+Plain password: "password"
+Generated salt: "^%$*" (randomly generated)
+Generated pepper (length 1): "A" (randomly generated)
+Pepper position: "end"
+```
+
+```python
+input_password = "password"
+database_password = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..." # pulled from database
+database_salt = "^%$*" # pulled from database
+
+authenticate_password(
+ input_password,
+ database_password,
+ database_salt
+) # >>> True
+```
diff --git a/docs/_md/v1/quart_imp_auth-encrypt_password.md b/docs/_md/v1/quart_imp_auth-encrypt_password.md
new file mode 100644
index 0000000..56d76c4
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-encrypt_password.md
@@ -0,0 +1,53 @@
+```
+Menu = quart_imp.auth/encrypt_password
+Title = encrypt_password - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import encrypt_password
+```
+
+```python
+encrypt_password(
+ password: str,
+ salt: str,
+ encryption_level: int = 512,
+ pepper_length: int = 1,
+ pepper_position: t.Literal["start", "end"] = "end"
+) -> str
+```
+
+---
+
+For use in password hashing.
+
+To be used alongside the [quart_imp.auth / authenticate_password](quart_imp_auth-authenticate_password.html) function.
+
+Takes the plain password, applies a pepper, salts it, then produces a digested sha512 or sha256 if specified.
+
+Can set the encryption level to 256 or 512, defaults to 512.
+
+Can set the pepper length, defaults to 1. Max is 3.
+
+Can set the pepper position, "start" or "end", defaults to "end".
+
+**Note:**
+
+- You must inform the authenticate_password function of the pepper length used to hash the password.
+- You must inform the authenticate_password function of the position of the pepper used to hash the password.
+- You must inform the authenticate_password function of the encryption level used to hash the password.
+
+#### Encryption Scenario:
+
+```
+Plain password: "password"
+Generated salt: "^%$*" (randomly generated)
+Generated pepper (length 1): "A" (randomly generated)
+Pepper position: "end"
+```
+
+1. Pepper is added to the end of the plain password: "passwordA"
+2. Salt is added to the end of the peppered password: "passwordA^%$*"
+3. Password is hashed: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..."
+4. Salt and hashed password are then stored in the database.
+
diff --git a/docs/_md/v1/quart_imp_auth-generate_alphanumeric_validator.md b/docs/_md/v1/quart_imp_auth-generate_alphanumeric_validator.md
new file mode 100644
index 0000000..6516b65
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_alphanumeric_validator.md
@@ -0,0 +1,24 @@
+```
+Menu = quart_imp.auth/generate_alphanumeric_validator
+Title = generate_alphanumeric_validator - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_alphanumeric_validator
+```
+
+```python
+generate_alphanumeric_validator(length: int = 8) -> str
+```
+
+---
+
+Generates a random alphanumeric string of the given length.
+
+(letters are capitalized)
+
+##### Example:
+
+```python
+generate_alphanumeric_validator(8) # >>> 'A1B2C3D4'
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_auth-generate_csrf_token.md b/docs/_md/v1/quart_imp_auth-generate_csrf_token.md
new file mode 100644
index 0000000..724fb06
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_csrf_token.md
@@ -0,0 +1,26 @@
+```
+Menu = quart_imp.auth/generate_csrf_token
+Title = generate_csrf_token - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_csrf_token
+```
+
+```python
+generate_csrf_token() -> str
+```
+
+---
+
+Generates a SHA1 using the current date and time.
+
+For use in Cross-Site Request Forgery.
+
+Also used by the [quart_imp.security / csrf_protect](quart_imp_security-include_csrf.html) decorator.
+
+##### Example:
+
+```python
+generate_csrf_token() # >>> 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0'
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_auth-generate_email_validator.md b/docs/_md/v1/quart_imp_auth-generate_email_validator.md
new file mode 100644
index 0000000..95e7ca5
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_email_validator.md
@@ -0,0 +1,27 @@
+```
+Menu = quart_imp.auth/generate_email_validator
+Title = generate_email_validator - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_email_validator
+```
+
+```python
+generate_email_validator() -> str
+```
+
+---
+
+Uses `generate_alphanumeric_validator` with a length of 8 to
+generate a random alphanumeric value for the specific use of
+validating accounts via email.
+
+See [quart_imp.auth / generate_alphanumeric_validator](quart_imp_auth-generate_alphanumeric_validator.html)
+for more information.
+
+##### Example:
+
+```python
+generate_email_validator() # >>> 'A1B2C3D4'
+```
diff --git a/docs/_md/v1/quart_imp_auth-generate_numeric_validator.md b/docs/_md/v1/quart_imp_auth-generate_numeric_validator.md
new file mode 100644
index 0000000..f9aeb1b
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_numeric_validator.md
@@ -0,0 +1,27 @@
+```
+Menu = quart_imp.auth/generate_numeric_validator
+Title = generate_numeric_validator - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_numeric_validator
+```
+
+```python
+generate_numeric_validator(length: int) -> int
+```
+
+---
+
+
+Generates random choice between 1 * (length) and 9 * (length).
+
+If the length is 4, it will generate a number between 1111 and 9999.
+
+For use in MFA email, or unique filename generation.
+
+##### Example:
+
+```python
+generate_numeric_validator(4) # >>> 1234
+```
diff --git a/docs/_md/v1/quart_imp_auth-generate_password.md b/docs/_md/v1/quart_imp_auth-generate_password.md
new file mode 100644
index 0000000..08e293a
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_password.md
@@ -0,0 +1,26 @@
+```
+Menu = quart_imp.auth/generate_password
+Title = generate_password - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_password
+```
+
+```python
+generate_password(style: str = "mixed", length: int = 3) -> str
+```
+
+---
+
+Generates a password of (length) characters.
+
+The Default length is 3.
+
+Style options: "animals", "colors", "mixed" - defaults to "mixed"
+
+##### Example:
+
+```python
+generate_password(style="animals", length=3) # >>> 'Cat-Goat-Pig12'
+```
diff --git a/docs/_md/v1/quart_imp_auth-generate_private_key.md b/docs/_md/v1/quart_imp_auth-generate_private_key.md
new file mode 100644
index 0000000..037d6e1
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_private_key.md
@@ -0,0 +1,38 @@
+```
+Menu = quart_imp.auth/generate_private_key
+Title = generate_private_key - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_private_key
+```
+
+```python
+generate_private_key(hook: t.Optional[str]) -> str
+```
+
+---
+
+Generates a sha256 private key from a passed in hook value.
+
+If no hook is passed in, it will generate a hook using datetime.now() and a
+random number between 1 and 1000.
+
+```python
+@app.route('/register', methods=['GET', 'POST'])
+async def register():
+ if request.method == "POST":
+ ...
+ salt = generate_salt()
+ password = request.form.get('password')
+ encrypted_password = encrypt_password(password, salt)
+ ...
+ user = User(
+ username=username,
+ email=email,
+ password=encrypted_password,
+ salt=salt,
+ private_key=generate_private_key(hook=username)
+ )
+ ...
+```
diff --git a/docs/_md/v1/quart_imp_auth-generate_salt.md b/docs/_md/v1/quart_imp_auth-generate_salt.md
new file mode 100644
index 0000000..4eee480
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-generate_salt.md
@@ -0,0 +1,46 @@
+```
+Menu = quart_imp.auth/generate_salt
+Title = generate_salt - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import generate_salt
+```
+
+```python
+generate_salt(length: int = 4) -> str
+```
+
+---
+
+Generates a string of (length) characters of punctuation.
+
+The Default length is 4.
+
+For use in password hashing and storage of passwords in the database.
+
+##### Example:
+
+```python
+generate_salt() # >>> '*!$%'
+```
+
+```python
+@app.route('/register', methods=['GET', 'POST'])
+async def register():
+ if request.method == "POST":
+ ...
+ salt = generate_salt()
+ password = request.form.get('password')
+ encrypted_password = encrypt_password(password, salt)
+ ...
+
+ user = User(
+ username=username,
+ email=email,
+ password=encrypted_password,
+ salt=salt
+ )
+ ...
+```
+
diff --git a/docs/_md/v1/quart_imp_auth-is_email_address_valid.md b/docs/_md/v1/quart_imp_auth-is_email_address_valid.md
new file mode 100644
index 0000000..6f11cff
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-is_email_address_valid.md
@@ -0,0 +1,46 @@
+```
+Menu = quart_imp.auth/is_email_address_valid
+Title = is_email_address_valid - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import is_email_address_valid
+```
+
+```python
+is_email_address_valid(
+ email_address: str
+) -> bool
+```
+
+---
+
+Checks if an email address is valid.
+
+Is not completely RFC 5322 compliant, but it is good enough for most use cases.
+
+Here are examples of mistakes that it will not catch:
+
+##### Valid but fails:
+
+```text
+email@[123.123.123.123]
+“email”@example.com
+very.unusual.“@”.unusual.com@example.com
+very.“(),:;<>[]”.VERY.“very@\\ "very”.unusual@strange.example.com
+```
+
+##### Invalid but passes:
+
+```text
+email@example.com (Joe Smith)
+email@111.222.333.44444
+```
+
+##### Example:
+
+```python
+is_email_address_valid('hello@example.com') # >>> True
+
+is_email_address_valid('hello@hello@example.com') # >>> False
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_auth-is_username_valid.md b/docs/_md/v1/quart_imp_auth-is_username_valid.md
new file mode 100644
index 0000000..0a7c370
--- /dev/null
+++ b/docs/_md/v1/quart_imp_auth-is_username_valid.md
@@ -0,0 +1,58 @@
+```
+Menu = quart_imp.auth/is_username_valid
+Title = is_username_valid - quart_imp.auth
+```
+
+```python
+from quart_imp.auth import is_username_valid
+```
+
+```python
+is_username_valid(
+ username: str,
+ allowed: t.Optional[t.List[t.Literal["all", "dot", "dash", "under"]]] = None
+) -> bool
+```
+
+---
+
+Checks if a username is valid.
+
+Valid usernames can only include letters,
+numbers, ., -, and _ but cannot begin or end with
+the last three mentioned.
+
+##### Example "all":
+
+```python
+is_username_valid("username", allowed=["all"])
+```
+
+Output:
+
+```text
+username : WILL PASS : True
+user.name : WILL PASS : True
+user-name : WILL PASS : True
+user_name : WILL PASS : True
+_user_name : WILL PASS : False
+```
+
+##### Example "dot", "dash":
+
+```python
+
+is_username_valid("username", allowed=["dot", "dash"])
+```
+
+Output:
+
+```text
+username : WILL PASS : True
+user.name : WILL PASS : True
+user-name : WILL PASS : True
+user-name.name : WILL PASS : True
+user_name : WILL PASS : False
+_user_name : WILL PASS : False
+.user.name : WILL PASS : False
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_config-impblueprintconfig.md b/docs/_md/v1/quart_imp_config-impblueprintconfig.md
new file mode 100644
index 0000000..c863479
--- /dev/null
+++ b/docs/_md/v1/quart_imp_config-impblueprintconfig.md
@@ -0,0 +1,33 @@
+```
+Menu = quart_imp.config/ImpBlueprintConfig
+Title = ImpBlueprintConfig - quart_imp.config
+```
+
+```python
+from quart_imp.config import ImpBlueprintConfig
+```
+
+```python
+ImpBlueprintConfig(
+ enabled: bool = False,
+ url_prefix: str = None,
+ subdomain: str = None,
+ url_defaults: dict = None,
+ static_folder: t.Optional[str] = None,
+ template_folder: t.Optional[str] = None,
+ static_url_path: t.Optional[str] = None,
+ root_path: str = None,
+ cli_group: str = None,
+ init_session: dict = None
+)
+```
+
+---
+
+A class that holds a Quart-Imp blueprint configuration.
+
+Most of these values are passed to the `Blueprint` class when the blueprint is registered.
+
+The `enabled` argument is used to enable or disable the blueprint. This is useful for feature flags.
+
+`init_session` is used to set the session values in the main `before_request` function.
diff --git a/docs/_md/v1/quart_imp_config-impconfig.md b/docs/_md/v1/quart_imp_config-impconfig.md
new file mode 100644
index 0000000..c38b955
--- /dev/null
+++ b/docs/_md/v1/quart_imp_config-impconfig.md
@@ -0,0 +1,32 @@
+```
+Menu = quart_imp.config/ImpConfig
+Title = ImpConfig - quart_imp.config
+```
+
+```python
+from quart_imp.config import ImpConfig
+```
+
+```python
+ImpConfig(
+ init_session: t.Optional[t.Dict[str, t.Any]] = None,
+)
+```
+
+---
+
+The `ImpConfig` class is used to set the initial session data
+that the application will use.
+
+```python
+imp_config = ImpConfig(
+ init_session={"key": "value"},
+)
+
+
+def create_app():
+ app = Quart(__name__)
+ QuartConfig(debug=True, app_instance=app)
+ imp.init_app(app, imp_config)
+ ...
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_config-quartconfig.md b/docs/_md/v1/quart_imp_config-quartconfig.md
new file mode 100644
index 0000000..b968979
--- /dev/null
+++ b/docs/_md/v1/quart_imp_config-quartconfig.md
@@ -0,0 +1,59 @@
+```
+Menu = quart_imp.config/QuartConfig
+Title = QuartConfig - quart_imp.config
+```
+
+```python
+from quart_imp.config import QuartConfig
+```
+
+```python
+QuartConfig(
+ debug: t.Optional[bool] = None,
+ propagate_exceptions: t.Optional[bool] = None,
+ trap_http_exceptions: t.Optional[bool] = None,
+ trap_bad_request_errors: t.Optional[bool] = None,
+ secret_key: t.Optional[str] = None,
+ session_cookie_name: t.Optional[str] = None,
+ session_cookie_domain: t.Optional[str] = None,
+ session_cookie_path: t.Optional[str] = None,
+ session_cookie_httponly: t.Optional[bool] = None,
+ session_cookie_secure: t.Optional[bool] = None,
+ session_cookie_samesite: t.Optional[t.Literal["Lax", "Strict"]] = None,
+ permanent_session_lifetime: t.Optional[int] = None,
+ session_refresh_each_request: t.Optional[bool] = None,
+ use_x_sendfile: t.Optional[bool] = None,
+ send_file_max_age_default: t.Optional[int] = None,
+ error_404_help: t.Optional[bool] = None,
+ server_name: t.Optional[str] = None,
+ application_root: t.Optional[str] = None,
+ preferred_url_scheme: t.Optional[str] = None,
+ max_content_length: t.Optional[int] = None,
+ templates_auto_reload: t.Optional[bool] = None,
+ explain_template_loading: t.Optional[bool] = None,
+ max_cookie_size: t.Optional[int] = None,
+ app_instance: t.Optional["Quart"] = None
+)
+```
+
+---
+
+A class that holds a Quart configuration values.
+
+You can set the configuration values to the app instance by either passing the app instance to the `app_instance`
+parameter or by calling the `apply_config` method on the `QuartConfig` instance.
+
+```python
+def create_app():
+ app = Quart(__name__)
+ QuartConfig(debug=True, app_instance=app)
+ return app
+```
+or
+```python
+def create_app():
+ app = Quart(__name__)
+ config = QuartConfig(debug=True)
+ config.apply_config(app)
+ return app
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_security-api_login_check.md b/docs/_md/v1/quart_imp_security-api_login_check.md
new file mode 100644
index 0000000..f1240c0
--- /dev/null
+++ b/docs/_md/v1/quart_imp_security-api_login_check.md
@@ -0,0 +1,47 @@
+```
+Menu = quart_imp.security/api_login_check
+Title = api_login_check - quart_imp.security
+```
+
+
+```python
+from quart_imp.security import api_login_check
+```
+
+```python
+api_login_check(
+ session_key: str,
+ values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
+ fail_json: t.Optional[t.Dict[str, t.Any]] = None
+)
+```
+
+`@api_login_check(...)`
+
+---
+
+A decorator that is used to secure API routes that return JSON responses.
+
+`session_key` The session key to check for.
+
+`values_allowed` A list of or singular value(s) that the session key must contain.
+
+`fail_json` JSON that is returned on failure. `{"error": "You are not logged in."}` by default.
+
+##### Example:
+
+```python
+@bp.route("/api/resource", methods=["GET"])
+@api_login_check('logged_in', True)
+async def api_page():
+ ...
+```
+
+##### Example of defined fail_json:
+
+```python
+@bp.route("/api/resource", methods=["GET"])
+@api_login_check('logged_in', True, fail_json={"failed": "You need to be logged in."})
+async def api_page():
+ ...
+```
diff --git a/docs/_md/v1/quart_imp_security-include_csrf.md b/docs/_md/v1/quart_imp_security-include_csrf.md
new file mode 100644
index 0000000..c86f7af
--- /dev/null
+++ b/docs/_md/v1/quart_imp_security-include_csrf.md
@@ -0,0 +1,48 @@
+```
+Menu = quart_imp.security/include_csrf
+Title = include_csrf - quart_imp.security
+```
+
+```python
+from quart_imp.security import include_csrf
+```
+
+```python
+include_csrf(
+ session_key: str = "csrf",
+ form_key: str = "csrf",
+ abort_code: int = 401
+)
+```
+
+`@include_csrf(...)`
+
+---
+
+
+A decorator that handles CSRF protection.
+
+On a **GET** request, a CSRF token is generated and stored in the session key
+specified by the session_key parameter.
+
+On a **POST** request, the form_key specified is checked against the session_key
+specified.
+
+- If they match, the request is allowed to continue.
+- If no match, the response will be abort(abort_code), default 401.
+
+```python
+@bp.route("/admin", methods=["GET", "POST"])
+@include_csrf(session_key="csrf", form_key="csrf")
+async def admin_page():
+ ...
+ # You must pass in the CSRF token from the session into the template.
+ # Then add to the form.
+ return await render_template("admin.html", csrf=session.get("csrf"))
+```
+
+Form key:
+
+```html
+
+```
\ No newline at end of file
diff --git a/docs/_md/v1/quart_imp_security-login_check.md b/docs/_md/v1/quart_imp_security-login_check.md
new file mode 100644
index 0000000..f269f4b
--- /dev/null
+++ b/docs/_md/v1/quart_imp_security-login_check.md
@@ -0,0 +1,66 @@
+```
+Menu = quart_imp.security/login_check
+Title = login_check - quart_imp.security
+```
+
+```python
+from quart_imp.security import login_check
+```
+
+```python
+login_check(
+ session_key: str,
+ values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
+ fail_endpoint: t.Optional[str] = None,
+ pass_endpoint: t.Optional[str] = None,
+ endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
+ message: t.Optional[str] = None,
+ message_category: str = "message"
+)
+```
+
+`@login_check(...)`
+
+---
+
+A decorator that checks if the specified session key exists and contains the specified value.
+
+`session_key` The session key to check for.
+
+`values_allowed` A list of or singular value(s) that the session key must contain.
+
+`fail_endpoint` The endpoint to redirect to if the session key does not exist or does not contain the specified values.
+
+`endpoint_kwargs` A dictionary of keyword arguments to pass to the redirect endpoint.
+
+`message` If a message is specified, a flash message is shown.
+
+`message_category` The category of the flash message.
+
+##### Example of a route that requires a user to be logged in:
+
+```python
+@bp.route("/admin", methods=["GET"])
+@login_check(
+ 'logged_in',
+ True,
+ fail_endpoint='blueprint.login_page',
+ message="Login needed"
+)
+async def admin_page():
+ ...
+```
+
+##### Example of a route that if the user is already logged in, redirects to the specified endpoint:
+
+```python
+@bp.route("/login-page", methods=["GET"])
+@login_check(
+ 'logged_in',
+ True,
+ pass_endpoint='blueprint.admin_page',
+ message="Already logged in"
+)
+async def login_page():
+ ...
+```
diff --git a/docs/_md/v1/quart_imp_security-pass_function_check.md b/docs/_md/v1/quart_imp_security-pass_function_check.md
new file mode 100644
index 0000000..c533f3a
--- /dev/null
+++ b/docs/_md/v1/quart_imp_security-pass_function_check.md
@@ -0,0 +1,114 @@
+```
+Menu = quart_imp.security/pass_function_check
+Title = pass_function_check - quart_imp.security
+```
+
+```python
+from quart_imp.security import pass_function_check
+```
+
+```python
+def pass_function_check(
+ function: t.Callable,
+ predefined_args: t.Optional[t.Dict] = None,
+ fail_endpoint: t.Optional[str] = None,
+ pass_endpoint: t.Optional[str] = None,
+ endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
+ message: t.Optional[str] = None,
+ message_category: str = "message",
+ fail_on_missing_kwargs: bool = False,
+ with_app_context: bool = False,
+)
+```
+
+**NOTE: This was added mostly as an experimental feature, but ended up being useful in some cases.**
+
+A decorator that takes the result of a function and checks if it is True or False.
+
+URL variables from `@route` will be read by this decorator.
+To use URL variables in your passed in function,
+make sure your functions argument(s) name(s) match the name(s) of the URL variable(s).
+
+**Example:**
+
+```python
+def check_if_number(value):
+ if isinstance(value, int):
+ return True
+ return False
+
+@bp.route("/admin-page/", methods=["GET"])
+@login_check('logged_in', True, 'blueprint.login_page') # can be mixed with login_check
+@pass_function_check(
+ check_if_number,
+ predefined_args=None,
+ fail_endpoint='www.index',
+ message="Failed message"
+)
+async def admin_page():
+ ...
+
+@bp.route("/admin-page/", methods=["GET"])
+@login_check('logged_in', True, 'blueprint.login_page') # can be mixed with login_check
+@pass_function_check(
+ check_if_number,
+ predefined_args={'value': 10},
+ fail_endpoint='www.index',
+ message="Failed message"
+)
+async def admin_page_overwrite():
+ ...
+```
+
+**Advanced use case:**
+
+Here's an example of accessing quart.session from within the passed in function. including the
+`with_app_context` parameter, the function will be called with `app_context()`.
+
+```python
+from quart import current_app
+from quart import session
+
+...
+
+def check_if_number(number=1, session_=None):
+ if session_:
+ print(session_)
+ try:
+ int(number)
+ return True
+ except ValueError:
+ return False
+
+@bp.route("/pass-func-check-with-url-var/", methods=["GET"])
+@pass_function_check(
+ check_if_number,
+ predefined_args={'number': 10, 'session_': session},
+ fail_endpoint="www.index",
+ with_app_context=True
+)
+async def admin_page_overwrite_with_session():
+ ...
+```
+
+If you pass in a predefined arg that has the same key name as a session variable that exists, the value
+of that predefined arg will be replaced with the session variable value.
+
+```python
+session['car'] = 'Toyota'
+...
+def check_function(car):
+ if car == 'Toyota':
+ return True
+ return False
+...
+@bp.route("/pass-func-check-with-url-var/", methods=["GET"])
+@pass_function_check(
+ check_function,
+ predefined_args={'car': session},
+ ...
+
+```
+
+This will pass, as pass_function_check will replace the value of the predefined arg 'car' with the value
+of the session variable 'car'.
diff --git a/docs/_md/v1/quart_imp_security-permission_check.md b/docs/_md/v1/quart_imp_security-permission_check.md
new file mode 100644
index 0000000..d6309ab
--- /dev/null
+++ b/docs/_md/v1/quart_imp_security-permission_check.md
@@ -0,0 +1,57 @@
+```
+Menu = quart_imp.security/permission_check
+Title = permission_check - quart_imp.security
+```
+
+```python
+from quart_imp.security import permission_check
+```
+
+```python
+permission_check(
+ session_key: str,
+ values_allowed: t.Union[t.List[t.Union[str, int, bool]], str, int, bool],
+ fail_endpoint: t.Optional[str] = None,
+ endpoint_kwargs: t.Optional[t.Dict[str, t.Union[str, int]]] = None,
+ message: t.Optional[str] = None,
+ message_category: str = "message"
+)
+```
+
+`@permission_check(...)`
+
+---
+
+A decorator that checks if the specified session key exists and its value(s) match the specified value(s).
+
+`session_key` The session key to check for.
+
+`values_allowed` A list of or singular value(s) that the session key must contain.
+
+`fail_endpoint` The endpoint to redirect to if the session key does not exist or does not contain the specified values.
+
+`endpoint_kwargs` A dictionary of keyword arguments to pass to the redirect endpoint.
+
+`message` If a message is specified, a flash message is shown.
+
+`message_category` The category of the flash message.
+
+##### Example:
+
+```python
+@bp.route("/admin-page", methods=["GET"])
+@login_check(
+ 'logged_in',
+ True,
+ 'blueprint.login_page'
+) # can be mixed with login_check
+@permission_check(
+ 'permissions',
+ ['admin'],
+ fail_endpoint='www.index',
+ message="Failed message"
+)
+async def admin_page():
+ ...
+```
+
diff --git a/docs/_ssg/__init__.py b/docs/_ssg/__init__.py
new file mode 100644
index 0000000..9648d99
--- /dev/null
+++ b/docs/_ssg/__init__.py
@@ -0,0 +1,3 @@
+from .compiler import compiler
+
+__all__ = ["compiler"]
diff --git a/docs/_ssg/compiler.py b/docs/_ssg/compiler.py
new file mode 100644
index 0000000..dbf0a99
--- /dev/null
+++ b/docs/_ssg/compiler.py
@@ -0,0 +1,132 @@
+import re
+import typing as t
+from pathlib import Path
+
+import mistune
+from flask import render_template
+
+from .exceptions import NoPostDefinition
+from .helpers import get_relative_files_in_the_docs_folder, pytz_dt_now, post_date
+from .render_engines import HighlightRenderer
+
+
+def _raw_markdown_processor(raw_markdown: str) -> tuple[t.Optional[list], str, str]:
+ """
+ :param raw_markdown: The raw markdown to process
+ :return: publish: bool, date: str, title: str, description: str, post: str
+ """
+ if not raw_markdown.startswith("```"):
+ raise NoPostDefinition
+
+ split_md = raw_markdown.split("```")[1:]
+ raw_meta = split_md[0]
+
+ menu_ptn = re.compile(r"Menu =(.*?)\n", re.IGNORECASE)
+ title_ptn = re.compile(r"Title =(.*?)\n", re.IGNORECASE)
+
+ try:
+ menu = menu_ptn.findall(raw_meta)[0].strip().split("/")
+ except (ValueError, IndexError, TypeError) as _:
+ menu = None
+
+ try:
+ title = title_ptn.findall(raw_meta)[0].strip()
+ except (ValueError, IndexError, TypeError) as _:
+ title = "[Unable to find Title]"
+
+ try:
+ post = "```".join(split_md[1:])
+ except (IndexError, TypeError, ValueError) as _:
+ post = "[Unable to find Post]"
+
+ return menu, title, post
+
+
+def compiler(docs_dir: Path, markdown_dir: Path):
+ docs_dir.mkdir(exist_ok=True)
+ markdown_dir.mkdir(exist_ok=True)
+
+ markdown_menu = markdown_dir / "__menu__.md"
+ markdown_index = markdown_dir / "__index__.md"
+
+ markdown_menu_dict = dict()
+
+ with open(markdown_menu, mode="r") as menu_file:
+ for line in menu_file.readlines():
+ if line.startswith("-"):
+ line_strip = line.strip()
+ markdown_menu_dict[line_strip.replace("- ", "").strip()] = {
+ "page": "",
+ "pages": [],
+ }
+ continue
+
+ if line.startswith(" ") or line.startswith("\t"):
+ line_strip = line.strip()
+ if line_strip.startswith("-"):
+ markdown_menu_dict[list(markdown_menu_dict.keys())[-1]][
+ "pages"
+ ].append({line_strip.replace("- ", "").strip(): ""})
+
+ main_index_html = docs_dir.parent / "index.html"
+ index_html = docs_dir / "index.html"
+
+ docs_dir_files = get_relative_files_in_the_docs_folder(docs_dir)
+ markdown_dir_files = markdown_dir.glob("*.md")
+ html_engine = mistune.create_markdown(renderer=HighlightRenderer())
+
+ html_pages = dict()
+ dt_date = pytz_dt_now()
+
+ main_index_html.unlink(missing_ok=True)
+ main_index_html.write_text(
+ render_template("main_index.html", latest_version=docs_dir.name)
+ )
+
+ for file in docs_dir_files:
+ (docs_dir / f"{file}.html").unlink()
+
+ for file in markdown_dir_files:
+ if "__" in file.stem:
+ continue
+
+ raw_markdown = file.read_text()
+ menu, title, post = _raw_markdown_processor(raw_markdown)
+ html_filename = f'{file.stem.lower().replace(" ", "_")}.html'
+
+ html_pages[html_filename] = {
+ "menu": menu,
+ "title": title,
+ "content": html_engine(post),
+ }
+
+ if menu is not None:
+ if len(menu) == 1:
+ markdown_menu_dict[menu[0]]["page"] = html_filename
+ else:
+ for keys in markdown_menu_dict[menu[0]]["pages"]:
+ if menu[1] in keys.keys():
+ keys[menu[1]] = html_filename
+
+ # write html files
+ for page, meta in html_pages.items():
+ with open(docs_dir / page, mode="w") as html_file:
+ html_file.write(
+ render_template(
+ "__main__.html",
+ menu=markdown_menu_dict,
+ title=meta["title"],
+ date=post_date(dt_date),
+ content=meta["content"],
+ )
+ )
+
+ # write main index.html
+ index_html.write_text(
+ render_template(
+ "index.html",
+ menu=markdown_menu_dict,
+ date=post_date(dt_date),
+ index=html_engine(markdown_index.read_text()),
+ )
+ )
diff --git a/docs/_ssg/exceptions.py b/docs/_ssg/exceptions.py
new file mode 100644
index 0000000..f34ca08
--- /dev/null
+++ b/docs/_ssg/exceptions.py
@@ -0,0 +1,38 @@
+class NoPostDefinition(Exception):
+ builtin_msg = f"""\n
+No post definition found!
+
+{"_" * 10}TOP_OF_FILE{"_" * 10}
+```
+Publish = Bool
+Date = 0000-00-00 00:00:00 +0100 or set-on-compile
+Title = String
+Description = String
+```
+
+Must be at the top of the file, and must be followed by a blank line.
+
+"""
+
+ def __str__(self):
+ return self.builtin_msg
+
+
+class ErrorInPostDefinition(Exception):
+ builtin_msg = f"""\n
+There is an error in the post description!
+
+{"_" * 10}TOP_OF_FILE{"_" * 10}
+```
+Publish = Bool
+Date = 0000-00-00 00:00:00 +0100 or set-on-compile
+Title = String
+Description = String
+```
+
+Must be at the top of the file, and must be followed by a blank line.
+
+"""
+
+ def __str__(self):
+ return self.builtin_msg
diff --git a/docs/_ssg/helpers.py b/docs/_ssg/helpers.py
new file mode 100644
index 0000000..c6dbe98
--- /dev/null
+++ b/docs/_ssg/helpers.py
@@ -0,0 +1,53 @@
+import re
+from datetime import datetime
+from pathlib import Path
+
+from pytz import timezone
+
+local_tz = timezone("Europe/London")
+
+
+def pytz_dt_now() -> datetime:
+ return datetime.now(local_tz)
+
+
+def pytz_dt_epoch() -> float:
+ return pytz_dt_now().timestamp()
+
+
+def pytz_dt_now_str(mask: str = "%Y-%m-%d %H:%M:%S %z") -> str:
+ return datetime.now(local_tz).strftime(mask)
+
+
+def pytz_dt_to_str(pytz_dt: datetime, mask: str = "%Y-%m-%d %H:%M:%S %z") -> str:
+ return pytz_dt.strftime(mask)
+
+
+def pytz_dt_str_to_dt(pytz_dt_str: str) -> datetime:
+ """
+ :param pytz_dt_str: "2020-01-01 00:00:00 +0000"
+ """
+ return datetime.strptime(pytz_dt_str, "%Y-%m-%d %H:%M:%S %z")
+
+
+def post_date(pytz_dt: datetime) -> str:
+ return pytz_dt.strftime("%a, %d %b %Y")
+
+
+def switch_date(content, new_date):
+ pattern = re.compile(r'date="(.*?)"', re.IGNORECASE)
+ return pattern.sub(f'date="{new_date}"', content)
+
+
+def get_relative_files_in_the_docs_folder(docs_dir: Path) -> list:
+ _ = []
+ for f in docs_dir.glob("*.html"):
+ if f.stem == "index":
+ continue
+ _.append(f.stem)
+
+ return _
+
+
+def excessive_br_cleanup(base_xml: str) -> str:
+ return base_xml.replace("
", "").replace(" ", "")
diff --git a/docs/_ssg/render_engines.py b/docs/_ssg/render_engines.py
new file mode 100644
index 0000000..cff2fe9
--- /dev/null
+++ b/docs/_ssg/render_engines.py
@@ -0,0 +1,18 @@
+import mistune
+from pygments import highlight
+from pygments.formatters import HtmlFormatter
+from pygments.lexers import get_lexer_by_name
+from pygments.util import ClassNotFound
+
+
+class HighlightRenderer(mistune.HTMLRenderer):
+ def block_code(self, code, info=None):
+ if info:
+ if info == "jinja2":
+ info = "jinja"
+ try:
+ lexer = get_lexer_by_name(info, stripall=True)
+ except ClassNotFound:
+ lexer = get_lexer_by_name("text", stripall=True)
+ return highlight(code, lexer, HtmlFormatter())
+ return "" + mistune.escape(code) + " "
diff --git a/docs/_templates/__main__.html b/docs/_templates/__main__.html
new file mode 100644
index 0000000..d9a26de
--- /dev/null
+++ b/docs/_templates/__main__.html
@@ -0,0 +1,26 @@
+
+
+
+
+ {{ title|title }} | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+{% include "__menu__.html" %}
+
+
+ {{ title }}
+ {{ content|safe }}
+
+
+
+
\ No newline at end of file
diff --git a/docs/_templates/__menu__.html b/docs/_templates/__menu__.html
new file mode 100644
index 0000000..98dd3cc
--- /dev/null
+++ b/docs/_templates/__menu__.html
@@ -0,0 +1,31 @@
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ {{ date }}
+ Menu
+
+
+ {% for title, values in menu.items() %}
+ {% if values["page"] %}
+ {{ title }}
+ {% else %}
+ {{ title }}
+ {% endif %}
+ {% if values["pages"] %}
+
+ {% endif %}
+ {% endfor %}
+
+
+ Hosted on GitHub Pages.
+
diff --git a/docs/_templates/index.html b/docs/_templates/index.html
new file mode 100644
index 0000000..c4ff980
--- /dev/null
+++ b/docs/_templates/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+ Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+{% include "__menu__.html" %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/_templates/main_index.html b/docs/_templates/main_index.html
new file mode 100644
index 0000000..c2d3abf
--- /dev/null
+++ b/docs/_templates/main_index.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Redirecting to latest...
+
+
+
+
+Click here if redirection is not working.
+
+
+
\ No newline at end of file
diff --git a/docs/config.py b/docs/config.py
new file mode 100644
index 0000000..8003f18
--- /dev/null
+++ b/docs/config.py
@@ -0,0 +1,7 @@
+from dataclasses import dataclass
+
+
+@dataclass
+class Config:
+ latest = "v1"
+ versions = ["v1"]
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..9411a8b
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+ Redirecting to latest...
+
+
+
+
+Click here if redirection is not working.
+
+
+
\ No newline at end of file
diff --git a/docs/v1/cli_commands-quart-imp_blueprint.html b/docs/v1/cli_commands-quart-imp_blueprint.html
new file mode 100644
index 0000000..10022bf
--- /dev/null
+++ b/docs/v1/cli_commands-quart-imp_blueprint.html
@@ -0,0 +1,304 @@
+
+
+
+
+ Generate A Quart-Imp Blueprint | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Generate a Quart-Imp Blueprint
+ Quart-Imp has its own type of blueprint. It comes with some methods to auto import routes, and nested blueprints etc...
+see ImpBlueprint / Introduction for more information.
+You have the option to generate a regular template rendering blueprint, or a API blueprint that returns a JSON response.
+ quart-imp blueprint --help
+
+or
+ quart-imp api-blueprint --help
+
+To generate a Quart-Imp blueprint, run the following command:
+
+or
+ quart-imp api-blueprint
+
+After running this command, you will be prompted to enter the location of where you want to create your blueprint:
+ ~ $ quart-imp blueprint
+(Creation is relative to the current working directory)
+Folder to create blueprint in [Current Working Directory]:
+
+As detailed in the prompt, the creation of the blueprint is relative to the current working directory. So to create a
+blueprint in the folder app/blueprints, you would enter app/blueprints in the prompt.
+ ~ $ quart-imp blueprint
+(Creation is relative to the current working directory)
+Folder to create blueprint in [Current Working Directory]: app/blueprints
+
+You will then be prompted to enter a name for your blueprint:
+ ~ $ quart-imp blueprint
+...
+Name of the blueprint to create [my_new_blueprint]:
+
+The default name is 'my_new_blueprint', we will change this to 'admin'
+ ~ $ quart-imp blueprint
+...
+Name of the blueprint to create [my_new_blueprint]: admin
+
+After creating your blueprint, the folder structure will look like this:
+ app/
+├── blueprints
+│ └── admin
+│ ├── routes
+│ │ └── index.py
+│ │
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ │
+│ ├── templates
+│ │ └── www
+│ │ ├── extends
+│ │ │ └── main.html
+│ │ ├── includes
+│ │ │ ├── footer.html
+│ │ │ └── header.html
+│ │ └── index.html
+│ │
+│ └── __init__.py
+│
+...
+
+This is a self-contained blueprint, so it has its own static, templates and routes folders.
+You can now navigate '/admin'
+You can streamline this process by specifying the name of the blueprint, the folder to
+create it in and the configuration to use, like so:
+ quart-imp blueprint -n admin -f app/blueprints
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/cli_commands-quart-imp_init.html b/docs/v1/cli_commands-quart-imp_init.html
new file mode 100644
index 0000000..70846f6
--- /dev/null
+++ b/docs/v1/cli_commands-quart-imp_init.html
@@ -0,0 +1,398 @@
+
+
+
+
+ Initialising A Quart-Imp Project | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Initialising a Quart-Imp Project
+ Quart-Imp has a cli command that deploys a new ready-to-go project.
+This project is structured in a way to give you the best idea of
+how to use Quart-Imp.
+
+Create a new project
+Make sure you are in the virtual environment, and at the root of your
+project folder, then run the following command:
+
+After running this command, you will be prompted to choose what type of
+app you want to deploy:
+ ~ $ quart-imp init
+What type of app would you like to create? (minimal, slim, full) [minimal]:
+
+See below for the differences between the app types.
+After this, you will be prompted to enter a name for your app:
+ ~ $ quart-imp init
+...
+What would you like to call your app? [app]:
+
+'app' is the default name, so if you just press enter, your app will be
+called 'app'. You will then see this output:
+ ~ FILES CREATED WILL LOOP OUT HERE ~
+
+===================
+Quart app deployed!
+===================
+
+Your app has the default name of 'app'
+Quart will automatically look for this!
+Run: quart run --debug
+
+If you called your app something other than 'app', like 'new' for example, you will see:
+ ~ FILES CREATED WILL LOOP OUT HERE ~
+
+===================
+Quart app deployed!
+===================
+
+Your app has the name of 'new'
+Run: quart --app new run --debug
+
+As you can see from the output, it gives you instructions on how to start your app,
+depending on the name you gave it.
+You should see a new folder that has been given the name you specified in
+the quart-imp init command.
+Additional options
+You can also specify a name for your app in the command itself, like so:
+ quart-imp init -n my_app
+
+This will create a new app called 'my_app'.
+The default will be a minimal app, this has no blueprints.
+You can also deploy a slim app, that will have one blueprint, like so:
+ quart-imp init -n my_app --slim
+
+You can also deploy a full app that is setup for multiple blueprints, like so:
+ quart-imp init -n my_app --full
+
+init Folder structures
+Minimal app (default)
+quart-imp init --minimal:
+ app/
+├── resources
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── favicon.ico
+│ ├── templates
+│ │ └── index.html
+│ └── routes.py
+│
+└── __init__.py
+
+Slim app
+quart-imp init --slim:
+ app/
+├── extensions
+│ └── __init__.py
+│
+├── resources
+│ ├── cli
+│ │ └── cli.py
+│ ├── error_handlers
+│ │ └── error_handlers.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── favicon.ico
+│ └── templates
+│ └── error.html
+│
+├── www
+│ ├── __init__.py
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ └── templates
+│ └── www
+│ ├── extends
+│ │ └── main.html
+│ ├── includes
+│ │ ├── footer.html
+│ │ └── header.html
+│ └── index.html
+│
+└── __init__.py
+
+Full app
+quart-imp init --full:
+ app/
+├── blueprints
+│ └── www
+│ ├── __init__.py
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ ├── css
+│ │ │ └── water.css
+│ │ ├── img
+│ │ │ └── quart-imp-logo.png
+│ │ └── js
+│ │ └── main.js
+│ └── templates
+│ └── www
+│ ├── extends
+│ │ └── main.html
+│ ├── includes
+│ │ ├── footer.html
+│ │ └── header.html
+│ └── index.html
+│
+├── extensions
+│ └── __init__.py
+│
+├── resources
+│ ├── cli
+│ │ └── cli.py
+│ ├── context_processors
+│ │ └── context_processors.py
+│ ├── error_handlers
+│ │ └── error_handlers.py
+│ ├── filters
+│ │ └── filters.py
+│ ├── routes
+│ │ └── routes.py
+│ ├── static
+│ │ └── favicon.ico
+│ └── templates
+│ └── error.html
+│
+└── __init__.py
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-import_app_resources.html b/docs/v1/imp-import_app_resources.html
new file mode 100644
index 0000000..02893d6
--- /dev/null
+++ b/docs/v1/imp-import_app_resources.html
@@ -0,0 +1,304 @@
+
+
+
+
+ Imp.import_app_resources | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Imp.import_app_resources
+ import_app_resources (
+ folder : str = "resources" ,
+ factories : Optional [ List ] = None ,
+ static_folder : str = "static" ,
+ templates_folder : str = "templates" ,
+ files_to_import : Optional [ List ] = None ,
+ folders_to_import : Optional [ List ] = None ,
+ ) -> None
+
+
+Import standard app resources from the specified folder.
+This will import any resources that have been set to the Quart app.
+Routes, context processors, cli, etc.
+Can only be called once.
+If no static and or template folder is found, the static and or template folder will be set to None in the Quart app
+config.
+Small example of usage:
+imp . import_app_resources ( folder = "resources" )
+# or
+imp . import_app_resources ()
+# as the default folder is "resources"
+
+Folder Structure: resources
+ app
+├── resources
+│ ├── routes.py
+│ ├── app_fac.py
+│ ├── static
+│ │ └── css
+│ │ └── style.css
+│ └── templates
+│ └── index.html
+└── ...
+...
+
+File: routes.py
+from quart import current_app as app
+from quart import render_template
+
+
+@app . route ( "/" )
+async def index ():
+ return await render_template ( "index.html" )
+
+How factories work
+Factories are functions that are called when importing the app resources. Here's an example:
+imp . import_app_resources (
+ folder = "resources" ,
+ factories = [ "development_cli" ]
+)
+
+["development_cli"] => development_cli(app) function will be called, and the current app will be passed in.
+File: app_fac.py
+def development_cli ( app ):
+ @app . cli . command ( "dev" )
+ def dev ():
+ print ( "dev cli command" )
+
+Scoping imports
+By default, all files and folders will be imported.
+To disable this, set files_to_import and or
+folders_to_import to [None].
+imp . import_app_resources ( scope_import = [ None ], folders_to_import = [ None ])
+
+To scope the imports, set the files_to_import and or folders_to_import to a list of files and or folders.
+files_to_import=["cli.py", "routes.py"] => will only import the files resources/cli.py
+and resources/routes.py
+folders_to_import=["template_filters", "context_processors"] => will import all files in the folders
+resources/template_filters/*.py and resources/context_processors/*.py
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-import_blueprint.html b/docs/v1/imp-import_blueprint.html
new file mode 100644
index 0000000..55c1bf6
--- /dev/null
+++ b/docs/v1/imp-import_blueprint.html
@@ -0,0 +1,324 @@
+
+
+
+
+ Imp.import_blueprint | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Imp.import_blueprint
+ import_blueprint ( self , blueprint : str ) -> None
+
+
+Import a specified Quart-Imp or standard Quart Blueprint relative to the Quart app root.
+ app
+├── my_blueprint
+│ ├── ...
+│ └── __init__.py
+├── ...
+└── __init__.py
+
+File: app/__init__.py
+from quart import Quart
+
+from quart_imp import Imp
+
+imp = Imp ()
+
+
+def create_app ():
+ app = Quart ( __name__ )
+ imp . init_app ( app )
+
+ imp . import_blueprint ( "my_blueprint" )
+
+ return app
+
+Quart-Imp Blueprints have the ability to auto import resources, and initialize session variables.
+For more information on how Quart-Imp Blueprints work, see the ImpBlueprint / Introduction
+Example of 'my_blueprint' as a Quart-Imp Blueprint:
+ app
+├── my_blueprint
+│ ├── routes
+│ │ └── index.py
+│ ├── static
+│ │ └── css
+│ │ └── style.css
+│ ├── templates
+│ │ └── my_blueprint
+│ │ └── index.html
+│ ├── __init__.py
+│ └── config.toml
+└── ...
+
+File: __init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint (
+ __name__ ,
+ ImpBlueprintConfig (
+ enabled = True ,
+ url_prefix = "/my-blueprint" ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+ static_url_path = "/static/my_blueprint" ,
+ init_session = { "my_blueprint" : "session_value" },
+ ),
+)
+
+bp . import_resources ( "routes" )
+
+File: routes / index.py
+from .. import bp
+
+
+@bp . route ( "/" )
+async def index ():
+ return "regular_blueprint"
+
+Example of 'my_blueprint' as a standard Quart Blueprint:
+ app
+├── my_blueprint
+│ ├── ...
+│ └── __init__.py
+└── ...
+
+File: __init__.py
+from quart import Blueprint
+
+bp = Blueprint ( "my_blueprint" , __name__ , url_prefix = "/my-blueprint" )
+
+
+@bp . route ( "/" )
+async def index ():
+ return "regular_blueprint"
+
+Both of the above examples will work with imp.import_blueprint("my_blueprint"), they will be registered
+with the Quart app, and will be accessible via url_for("my_blueprint.index").
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-import_blueprints.html b/docs/v1/imp-import_blueprints.html
new file mode 100644
index 0000000..13a147e
--- /dev/null
+++ b/docs/v1/imp-import_blueprints.html
@@ -0,0 +1,271 @@
+
+
+
+
+ Imp.import_blueprints | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Imp.import_blueprints
+ import_blueprints ( self , folder : str ) -> None
+
+
+Import all Quart-Imp or standard Quart Blueprints from a specified folder relative to the Quart app root.
+ app/
+├── blueprints/
+│ ├── admin/
+│ │ ├── ...
+│ │ └── __init__.py
+│ ├── www/
+│ │ ├── ...
+│ │ └── __init__.py
+│ └── api/
+│ ├── ...
+│ └── __init__.py
+├── ...
+└── __init__.py
+
+File: app/__init__.py
+from quart import Quart
+
+from quart_imp import Imp
+
+imp = Imp ()
+
+
+def create_app ():
+ app = Quart ( __name__ )
+ imp . init_app ( app )
+
+ imp . import_blueprints ( "blueprints" )
+
+ return app
+
+This will import all Blueprints from the blueprints folder using the Imp.import_blueprint method.
+See Imp / import_blueprint for more information.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-init_app-init.html b/docs/v1/imp-init_app-init.html
new file mode 100644
index 0000000..b0e183c
--- /dev/null
+++ b/docs/v1/imp-init_app-init.html
@@ -0,0 +1,248 @@
+
+
+
+
+ Imp.init_app, __init__ | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Imp.init_app, __init__
+ def init_app (
+ app : Quart ,
+ config : ImpConfig
+) -> None :
+# -or-
+Imp (
+ app : Quart ,
+ config : ImpConfig
+)
+
+
+Initializes the quart app to work with quart-imp.
+See quart_imp_config-impconfig.md for more information on the ImpConfig class.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-init_session.html b/docs/v1/imp-init_session.html
new file mode 100644
index 0000000..3b7c8ac
--- /dev/null
+++ b/docs/v1/imp-init_session.html
@@ -0,0 +1,263 @@
+
+
+
+
+ Imp.init_session | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Imp.init_session
+
+
+Initialize the session variables found in the config. Commonly used in app.before_request.
+@app . before_request
+async def before_request ():
+ imp . _init_session ()
+
+File: config.toml
+...
+[SESSION]
+logged_in = false
+...
+
+logged_in is now available in the session.
+@app . route ( '/get-session-value' )
+async def login ():
+ print ( session [ 'logged_in' ])
+ return "Check Terminal"
+
+Output: False
+Can also be used to reset the values in the session. Here's an example:
+@app . route ( '/logout' )
+async def logout ():
+ session . clear ()
+ imp . _init_session ()
+ return redirect ( url_for ( 'index' ))
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/imp-introduction.html b/docs/v1/imp-introduction.html
new file mode 100644
index 0000000..3222099
--- /dev/null
+++ b/docs/v1/imp-introduction.html
@@ -0,0 +1,297 @@
+
+
+
+
+ Quart-Imp Introduction | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Quart-Imp Introduction
+ Quart-Imp is a Quart extension that provides auto import methods for various Quart resources. It will import
+blueprints, and other resources. It uses the importlib module to achieve this.
+Quart-Imp favors the application factory pattern as a project structure, and is opinionated towards using
+Blueprints. However, you can use Quart-Imp without using Blueprints.
+Here's an example of a standard Quart-Imp project structure:
+ app/
+├── blueprints/
+│ ├── admin/...
+│ ├── api/...
+│ └── www/...
+├── resources/
+│ ├── filters/...
+│ ├── context_processors/...
+│ ├── static/...
+│ └── templates/...
+└── __init__.py
+
+Here's an example of the app/__init__.py file:
+from quart import Quart
+from quart_sqlalchemy import SQLAlchemy
+from quart_imp import Imp
+from quart_imp.config import QuartConfig , ImpConfig
+
+db = SQLAlchemy ()
+imp = Imp ()
+
+
+def create_app ():
+ app = Quart ( __name__ )
+ QuartConfig (
+ secret_key = "super_secret_key" ,
+ app_instance = app ,
+ )
+
+ imp . init_app ( app , config = ImpConfig (
+ init_session = { "logged_in" : False },
+ ))
+ imp . import_app_resources ( "resources" )
+ imp . import_blueprints ( "blueprints" )
+
+ db . init_app ( app )
+
+ return app
+
+The Quart configuration can be loaded from any standard Quart configuration method, or from the QuartConfig class
+shown above.
+This class contains the standard Quart configuration options found in the Quart documentation.
+The ImpConfig class is used to configure the Imp instance.
+The init_session option of the ImpConfig class is used to set the initial session variables for the Quart app.
+This happens before the request is processed.
+ImpConfig also has the ability to set SQLALCHEMY_DATABASE_URI and SQLALCHEMY_BINDS
+For more information about the configuration setting see
+quart_imp_config-impconfig.md .
+import_app_resources will walk one level deep into the resources folder, and import
+all .py files as modules.
+It will also check for the existence of a static and templates folder, and register them with the Quart app.
+There is a couple of options for import_app_resources to control what
+is imported, see: Imp / import_app_resources
+import_blueprints expects a folder that contains many Blueprint as Python packages.
+It will check each blueprint folder's __init__.py file for an instance of a Quart Blueprint or a
+Quart-Imp Blueprint. That instant will then be registered with the Quart app.
+See more about how importing blueprints work here: ImpBlueprint / Introduction
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-import_nested_blueprint.html b/docs/v1/impblueprint-import_nested_blueprint.html
new file mode 100644
index 0000000..1773895
--- /dev/null
+++ b/docs/v1/impblueprint-import_nested_blueprint.html
@@ -0,0 +1,291 @@
+
+
+
+
+ Impblueprint.import_nested_blueprint | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpBlueprint.import_nested_blueprint
+ import_nested_blueprint ( self , blueprint : str ) -> None
+
+
+Import a specified Quart-Imp or standard Quart Blueprint relative to the Blueprint root.
+Works the same as Imp / import_blueprint but relative to the Blueprint root.
+Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
+url_for('my_blueprint.my_nested_blueprint.index')
+ my_blueprint/
+├── routes/...
+├── static/...
+├── templates/...
+│
+├── my_nested_blueprint/
+│ ├── routes/
+│ │ └── index.py
+│ ├── static/...
+│ ├── templates/...
+│ ├── __init__.py
+│
+├── __init__.py
+
+File: my_blueprint/__init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint ( __name__ , ImpBlueprintConfig (
+ enabled = True ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+))
+
+bp . import_resources ( "routes" )
+bp . import_nested_blueprint ( "my_nested_blueprint" )
+
+File: my_blueprint/my_nested_blueprint/__init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint ( __name__ , ImpBlueprintConfig (
+ enabled = True ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+))
+
+bp . import_resources ( "routes" )
+
+File: my_blueprint/my_nested_blueprint/routes/index.py
+from quart import render_template
+
+from .. import bp
+
+
+@bp . route ( "/" )
+async def index ():
+ return await render_template ( bp . tmpl ( "index.html" ))
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-import_nested_blueprints.html b/docs/v1/impblueprint-import_nested_blueprints.html
new file mode 100644
index 0000000..c10210a
--- /dev/null
+++ b/docs/v1/impblueprint-import_nested_blueprints.html
@@ -0,0 +1,277 @@
+
+
+
+
+ Impblueprint.import_nested_blueprints | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpBlueprint.import_nested_blueprints
+ import_nested_blueprints ( self , folder : str ) -> None
+
+
+Will import all the Blueprints from the given folder relative to the Blueprint's root directory.
+Uses Blueprint / import_nested_blueprint to import blueprints from
+the specified folder.
+Blueprints that are imported this way will be scoped to the parent Blueprint that imported them.
+url_for('my_blueprint.nested_bp_one.index')
+url_for('my_blueprint.nested_bp_two.index')
+url_for('my_blueprint.nested_bp_three.index')
+ my_blueprint/
+├── routes/...
+├── static/...
+├── templates/...
+│
+├── nested_blueprints/
+│ │
+│ ├── nested_bp_one/
+│ │ ├── ...
+│ │ ├── __init__.py
+│ ├── nested_bp_two/
+│ │ ├── ...
+│ │ ├── __init__.py
+│ └── nested_bp_three/
+│ ├── ...
+│ ├── __init__.py
+│
+├── __init__.py
+
+File: my_blueprint/__init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint ( __name__ , ImpBlueprintConfig (
+ enabled = True ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+))
+
+bp . import_resources ( "routes" )
+bp . import_nested_blueprints ( "nested_blueprints" )
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-import_resources.html b/docs/v1/impblueprint-import_resources.html
new file mode 100644
index 0000000..c7993c2
--- /dev/null
+++ b/docs/v1/impblueprint-import_resources.html
@@ -0,0 +1,276 @@
+
+
+
+
+ Impblueprint.import_resources | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpBlueprint.import_resources
+ import_resources ( folder : str = "routes" ) -> None
+
+
+Will import all the resources (cli, routes, filters, context_processors...) from the given folder relative to the
+Blueprint's root directory.
+ my_blueprint
+├── user_routes
+│ ├── user_dashboard.py
+│ └── user_settings.py
+├── car_routes
+│ ├── car_dashboard.py
+│ └── car_settings.py
+├── static/...
+├── templates/
+│ └── my_blueprint/
+│ ├── user_dashboard.html
+│ └── ...
+├── __init__.py
+
+File: my_blueprint/__init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint ( __name__ , ImpBlueprintConfig (
+ enabled = True ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+))
+
+bp . import_resources ( "user_routes" )
+bp . import_resources ( "car_routes" )
+
+File: my_blueprint/user_routes/user_dashboard.py
+from quart import render_template
+
+from .. import bp
+
+@bp . route ( "/user-dashboard" )
+async def user_dashboard ():
+ return await render_template ( bp . tmpl ( "user_dashboard.html" ))
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-init.html b/docs/v1/impblueprint-init.html
new file mode 100644
index 0000000..dc4f679
--- /dev/null
+++ b/docs/v1/impblueprint-init.html
@@ -0,0 +1,242 @@
+
+
+
+
+ Quart-Imp Blueprint __init__ | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Quart-Imp Blueprint __init__
+ ImpBlueprint ( dunder_name : str , config : ImpBlueprintConfig ) -> None
+
+
+Initializes the Quart-Imp Blueprint.
+dunder_name should always be set to __name__
+config is an instance of ImpBlueprintConfig that will be used to load the Blueprint's configuration.
+See quart_imp.config / ImpBlueprintConfig for more information.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-introduction.html b/docs/v1/impblueprint-introduction.html
new file mode 100644
index 0000000..321789b
--- /dev/null
+++ b/docs/v1/impblueprint-introduction.html
@@ -0,0 +1,285 @@
+
+
+
+
+ Quart-Imp Blueprint Introduction | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Quart-Imp Blueprint Introduction
+ The Quart-Imp Blueprint inherits from the Quart Blueprint class, then adds some additional methods to allow for auto
+importing of resources and other nested blueprints.
+The Quart-Imp Blueprint requires you to provide the ImpBlueprintConfig class as the second argument to the Blueprint.
+Here's an example of a Quart-Imp Blueprint structure:
+ www/
+├── nested_blueprints/
+│ ├── blueprint_one/
+│ │ ├── ...
+│ │ └── __init__.py
+│ └── blueprint_two/
+│ ├── ...
+│ └── __init__.py
+├── standalone_nested_blueprint/
+│ ├── ...
+│ └── __init__.py
+├── routes/
+│ └── index.py
+├── static/
+│ └── ...
+├── templates/
+│ └── www/
+│ └── index.html
+└── __init__.py
+
+File: __init__.py
+from quart_imp import ImpBlueprint
+from quart_imp.config import ImpBlueprintConfig
+
+bp = ImpBlueprint ( __name__ , ImpBlueprintConfig (
+ enabled = True ,
+ url_prefix = "/www" ,
+ static_folder = "static" ,
+ template_folder = "templates" ,
+ init_session = { "logged_in" : False },
+))
+
+bp . import_resources ( "routes" )
+bp . import_nested_blueprints ( "nested_blueprints" )
+bp . import_nested_blueprint ( "standalone_nested_blueprint" )
+
+The ImpBlueprintConfig class is used to configure the Blueprint. It provides a little more flexibility than the
+standard Quart Blueprint configuration, like the ability to enable or disable the Blueprint.
+ImpBlueprintConfig's init_session works the same as ImpConfig's init_session, this will add the session data to
+the Quart app's session object on initialization of the Quart app.
+To see more about configuration see: quart_imp.config / ImpBlueprintConfig
+import_resources method will walk one level deep into the routes folder, and import all .py files as modules.
+For more information see: ImpBlueprint / import_resources
+import_nested_blueprints will do the same as imp.import_blueprints, but will register the blueprints found as
+nested to the current blueprint. For example www.blueprint_one.index
+import_nested_blueprint behaves the same as import_nested_blueprints, but will only import a single blueprint.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/impblueprint-tmpl.html b/docs/v1/impblueprint-tmpl.html
new file mode 100644
index 0000000..ce64853
--- /dev/null
+++ b/docs/v1/impblueprint-tmpl.html
@@ -0,0 +1,264 @@
+
+
+
+
+ Impblueprint.tmpl | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpBlueprint.tmpl
+ tmpl ( template : str ) -> str
+
+
+Scopes the template lookup to the name of the blueprint (this takes from the __name__ attribute of the Blueprint).
+Due to the way Quart templating works, and to avoid template name collisions.
+It is standard practice to place the name of the Blueprint in the template path,
+then to place any templates under that folder.
+ my_blueprint/
+├── routes/
+│ └── index.py
+├── static/...
+│
+├── templates/
+│ └── my_blueprint/
+│ └── index.html
+│
+├── __init__.py
+
+File: my_blueprint/routes/index.py
+from quart import render_template
+
+from .. import bp
+
+
+@bp . route ( "/" )
+async def index ():
+ return await render_template ( bp . tmpl ( "index.html" ))
+
+bp.tmpl("index.html") will output "my_blueprint/index.html".
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/index.html b/docs/v1/index.html
new file mode 100644
index 0000000..da963a1
--- /dev/null
+++ b/docs/v1/index.html
@@ -0,0 +1,317 @@
+
+
+
+
+ Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ Welcome to the Quart-Imp Documentation
+What is Quart-Imp?
+Quart-Imp's main purpose is to help simplify the importing of blueprints, and resources. It has a few extra
+features built in to help with securing pages and password authentication.
+Install Quart-Imp
+
+Getting Started
+To get started right away, you can use the CLI commands to create a new Quart-Imp project.
+
+Minimal Quart-Imp Setup
+Run the following command to create a minimal Quart-Imp project.
+ quart-imp init -n app --minimal
+
+See CLI Commands / quart-imp init for more information.
+The minimal structure
+Folder Structure
+ app/
+├── resources/
+│ ├── static/...
+│ ├── templates/
+│ │ └── index.html
+│ └── index.py
+└── __init__.py
+
+File: app/__init__.py
+from quart import Quart
+
+from quart_imp import Imp
+from quart_imp.config import QuartConfig , ImpConfig
+
+imp = Imp ()
+
+
+def create_app ():
+ app = Quart ( __name__ , static_url_path = "/" )
+ QuartConfig (
+ secret_key = "secret_key" ,
+ app_instance = app
+ )
+
+ imp . init_app ( app , ImpConfig ())
+
+ imp . import_app_resources ()
+ # Takes argument 'folder' default folder is 'resources'
+
+ return app
+
+File: app/resources/routes.py
+from quart import current_app as app
+from quart import render_template
+
+
+@app . route ( "/" )
+async def index ():
+ return await render_template ( "index.html" )
+
+File: app/resources/templates/index.html
+<!DOCTYPE html>
+< html lang = "en" >
+< head >
+ < meta charset = "UTF-8" >
+ < title > Quart-Imp</ title >
+</ head >
+< body >
+< h1 > Quart-Imp</ h1 >
+</ body >
+</ html >
+
+
+Setting up a virtual environment is recommended.
+Linux / Darwin
+
+source venv/bin/activate
+
+Windows
+
+ .\venv\Scripts\activate
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-authenticate_password.html b/docs/v1/quart_imp_auth-authenticate_password.html
new file mode 100644
index 0000000..d602f96
--- /dev/null
+++ b/docs/v1/quart_imp_auth-authenticate_password.html
@@ -0,0 +1,272 @@
+
+
+
+
+ Authenticate_password - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ authenticate_password - quart_imp.auth
+ from quart_imp.auth import authenticate_password
+
+authenticate_password (
+ input_password : str ,
+ database_password : str ,
+ database_salt : str ,
+ encryption_level : int = 512 ,
+ pepper_length : int = 1 ,
+ pepper_position : t . Literal [ "start" , "end" ] = "end"
+) -> bool
+
+
+For use in password hashing.
+To be used alongside the quart_imp.auth / encrypt_password function.
+Takes the plain input password, the stored hashed password along with the stored salt
+and will try every possible combination of pepper values to find a match.
+Note:
+
+You must know the pepper length used to hash the password.
+You must know the position of the pepper used to hash the password.
+You must know the encryption level used to hash the password.
+
+Authentication Scenario:
+Plain password: "password"
+Generated salt: "^%$*" (randomly generated)
+Generated pepper (length 1): "A" (randomly generated)
+Pepper position: "end"
+input_password = "password"
+database_password = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..." # pulled from database
+database_salt = "^%$*" # pulled from database
+
+authenticate_password (
+ input_password ,
+ database_password ,
+ database_salt
+) # >>> True
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-encrypt_password.html b/docs/v1/quart_imp_auth-encrypt_password.html
new file mode 100644
index 0000000..f8e16fd
--- /dev/null
+++ b/docs/v1/quart_imp_auth-encrypt_password.html
@@ -0,0 +1,269 @@
+
+
+
+
+ Encrypt_password - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ encrypt_password - quart_imp.auth
+ from quart_imp.auth import encrypt_password
+
+encrypt_password (
+ password : str ,
+ salt : str ,
+ encryption_level : int = 512 ,
+ pepper_length : int = 1 ,
+ pepper_position : t . Literal [ "start" , "end" ] = "end"
+) -> str
+
+
+For use in password hashing.
+To be used alongside the quart_imp.auth / authenticate_password function.
+Takes the plain password, applies a pepper, salts it, then produces a digested sha512 or sha256 if specified.
+Can set the encryption level to 256 or 512, defaults to 512.
+Can set the pepper length, defaults to 1. Max is 3.
+Can set the pepper position, "start" or "end", defaults to "end".
+Note:
+
+You must inform the authenticate_password function of the pepper length used to hash the password.
+You must inform the authenticate_password function of the position of the pepper used to hash the password.
+You must inform the authenticate_password function of the encryption level used to hash the password.
+
+Encryption Scenario:
+Plain password: "password"
+Generated salt: "^%$*" (randomly generated)
+Generated pepper (length 1): "A" (randomly generated)
+Pepper position: "end"
+
+Pepper is added to the end of the plain password: "passwordA"
+Salt is added to the end of the peppered password: "passwordA^%$*"
+Password is hashed: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0..."
+Salt and hashed password are then stored in the database.
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_alphanumeric_validator.html b/docs/v1/quart_imp_auth-generate_alphanumeric_validator.html
new file mode 100644
index 0000000..f8ec0a9
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_alphanumeric_validator.html
@@ -0,0 +1,245 @@
+
+
+
+
+ Generate_alphanumeric_validator - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_alphanumeric_validator - quart_imp.auth
+ from quart_imp.auth import generate_alphanumeric_validator
+
+generate_alphanumeric_validator ( length : int = 8 ) -> str
+
+
+Generates a random alphanumeric string of the given length.
+(letters are capitalized)
+Example:
+generate_alphanumeric_validator ( 8 ) # >>> 'A1B2C3D4'
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_csrf_token.html b/docs/v1/quart_imp_auth-generate_csrf_token.html
new file mode 100644
index 0000000..37e7537
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_csrf_token.html
@@ -0,0 +1,246 @@
+
+
+
+
+ Generate_csrf_token - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_csrf_token - quart_imp.auth
+ from quart_imp.auth import generate_csrf_token
+
+generate_csrf_token () -> str
+
+
+Generates a SHA1 using the current date and time.
+For use in Cross-Site Request Forgery.
+Also used by the quart_imp.security / csrf_protect decorator.
+Example:
+generate_csrf_token () # >>> 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0'
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_email_validator.html b/docs/v1/quart_imp_auth-generate_email_validator.html
new file mode 100644
index 0000000..a81d077
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_email_validator.html
@@ -0,0 +1,248 @@
+
+
+
+
+ Generate_email_validator - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_email_validator - quart_imp.auth
+ from quart_imp.auth import generate_email_validator
+
+generate_email_validator () -> str
+
+
+Uses generate_alphanumeric_validator with a length of 8 to
+generate a random alphanumeric value for the specific use of
+validating accounts via email.
+See quart_imp.auth / generate_alphanumeric_validator
+for more information.
+Example:
+generate_email_validator () # >>> 'A1B2C3D4'
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_numeric_validator.html b/docs/v1/quart_imp_auth-generate_numeric_validator.html
new file mode 100644
index 0000000..b98221a
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_numeric_validator.html
@@ -0,0 +1,246 @@
+
+
+
+
+ Generate_numeric_validator - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_numeric_validator - quart_imp.auth
+ from quart_imp.auth import generate_numeric_validator
+
+generate_numeric_validator ( length : int ) -> int
+
+
+Generates random choice between 1 * (length) and 9 * (length).
+If the length is 4, it will generate a number between 1111 and 9999.
+For use in MFA email, or unique filename generation.
+Example:
+generate_numeric_validator ( 4 ) # >>> 1234
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_password.html b/docs/v1/quart_imp_auth-generate_password.html
new file mode 100644
index 0000000..f8a06a7
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_password.html
@@ -0,0 +1,246 @@
+
+
+
+
+ Generate_password - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_password - quart_imp.auth
+ from quart_imp.auth import generate_password
+
+generate_password ( style : str = "mixed" , length : int = 3 ) -> str
+
+
+Generates a password of (length) characters.
+The Default length is 3.
+Style options: "animals", "colors", "mixed" - defaults to "mixed"
+Example:
+generate_password ( style = "animals" , length = 3 ) # >>> 'Cat-Goat-Pig12'
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_private_key.html b/docs/v1/quart_imp_auth-generate_private_key.html
new file mode 100644
index 0000000..143e5fe
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_private_key.html
@@ -0,0 +1,260 @@
+
+
+
+
+ Generate_private_key - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_private_key - quart_imp.auth
+ from quart_imp.auth import generate_private_key
+
+generate_private_key ( hook : t . Optional [ str ]) -> str
+
+
+Generates a sha256 private key from a passed in hook value.
+If no hook is passed in, it will generate a hook using datetime.now() and a
+random number between 1 and 1000.
+@app . route ( '/register' , methods = [ 'GET' , 'POST' ])
+async def register ():
+ if request . method == "POST" :
+ ...
+ salt = generate_salt ()
+ password = request . form . get ( 'password' )
+ encrypted_password = encrypt_password ( password , salt )
+ ...
+ user = User (
+ username = username ,
+ email = email ,
+ password = encrypted_password ,
+ salt = salt ,
+ private_key = generate_private_key ( hook = username )
+ )
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-generate_salt.html b/docs/v1/quart_imp_auth-generate_salt.html
new file mode 100644
index 0000000..85f7acc
--- /dev/null
+++ b/docs/v1/quart_imp_auth-generate_salt.html
@@ -0,0 +1,263 @@
+
+
+
+
+ Generate_salt - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ generate_salt - quart_imp.auth
+ from quart_imp.auth import generate_salt
+
+generate_salt ( length : int = 4 ) -> str
+
+
+Generates a string of (length) characters of punctuation.
+The Default length is 4.
+For use in password hashing and storage of passwords in the database.
+Example:
+generate_salt () # >>> '*!$%'
+
+@app . route ( '/register' , methods = [ 'GET' , 'POST' ])
+async def register ():
+ if request . method == "POST" :
+ ...
+ salt = generate_salt ()
+ password = request . form . get ( 'password' )
+ encrypted_password = encrypt_password ( password , salt )
+ ...
+
+ user = User (
+ username = username ,
+ email = email ,
+ password = encrypted_password ,
+ salt = salt
+ )
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-is_email_address_valid.html b/docs/v1/quart_imp_auth-is_email_address_valid.html
new file mode 100644
index 0000000..0ba562c
--- /dev/null
+++ b/docs/v1/quart_imp_auth-is_email_address_valid.html
@@ -0,0 +1,260 @@
+
+
+
+
+ Is_email_address_valid - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ is_email_address_valid - quart_imp.auth
+ from quart_imp.auth import is_email_address_valid
+
+is_email_address_valid (
+ email_address : str
+) -> bool
+
+
+Checks if an email address is valid.
+Is not completely RFC 5322 compliant, but it is good enough for most use cases.
+Here are examples of mistakes that it will not catch:
+Valid but fails:
+ email@[123.123.123.123]
+“email”@example.com
+very.unusual.“@”.unusual.com@example.com
+very.“(),:;<>[]”.VERY.“very@\\ "very”.unusual@strange.example.com
+
+Invalid but passes:
+ email@example.com (Joe Smith)
+email@111.222.333.44444
+
+Example:
+is_email_address_valid ( 'hello@example.com' ) # >>> True
+
+is_email_address_valid ( 'hello@hello@example.com' ) # >>> False
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_auth-is_username_valid.html b/docs/v1/quart_imp_auth-is_username_valid.html
new file mode 100644
index 0000000..8b0e8cb
--- /dev/null
+++ b/docs/v1/quart_imp_auth-is_username_valid.html
@@ -0,0 +1,269 @@
+
+
+
+
+ Is_username_valid - Quart_imp.auth | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ is_username_valid - quart_imp.auth
+ from quart_imp.auth import is_username_valid
+
+is_username_valid (
+ username : str ,
+ allowed : t . Optional [ t . List [ t . Literal [ "all" , "dot" , "dash" , "under" ]]] = None
+) -> bool
+
+
+Checks if a username is valid.
+Valid usernames can only include letters,
+numbers, ., -, and _ but cannot begin or end with
+the last three mentioned.
+Example "all":
+is_username_valid ( "username" , allowed = [ "all" ])
+
+Output:
+ username : WILL PASS : True
+user.name : WILL PASS : True
+user-name : WILL PASS : True
+user_name : WILL PASS : True
+_user_name : WILL PASS : False
+
+Example "dot", "dash":
+is_username_valid ( "username" , allowed = [ "dot" , "dash" ])
+
+Output:
+ username : WILL PASS : True
+user.name : WILL PASS : True
+user-name : WILL PASS : True
+user-name.name : WILL PASS : True
+user_name : WILL PASS : False
+_user_name : WILL PASS : False
+.user.name : WILL PASS : False
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_config-impblueprintconfig.html b/docs/v1/quart_imp_config-impblueprintconfig.html
new file mode 100644
index 0000000..a491c3d
--- /dev/null
+++ b/docs/v1/quart_imp_config-impblueprintconfig.html
@@ -0,0 +1,255 @@
+
+
+
+
+ Impblueprintconfig - Quart_imp.config | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpBlueprintConfig - quart_imp.config
+ from quart_imp.config import ImpBlueprintConfig
+
+ImpBlueprintConfig (
+ enabled : bool = False ,
+ url_prefix : str = None ,
+ subdomain : str = None ,
+ url_defaults : dict = None ,
+ static_folder : t . Optional [ str ] = None ,
+ template_folder : t . Optional [ str ] = None ,
+ static_url_path : t . Optional [ str ] = None ,
+ root_path : str = None ,
+ cli_group : str = None ,
+ init_session : dict = None
+)
+
+
+A class that holds a Quart-Imp blueprint configuration.
+Most of these values are passed to the Blueprint class when the blueprint is registered.
+The enabled argument is used to enable or disable the blueprint. This is useful for feature flags.
+init_session is used to set the session values in the main before_request function.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_config-impconfig.html b/docs/v1/quart_imp_config-impconfig.html
new file mode 100644
index 0000000..4d2aae0
--- /dev/null
+++ b/docs/v1/quart_imp_config-impconfig.html
@@ -0,0 +1,255 @@
+
+
+
+
+ Impconfig - Quart_imp.config | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ ImpConfig - quart_imp.config
+ from quart_imp.config import ImpConfig
+
+ImpConfig (
+ init_session : t . Optional [ t . Dict [ str , t . Any ]] = None ,
+)
+
+
+The ImpConfig class is used to set the initial session data
+that the application will use.
+imp_config = ImpConfig (
+ init_session = { "key" : "value" },
+)
+
+
+def create_app ():
+ app = Quart ( __name__ )
+ QuartConfig ( debug = True , app_instance = app )
+ imp . init_app ( app , imp_config )
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_config-quartconfig.html b/docs/v1/quart_imp_config-quartconfig.html
new file mode 100644
index 0000000..ac84f8b
--- /dev/null
+++ b/docs/v1/quart_imp_config-quartconfig.html
@@ -0,0 +1,280 @@
+
+
+
+
+ Quartconfig - Quart_imp.config | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ QuartConfig - quart_imp.config
+ from quart_imp.config import QuartConfig
+
+QuartConfig (
+ debug : t . Optional [ bool ] = None ,
+ propagate_exceptions : t . Optional [ bool ] = None ,
+ trap_http_exceptions : t . Optional [ bool ] = None ,
+ trap_bad_request_errors : t . Optional [ bool ] = None ,
+ secret_key : t . Optional [ str ] = None ,
+ session_cookie_name : t . Optional [ str ] = None ,
+ session_cookie_domain : t . Optional [ str ] = None ,
+ session_cookie_path : t . Optional [ str ] = None ,
+ session_cookie_httponly : t . Optional [ bool ] = None ,
+ session_cookie_secure : t . Optional [ bool ] = None ,
+ session_cookie_samesite : t . Optional [ t . Literal [ "Lax" , "Strict" ]] = None ,
+ permanent_session_lifetime : t . Optional [ int ] = None ,
+ session_refresh_each_request : t . Optional [ bool ] = None ,
+ use_x_sendfile : t . Optional [ bool ] = None ,
+ send_file_max_age_default : t . Optional [ int ] = None ,
+ error_404_help : t . Optional [ bool ] = None ,
+ server_name : t . Optional [ str ] = None ,
+ application_root : t . Optional [ str ] = None ,
+ preferred_url_scheme : t . Optional [ str ] = None ,
+ max_content_length : t . Optional [ int ] = None ,
+ templates_auto_reload : t . Optional [ bool ] = None ,
+ explain_template_loading : t . Optional [ bool ] = None ,
+ max_cookie_size : t . Optional [ int ] = None ,
+ app_instance : t . Optional [ "Quart" ] = None
+)
+
+
+A class that holds a Quart configuration values.
+You can set the configuration values to the app instance by either passing the app instance to the app_instance
+parameter or by calling the apply_config method on the QuartConfig instance.
+def create_app ():
+ app = Quart ( __name__ )
+ QuartConfig ( debug = True , app_instance = app )
+ return app
+
+or
+def create_app ():
+ app = Quart ( __name__ )
+ config = QuartConfig ( debug = True )
+ config . apply_config ( app )
+ return app
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_security-api_login_check.html b/docs/v1/quart_imp_security-api_login_check.html
new file mode 100644
index 0000000..c2f7620
--- /dev/null
+++ b/docs/v1/quart_imp_security-api_login_check.html
@@ -0,0 +1,261 @@
+
+
+
+
+ Api_login_check - Quart_imp.security | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ api_login_check - quart_imp.security
+ from quart_imp.security import api_login_check
+
+api_login_check (
+ session_key : str ,
+ values_allowed : t . Union [ t . List [ t . Union [ str , int , bool ]], str , int , bool ],
+ fail_json : t . Optional [ t . Dict [ str , t . Any ]] = None
+)
+
+@api_login_check(...)
+
+A decorator that is used to secure API routes that return JSON responses.
+session_key The session key to check for.
+values_allowed A list of or singular value(s) that the session key must contain.
+fail_json JSON that is returned on failure. {"error": "You are not logged in."} by default.
+Example:
+@bp . route ( "/api/resource" , methods = [ "GET" ])
+@api_login_check ( 'logged_in' , True )
+async def api_page ():
+ ...
+
+Example of defined fail_json:
+@bp . route ( "/api/resource" , methods = [ "GET" ])
+@api_login_check ( 'logged_in' , True , fail_json = { "failed" : "You need to be logged in." })
+async def api_page ():
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_security-include_csrf.html b/docs/v1/quart_imp_security-include_csrf.html
new file mode 100644
index 0000000..9891d45
--- /dev/null
+++ b/docs/v1/quart_imp_security-include_csrf.html
@@ -0,0 +1,265 @@
+
+
+
+
+ Include_csrf - Quart_imp.security | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ include_csrf - quart_imp.security
+ from quart_imp.security import include_csrf
+
+include_csrf (
+ session_key : str = "csrf" ,
+ form_key : str = "csrf" ,
+ abort_code : int = 401
+)
+
+@include_csrf(...)
+
+A decorator that handles CSRF protection.
+On a GET request, a CSRF token is generated and stored in the session key
+specified by the session_key parameter.
+On a POST request, the form_key specified is checked against the session_key
+specified.
+
+If they match, the request is allowed to continue.
+If no match, the response will be abort(abort_code), default 401.
+
+@bp . route ( "/admin" , methods = [ "GET" , "POST" ])
+@include_csrf ( session_key = "csrf" , form_key = "csrf" )
+async def admin_page ():
+ ...
+ # You must pass in the CSRF token from the session into the template.
+ # Then add <input type="hidden" name="csrf" value="{{ csrf }}"> to the form.
+ return await render_template ( "admin.html" , csrf = session . get ( "csrf" ))
+
+Form key:
+< input type = "hidden" name = "csrf" value = "{{ csrf }}" >
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_security-login_check.html b/docs/v1/quart_imp_security-login_check.html
new file mode 100644
index 0000000..0f15eb9
--- /dev/null
+++ b/docs/v1/quart_imp_security-login_check.html
@@ -0,0 +1,278 @@
+
+
+
+
+ Login_check - Quart_imp.security | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ login_check - quart_imp.security
+ from quart_imp.security import login_check
+
+login_check (
+ session_key : str ,
+ values_allowed : t . Union [ t . List [ t . Union [ str , int , bool ]], str , int , bool ],
+ fail_endpoint : t . Optional [ str ] = None ,
+ pass_endpoint : t . Optional [ str ] = None ,
+ endpoint_kwargs : t . Optional [ t . Dict [ str , t . Union [ str , int ]]] = None ,
+ message : t . Optional [ str ] = None ,
+ message_category : str = "message"
+)
+
+@login_check(...)
+
+A decorator that checks if the specified session key exists and contains the specified value.
+session_key The session key to check for.
+values_allowed A list of or singular value(s) that the session key must contain.
+fail_endpoint The endpoint to redirect to if the session key does not exist or does not contain the specified values.
+endpoint_kwargs A dictionary of keyword arguments to pass to the redirect endpoint.
+message If a message is specified, a flash message is shown.
+message_category The category of the flash message.
+Example of a route that requires a user to be logged in:
+@bp . route ( "/admin" , methods = [ "GET" ])
+@login_check (
+ 'logged_in' ,
+ True ,
+ fail_endpoint = 'blueprint.login_page' ,
+ message = "Login needed"
+)
+async def admin_page ():
+ ...
+
+Example of a route that if the user is already logged in, redirects to the specified endpoint:
+@bp . route ( "/login-page" , methods = [ "GET" ])
+@login_check (
+ 'logged_in' ,
+ True ,
+ pass_endpoint = 'blueprint.admin_page' ,
+ message = "Already logged in"
+)
+async def login_page ():
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_security-pass_function_check.html b/docs/v1/quart_imp_security-pass_function_check.html
new file mode 100644
index 0000000..501b5c7
--- /dev/null
+++ b/docs/v1/quart_imp_security-pass_function_check.html
@@ -0,0 +1,326 @@
+
+
+
+
+ Pass_function_check - Quart_imp.security | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ pass_function_check - quart_imp.security
+ from quart_imp.security import pass_function_check
+
+def pass_function_check (
+ function : t . Callable ,
+ predefined_args : t . Optional [ t . Dict ] = None ,
+ fail_endpoint : t . Optional [ str ] = None ,
+ pass_endpoint : t . Optional [ str ] = None ,
+ endpoint_kwargs : t . Optional [ t . Dict [ str , t . Union [ str , int ]]] = None ,
+ message : t . Optional [ str ] = None ,
+ message_category : str = "message" ,
+ fail_on_missing_kwargs : bool = False ,
+ with_app_context : bool = False ,
+)
+
+NOTE: This was added mostly as an experimental feature, but ended up being useful in some cases.
+A decorator that takes the result of a function and checks if it is True or False.
+URL variables from @route will be read by this decorator.
+To use URL variables in your passed in function,
+make sure your functions argument(s) name(s) match the name(s) of the URL variable(s).
+Example:
+def check_if_number ( value ):
+ if isinstance ( value , int ):
+ return True
+ return False
+
+@bp . route ( "/admin-page/<int:value>" , methods = [ "GET" ])
+@login_check ( 'logged_in' , True , 'blueprint.login_page' ) # can be mixed with login_check
+@pass_function_check (
+ check_if_number ,
+ predefined_args = None ,
+ fail_endpoint = 'www.index' ,
+ message = "Failed message"
+)
+async def admin_page ():
+ ...
+
+@bp . route ( "/admin-page/<int:value>" , methods = [ "GET" ])
+@login_check ( 'logged_in' , True , 'blueprint.login_page' ) # can be mixed with login_check
+@pass_function_check (
+ check_if_number ,
+ predefined_args = { 'value' : 10 },
+ fail_endpoint = 'www.index' ,
+ message = "Failed message"
+)
+async def admin_page_overwrite ():
+ ...
+
+Advanced use case:
+Here's an example of accessing quart.session from within the passed in function. including the
+with_app_context parameter, the function will be called with app_context().
+from quart import current_app
+from quart import session
+
+...
+
+def check_if_number ( number = 1 , session_ = None ):
+ if session_ :
+ print ( session_ )
+ try :
+ int ( number )
+ return True
+ except ValueError :
+ return False
+
+@bp . route ( "/pass-func-check-with-url-var/<number>" , methods = [ "GET" ])
+@pass_function_check (
+ check_if_number ,
+ predefined_args = { 'number' : 10 , 'session_' : session },
+ fail_endpoint = "www.index" ,
+ with_app_context = True
+)
+async def admin_page_overwrite_with_session ():
+ ...
+
+If you pass in a predefined arg that has the same key name as a session variable that exists, the value
+of that predefined arg will be replaced with the session variable value.
+session [ 'car' ] = 'Toyota'
+...
+def check_function ( car ):
+ if car == 'Toyota' :
+ return True
+ return False
+...
+@bp . route ( "/pass-func-check-with-url-var/<number>" , methods = [ "GET" ])
+@pass_function_check (
+ check_function ,
+ predefined_args = { 'car' : session },
+ ...
+
+This will pass, as pass_function_check will replace the value of the predefined arg 'car' with the value
+of the session variable 'car'.
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/quart_imp_security-permission_check.html b/docs/v1/quart_imp_security-permission_check.html
new file mode 100644
index 0000000..2e78f9a
--- /dev/null
+++ b/docs/v1/quart_imp_security-permission_check.html
@@ -0,0 +1,271 @@
+
+
+
+
+ Permission_check - Quart_imp.security | Quart-Imp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Quart-Imp
+ Version: 1.0.x
+ Last Updated:
+ Fri, 16 Aug 2024
+ Menu
+
+
+
+
+ CLI Commands
+
+
+
+
+
+
+ Imp
+
+
+
+
+
+
+ ImpBlueprint
+
+
+
+
+
+
+ quart_imp.config
+
+
+
+
+
+
+ quart_imp.security
+
+
+
+
+
+
+ quart_imp.auth
+
+
+
+
+
+
+
+ Hosted on GitHub Pages.
+
+
+
+ permission_check - quart_imp.security
+ from quart_imp.security import permission_check
+
+permission_check (
+ session_key : str ,
+ values_allowed : t . Union [ t . List [ t . Union [ str , int , bool ]], str , int , bool ],
+ fail_endpoint : t . Optional [ str ] = None ,
+ endpoint_kwargs : t . Optional [ t . Dict [ str , t . Union [ str , int ]]] = None ,
+ message : t . Optional [ str ] = None ,
+ message_category : str = "message"
+)
+
+@permission_check(...)
+
+A decorator that checks if the specified session key exists and its value(s) match the specified value(s).
+session_key The session key to check for.
+values_allowed A list of or singular value(s) that the session key must contain.
+fail_endpoint The endpoint to redirect to if the session key does not exist or does not contain the specified values.
+endpoint_kwargs A dictionary of keyword arguments to pass to the redirect endpoint.
+message If a message is specified, a flash message is shown.
+message_category The category of the flash message.
+Example:
+@bp . route ( "/admin-page" , methods = [ "GET" ])
+@login_check (
+ 'logged_in' ,
+ True ,
+ 'blueprint.login_page'
+) # can be mixed with login_check
+@permission_check (
+ 'permissions' ,
+ [ 'admin' ],
+ fail_endpoint = 'www.index' ,
+ message = "Failed message"
+)
+async def admin_page ():
+ ...
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/v1/static/Flask-Imp-Medium.png b/docs/v1/static/Flask-Imp-Medium.png
new file mode 100644
index 0000000000000000000000000000000000000000..1140eb4cc5c4f9538ea0a3c788db40a8fe08a663
GIT binary patch
literal 7987
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
literal 0
HcmV?d00001
diff --git a/docs/v1/static/android-chrome-192x192.png b/docs/v1/static/android-chrome-192x192.png
new file mode 100644
index 0000000000000000000000000000000000000000..1207f940bfb8ce81bb79fded911cd9167265ce42
GIT binary patch
literal 15163
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
literal 0
HcmV?d00001
diff --git a/docs/v1/static/android-chrome-256x256.png b/docs/v1/static/android-chrome-256x256.png
new file mode 100644
index 0000000000000000000000000000000000000000..34e3d27479cf54144da963f21a635d7487f171ef
GIT binary patch
literal 20999
zcmV*9Kybf_P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006
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