aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-08-24 13:15:31 -0400
committerCara Salter <cara@devcara.com>2022-08-24 13:15:31 -0400
commit9de84e3fbae0f2893e9c4f1425afa06899959bf7 (patch)
treef96d33d881954edc9b03df0bc821fb35eb59c577
parent4b616447715b8129ae322341959e1c2bfabbd10e (diff)
downloadnccd-9de84e3fbae0f2893e9c4f1425afa06899959bf7.tar.gz
nccd-9de84e3fbae0f2893e9c4f1425afa06899959bf7.zip
flaskify
-rw-r--r--.flaskenv2
-rw-r--r--.gitignore199
-rw-r--r--Cargo.lock2565
-rw-r--r--Cargo.toml50
-rw-r--r--app/__init__.py41
-rw-r--r--app/auth/__init__.py3
-rw-r--r--app/auth/forms.py7
-rw-r--r--app/config.py16
-rw-r--r--app/database.py25
-rw-r--r--app/static/gen/style.css46
-rw-r--r--app/static/scss/style.scss (renamed from scss/style.scss)2
-rw-r--r--migrations-old/README1
-rw-r--r--migrations-old/alembic.ini50
-rw-r--r--migrations-old/env.py91
-rw-r--r--migrations-old/script.py.mako24
-rw-r--r--migrations/20220719122322_peer.sql11
-rw-r--r--migrations/20220719152547_users.sql8
-rw-r--r--migrations/README1
-rw-r--r--migrations/alembic.ini50
-rw-r--r--migrations/env.py91
-rw-r--r--migrations/script.py.mako24
-rw-r--r--migrations/versions/81173c1d4f0a_initial_migration.py90
-rw-r--r--migrations/versions/946e687d2d42_roles_users.py35
-rw-r--r--src/build.rs10
-rw-r--r--src/config.rs54
-rw-r--r--src/errors.rs49
-rw-r--r--src/handlers/auth.rs117
-rw-r--r--src/handlers/mod.rs24
-rw-r--r--src/handlers/nets.rs50
-rw-r--r--src/main.rs185
-rw-r--r--src/models.rs24
-rw-r--r--src/utils.rs51
-rw-r--r--templates/footer.rs.html7
-rw-r--r--templates/header.rs.html14
-rw-r--r--templates/index.rs.html71
-rw-r--r--templates/login.rs.html22
-rw-r--r--templates/new_net.rs.html21
-rw-r--r--templates/register.rs.html27
38 files changed, 797 insertions, 3361 deletions
diff --git a/.flaskenv b/.flaskenv
new file mode 100644
index 0000000..37195f2
--- /dev/null
+++ b/.flaskenv
@@ -0,0 +1,2 @@
+FLASK_APP=app.py:create_app
+FLASK_ENV=development
diff --git a/.gitignore b/.gitignore
index c159c44..12e9608 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,202 @@
/target
design/
.keypair
+# Created by https://www.toptal.com/developers/gitignore/api/python,virtualenv,vim
+# Edit at https://www.toptal.com/developers/gitignore?templates=python,virtualenv,vim
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+!*.svg # comment out if you don't need vector files
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+### VirtualEnv ###
+# Virtualenv
+# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
+[Bb]in
+[Ii]nclude
+[Ll]ib
+[Ll]ib64
+[Ll]ocal
+[Ss]cripts
+pyvenv.cfg
+pip-selfcheck.json
+
+# End of https://www.toptal.com/developers/gitignore/api/python,virtualenv,vim
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index 9010c78..0000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,2565 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "addr2line"
-version = "0.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "aead"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "aes"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
-dependencies = [
- "cfg-if 1.0.0",
- "cipher 0.3.0",
- "cpufeatures",
- "opaque-debug",
-]
-
-[[package]]
-name = "aes-gcm"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
-dependencies = [
- "aead",
- "aes",
- "cipher 0.3.0",
- "ctr",
- "ghash",
- "subtle",
-]
-
-[[package]]
-name = "ahash"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
-dependencies = [
- "getrandom",
- "once_cell",
- "version_check",
-]
-
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.58"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
-
-[[package]]
-name = "arrayref"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
-
-[[package]]
-name = "arrayvec"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
-
-[[package]]
-name = "async-lock"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
-dependencies = [
- "event-listener",
-]
-
-[[package]]
-name = "async-session"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07da4ce523b4e2ebaaf330746761df23a465b951a83d84bbce4233dabedae630"
-dependencies = [
- "anyhow",
- "async-lock",
- "async-trait",
- "base64",
- "bincode",
- "blake3",
- "chrono",
- "hmac 0.11.0",
- "log",
- "rand",
- "serde",
- "serde_json",
- "sha2 0.9.9",
-]
-
-[[package]]
-name = "async-trait"
-version = "0.1.56"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "atoi"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
-name = "axum"
-version = "0.5.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b9496f0c1d1afb7a2af4338bbe1d969cddfead41d87a9fb3aaa6d0bbc7af648"
-dependencies = [
- "async-trait",
- "axum-core",
- "bitflags",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "itoa",
- "matchit",
- "memchr",
- "mime",
- "percent-encoding",
- "pin-project-lite",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tower",
- "tower-http",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum-core"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635"
-dependencies = [
- "async-trait",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "mime",
-]
-
-[[package]]
-name = "axum-extra"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44"
-dependencies = [
- "axum",
- "bytes",
- "cookie",
- "futures-util",
- "http",
- "mime",
- "pin-project-lite",
- "tokio",
- "tower",
- "tower-http",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum-macros"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6293dae2ec708e679da6736e857cf8532886ef258e92930f38279c12641628b8"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "backtrace"
-version = "0.3.66"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if 1.0.0",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
-]
-
-[[package]]
-name = "base64"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
-
-[[package]]
-name = "bcrypt"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7e7c93a3fb23b2fdde989b2c9ec4dd153063ec81f408507f84c090cd91c6641"
-dependencies = [
- "base64",
- "blowfish",
- "getrandom",
- "zeroize",
-]
-
-[[package]]
-name = "bincode"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "blake3"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b64485778c4f16a6a5a9d335e80d449ac6c70cdd6a06d2af18a6f6f775a125b3"
-dependencies = [
- "arrayref",
- "arrayvec",
- "cc",
- "cfg-if 0.1.10",
- "constant_time_eq",
- "crypto-mac 0.8.0",
- "digest 0.9.0",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "blowfish"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
-dependencies = [
- "byteorder",
- "cipher 0.4.3",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
-
-[[package]]
-name = "bytecount"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
-
-[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
-name = "bytes"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
-
-[[package]]
-name = "cc"
-version = "1.0.73"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
-
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "chrono"
-version = "0.4.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
-dependencies = [
- "libc",
- "num-integer",
- "num-traits",
- "serde",
- "time 0.1.44",
- "winapi",
-]
-
-[[package]]
-name = "cipher"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "cipher"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e"
-dependencies = [
- "crypto-common",
- "inout",
-]
-
-[[package]]
-name = "color-eyre"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
-dependencies = [
- "backtrace",
- "color-spantrace",
- "eyre",
- "indenter",
- "once_cell",
- "owo-colors",
- "tracing-error",
-]
-
-[[package]]
-name = "color-spantrace"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
-dependencies = [
- "once_cell",
- "owo-colors",
- "tracing-core",
- "tracing-error",
-]
-
-[[package]]
-name = "constant_time_eq"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
-
-[[package]]
-name = "cookie"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
-dependencies = [
- "aes-gcm",
- "base64",
- "hmac 0.12.1",
- "percent-encoding",
- "rand",
- "sha2 0.10.2",
- "subtle",
- "time 0.3.11",
- "version_check",
-]
-
-[[package]]
-name = "core-foundation"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "crc"
-version = "3.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3"
-dependencies = [
- "crc-catalog",
-]
-
-[[package]]
-name = "crc-catalog"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff"
-
-[[package]]
-name = "crossbeam-queue"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
-dependencies = [
- "cfg-if 1.0.0",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
-dependencies = [
- "cfg-if 1.0.0",
- "once_cell",
-]
-
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "crypto-mac"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
-[[package]]
-name = "crypto-mac"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
-[[package]]
-name = "ctr"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
-dependencies = [
- "cipher 0.3.0",
-]
-
-[[package]]
-name = "digest"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
-dependencies = [
- "block-buffer 0.10.2",
- "crypto-common",
- "subtle",
-]
-
-[[package]]
-name = "dirs"
-version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
-[[package]]
-name = "dotenv"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
-
-[[package]]
-name = "either"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
-
-[[package]]
-name = "email-encoding"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34dd14c63662e0206599796cd5e1ad0268ab2b9d19b868d6050d688eba2bbf98"
-dependencies = [
- "base64",
- "memchr",
-]
-
-[[package]]
-name = "email_address"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8684b7c9cb4857dfa1e5b9629ef584ba618c9b93bae60f58cb23f4f271d0468e"
-
-[[package]]
-name = "event-listener"
-version = "2.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
-
-[[package]]
-name = "eyre"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
-dependencies = [
- "indenter",
- "once_cell",
-]
-
-[[package]]
-name = "fastrand"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
-dependencies = [
- "instant",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "foreign-types"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
-dependencies = [
- "foreign-types-shared",
-]
-
-[[package]]
-name = "foreign-types-shared"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
-dependencies = [
- "matches",
- "percent-encoding",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
-
-[[package]]
-name = "futures-intrusive"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62007592ac46aa7c2b6416f7deb9a8a8f63a01e0f1d6e1787d5630170db2b63e"
-dependencies = [
- "futures-core",
- "lock_api",
- "parking_lot 0.11.2",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
-
-[[package]]
-name = "futures-sink"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
-
-[[package]]
-name = "futures-task"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
-
-[[package]]
-name = "futures-util"
-version = "0.3.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
-dependencies = [
- "futures-core",
- "futures-io",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "wasi 0.11.0+wasi-snapshot-preview1",
-]
-
-[[package]]
-name = "ghash"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
-dependencies = [
- "opaque-debug",
- "polyval",
-]
-
-[[package]]
-name = "gimli"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
-
-[[package]]
-name = "h2"
-version = "0.3.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "futures-util",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.12.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022"
-dependencies = [
- "ahash",
-]
-
-[[package]]
-name = "hashlink"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d452c155cb93fecdfb02a73dd57b5d8e442c2063bd7aac72f1bc5e4263a43086"
-dependencies = [
- "hashbrown",
-]
-
-[[package]]
-name = "heck"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hex"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-
-[[package]]
-name = "hkdf"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
-dependencies = [
- "hmac 0.12.1",
-]
-
-[[package]]
-name = "hmac"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
-dependencies = [
- "crypto-mac 0.11.1",
- "digest 0.9.0",
-]
-
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest 0.10.3",
-]
-
-[[package]]
-name = "hostname"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
-dependencies = [
- "libc",
- "match_cfg",
- "winapi",
-]
-
-[[package]]
-name = "http"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
-dependencies = [
- "bytes",
- "http",
- "pin-project-lite",
-]
-
-[[package]]
-name = "http-range-header"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
-
-[[package]]
-name = "httparse"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
-
-[[package]]
-name = "httpdate"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
-
-[[package]]
-name = "hyper"
-version = "0.14.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
-[[package]]
-name = "idna"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
-dependencies = [
- "matches",
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "indenter"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
-
-[[package]]
-name = "indexmap"
-version = "1.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
-name = "inout"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "ipnetwork"
-version = "0.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f84f1612606f3753f205a4e9a2efd6fe5b4c573a6269b2cc6c3003d44a0d127"
-
-[[package]]
-name = "ipnetwork"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "itertools"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
-
-[[package]]
-name = "js-sys"
-version = "0.3.58"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "lettre"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eabca5e0b4d0e98e7f2243fb5b7520b6af2b65d8f87bcc86f2c75185a6ff243"
-dependencies = [
- "async-trait",
- "base64",
- "email-encoding",
- "email_address",
- "fastrand",
- "futures-io",
- "futures-util",
- "hostname",
- "httpdate",
- "idna",
- "mime",
- "native-tls",
- "nom",
- "once_cell",
- "quoted_printable",
- "socket2",
- "tokio",
- "tokio-native-tls",
- "tracing",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.126"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
-
-[[package]]
-name = "lock_api"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if 1.0.0",
-]
-
-[[package]]
-name = "match_cfg"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
-
-[[package]]
-name = "matchers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata",
-]
-
-[[package]]
-name = "matches"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
-
-[[package]]
-name = "matchit"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
-
-[[package]]
-name = "md-5"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "658646b21e0b72f7866c7038ab086d3d5e1cd6271f060fd37defb241949d0582"
-dependencies = [
- "digest 0.10.3",
-]
-
-[[package]]
-name = "md5"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
-
-[[package]]
-name = "memchr"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
-
-[[package]]
-name = "mime"
-version = "0.3.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
-
-[[package]]
-name = "mime_guess"
-version = "2.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
-dependencies = [
- "mime",
- "unicase",
-]
-
-[[package]]
-name = "minimal-lexical"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-
-[[package]]
-name = "miniz_oxide"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
-dependencies = [
- "adler",
-]
-
-[[package]]
-name = "mio"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
-dependencies = [
- "libc",
- "log",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys",
-]
-
-[[package]]
-name = "native-tls"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
-dependencies = [
- "lazy_static",
- "libc",
- "log",
- "openssl",
- "openssl-probe",
- "openssl-sys",
- "schannel",
- "security-framework",
- "security-framework-sys",
- "tempfile",
-]
-
-[[package]]
-name = "nccd"
-version = "0.1.0"
-dependencies = [
- "async-session",
- "axum",
- "axum-extra",
- "axum-macros",
- "bcrypt",
- "chrono",
- "color-eyre",
- "hyper",
- "ipnetwork 0.20.0",
- "lettre",
- "ructe",
- "serde",
- "sqlx",
- "thiserror",
- "tokio",
- "toml",
- "tower",
- "tower-http",
- "tracing",
- "tracing-subscriber",
- "ulid",
-]
-
-[[package]]
-name = "nom"
-version = "7.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
-dependencies = [
- "memchr",
- "minimal-lexical",
-]
-
-[[package]]
-name = "nom_locate"
-version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605"
-dependencies = [
- "bytecount",
- "memchr",
- "nom",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
-dependencies = [
- "autocfg",
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
-name = "num_threads"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "object"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
-
-[[package]]
-name = "opaque-debug"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-
-[[package]]
-name = "openssl"
-version = "0.10.41"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
-dependencies = [
- "bitflags",
- "cfg-if 1.0.0",
- "foreign-types",
- "libc",
- "once_cell",
- "openssl-macros",
- "openssl-sys",
-]
-
-[[package]]
-name = "openssl-macros"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "openssl-probe"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
-
-[[package]]
-name = "openssl-sys"
-version = "0.9.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f"
-dependencies = [
- "autocfg",
- "cc",
- "libc",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "owo-colors"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
-
-[[package]]
-name = "parking_lot"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core 0.8.5",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
-dependencies = [
- "lock_api",
- "parking_lot_core 0.9.3",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
-dependencies = [
- "cfg-if 1.0.0",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
-dependencies = [
- "cfg-if 1.0.0",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-sys",
-]
-
-[[package]]
-name = "paste"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
-
-[[package]]
-name = "percent-encoding"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
-
-[[package]]
-name = "pin-project"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
-
-[[package]]
-name = "polyval"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
-dependencies = [
- "cfg-if 1.0.0",
- "cpufeatures",
- "opaque-debug",
- "universal-hash",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "quoted_printable"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fee2dce59f7a43418e3382c766554c614e06a552d53a8f07ef499ea4b332c0f"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
-dependencies = [
- "getrandom",
- "redox_syscall",
- "thiserror",
-]
-
-[[package]]
-name = "regex"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
-dependencies = [
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
-
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "ring"
-version = "0.16.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
-dependencies = [
- "cc",
- "libc",
- "once_cell",
- "spin",
- "untrusted",
- "web-sys",
- "winapi",
-]
-
-[[package]]
-name = "rsass"
-version = "0.23.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92bfc58e02cd215730b021f9e6f0f46e0a7e1f75aa558ca7a7492763fbe88df2"
-dependencies = [
- "fastrand",
- "lazy_static",
- "nom",
- "nom_locate",
- "num-bigint",
- "num-integer",
- "num-rational",
- "num-traits",
-]
-
-[[package]]
-name = "ructe"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef88d8c2492d7266e264b31e0ffcf1149d5ba183bccd3abaf1483ee905fc85de"
-dependencies = [
- "base64",
- "bytecount",
- "itertools",
- "md5",
- "nom",
- "rsass",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
-
-[[package]]
-name = "rustls"
-version = "0.20.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
-dependencies = [
- "log",
- "ring",
- "sct",
- "webpki",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9"
-dependencies = [
- "base64",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
-
-[[package]]
-name = "schannel"
-version = "0.1.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
-dependencies = [
- "lazy_static",
- "windows-sys",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
-name = "sct"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "security-framework"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
-dependencies = [
- "bitflags",
- "core-foundation",
- "core-foundation-sys",
- "libc",
- "security-framework-sys",
-]
-
-[[package]]
-name = "security-framework-sys"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "serde"
-version = "1.0.139"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.139"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.82"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
-dependencies = [
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "sha-1"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
-dependencies = [
- "cfg-if 1.0.0",
- "cpufeatures",
- "digest 0.10.3",
-]
-
-[[package]]
-name = "sha2"
-version = "0.9.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
-dependencies = [
- "block-buffer 0.9.0",
- "cfg-if 1.0.0",
- "cpufeatures",
- "digest 0.9.0",
- "opaque-debug",
-]
-
-[[package]]
-name = "sha2"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
-dependencies = [
- "cfg-if 1.0.0",
- "cpufeatures",
- "digest 0.10.3",
-]
-
-[[package]]
-name = "sharded-slab"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "slab"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
-
-[[package]]
-name = "smallvec"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
-
-[[package]]
-name = "socket2"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "spin"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-
-[[package]]
-name = "sqlformat"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
-dependencies = [
- "itertools",
- "nom",
- "unicode_categories",
-]
-
-[[package]]
-name = "sqlx"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f82cbe94f41641d6c410ded25bbf5097c240cefdf8e3b06d04198d0a96af6a4"
-dependencies = [
- "sqlx-core",
- "sqlx-macros",
-]
-
-[[package]]
-name = "sqlx-core"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b69bf218860335ddda60d6ce85ee39f6cf6e5630e300e19757d1de15886a093"
-dependencies = [
- "ahash",
- "atoi",
- "base64",
- "bitflags",
- "byteorder",
- "bytes",
- "chrono",
- "crc",
- "crossbeam-queue",
- "dirs",
- "either",
- "event-listener",
- "futures-channel",
- "futures-core",
- "futures-intrusive",
- "futures-util",
- "hashlink",
- "hex",
- "hkdf",
- "hmac 0.12.1",
- "indexmap",
- "ipnetwork 0.19.0",
- "itoa",
- "libc",
- "log",
- "md-5",
- "memchr",
- "once_cell",
- "paste",
- "percent-encoding",
- "rand",
- "rustls",
- "rustls-pemfile",
- "serde",
- "serde_json",
- "sha-1",
- "sha2 0.10.2",
- "smallvec",
- "sqlformat",
- "sqlx-rt",
- "stringprep",
- "thiserror",
- "tokio-stream",
- "url",
- "webpki-roots",
- "whoami",
-]
-
-[[package]]
-name = "sqlx-macros"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40c63177cf23d356b159b60acd27c54af7423f1736988502e36bae9a712118f"
-dependencies = [
- "dotenv",
- "either",
- "heck",
- "once_cell",
- "proc-macro2",
- "quote",
- "sha2 0.10.2",
- "sqlx-core",
- "sqlx-rt",
- "syn",
- "url",
-]
-
-[[package]]
-name = "sqlx-rt"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "874e93a365a598dc3dadb197565952cb143ae4aa716f7bcc933a8d836f6bf89f"
-dependencies = [
- "once_cell",
- "tokio",
- "tokio-rustls",
-]
-
-[[package]]
-name = "stringprep"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "subtle"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
-
-[[package]]
-name = "syn"
-version = "1.0.98"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "sync_wrapper"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
-
-[[package]]
-name = "tempfile"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
-dependencies = [
- "cfg-if 1.0.0",
- "fastrand",
- "libc",
- "redox_syscall",
- "remove_dir_all",
- "winapi",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
-name = "time"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
-dependencies = [
- "libc",
- "wasi 0.10.0+wasi-snapshot-preview1",
- "winapi",
-]
-
-[[package]]
-name = "time"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
-dependencies = [
- "itoa",
- "libc",
- "num_threads",
- "time-macros",
-]
-
-[[package]]
-name = "time-macros"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
-
-[[package]]
-name = "tinyvec"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
-
-[[package]]
-name = "tokio"
-version = "1.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e"
-dependencies = [
- "autocfg",
- "bytes",
- "libc",
- "memchr",
- "mio",
- "num_cpus",
- "once_cell",
- "parking_lot 0.12.1",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "winapi",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tokio-native-tls"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.23.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
-dependencies = [
- "rustls",
- "tokio",
- "webpki",
-]
-
-[[package]]
-name = "tokio-stream"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
- "tracing",
-]
-
-[[package]]
-name = "toml"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project",
- "pin-project-lite",
- "tokio",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba"
-dependencies = [
- "bitflags",
- "bytes",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "http-range-header",
- "httpdate",
- "mime",
- "mime_guess",
- "percent-encoding",
- "pin-project-lite",
- "tokio",
- "tokio-util",
- "tower",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
-
-[[package]]
-name = "tower-service"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
-
-[[package]]
-name = "tracing"
-version = "0.1.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
-dependencies = [
- "cfg-if 1.0.0",
- "log",
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
-dependencies = [
- "once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-error"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
-dependencies = [
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
-dependencies = [
- "lazy_static",
- "log",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59"
-dependencies = [
- "ansi_term",
- "matchers",
- "once_cell",
- "regex",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
-
-[[package]]
-name = "typenum"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
-
-[[package]]
-name = "ulid"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be932d774bfad49722da2c4915ac7cc77b77dd223890739a0240de2b2a15957"
-dependencies = [
- "rand",
- "time 0.3.11",
-]
-
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
-
-[[package]]
-name = "unicode_categories"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
-
-[[package]]
-name = "universal-hash"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
-dependencies = [
- "generic-array",
- "subtle",
-]
-
-[[package]]
-name = "untrusted"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
-
-[[package]]
-name = "url"
-version = "2.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
-dependencies = [
- "form_urlencoded",
- "idna",
- "matches",
- "percent-encoding",
-]
-
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
-[[package]]
-name = "version_check"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-
-[[package]]
-name = "want"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
-dependencies = [
- "log",
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.10.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
-dependencies = [
- "cfg-if 1.0.0",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
-dependencies = [
- "bumpalo",
- "lazy_static",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.81"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
-
-[[package]]
-name = "web-sys"
-version = "0.3.58"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "webpki"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
-dependencies = [
- "ring",
- "untrusted",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.22.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
-dependencies = [
- "webpki",
-]
-
-[[package]]
-name = "whoami"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524b58fa5a20a2fb3014dd6358b70e6579692a56ef6fce928834e488f42f65e8"
-dependencies = [
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_msvc",
-]
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
-
-[[package]]
-name = "zeroize"
-version = "1.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20b578acffd8516a6c3f2a1bdefc1ec37e547bb4e0fb8b6b01a4cafc886b4442"
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index 60187f6..0000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,50 +0,0 @@
-[package]
-name = "nccd"
-version = "0.1.0"
-edition = "2021"
-
-build = "src/build.rs"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-axum = { version = "0.5.13", features = ["json", "tower-log"] }
-axum-extra = { version = "0.3.6", features = ["spa", "cookie", "cookie-private", "cookie-signed"] }
-color-eyre = "0.6.2"
-hyper = { version = "0.14.20", features = ["full"] }
-serde = { version = "1.0.139", features = ["derive"] }
-thiserror = "1.0.31"
-tokio = { version = "1.20.0", features = ["full"] }
-toml = "0.5.9"
-tower = { version = "0.4.13", features = ["util", "timeout"] }
-tower-http = { version = "0.3.4", features = ["add-extension", "trace"] }
-tracing = "0.1.35"
-tracing-subscriber = { version = "0.3.14", features = ["env-filter"] }
-axum-macros = "0.2.3"
-chrono = "0.4.19"
-ulid = "0.6.0"
-async-session = "3.0.0"
-bcrypt = "0.13.0"
-ipnetwork = "0.20.0"
-lettre = { version = "0.10.1", features = ["tokio1", "tracing", "tokio1-native-tls"] }
-
-[dependencies.sqlx]
-version = "0.6"
-features = [
- "postgres",
- "ipnetwork",
- "chrono",
- "runtime-tokio-rustls"
- ]
-
-
-[dependencies.ructe]
-version = "0.14"
-features = [
- "sass"
-]
-[build-dependencies.ructe]
-version = "0.14"
-features = [
- "sass"
-]
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..e929535
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,41 @@
+
+from flask import Flask
+from flask_security.datastore import SQLAlchemyUserDatastore
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+from flask_security.core import Security
+
+from flask_assets import Bundle, Environment
+
+from . import config
+
+db = SQLAlchemy()
+migrate = Migrate()
+security = Security()
+environment = Environment()
+
+
+def create_app():
+ app = Flask(__name__)
+
+ app.config.from_file("/etc/nccd/config.toml", load=config.load_config)
+
+ db.init_app(app)
+ migrate.init_app(app, db)
+ environment.init_app(app)
+
+ # Static file init
+ scss = Bundle('scss/style.scss', filters='scss', output='gen/style.css')
+ environment.register('scss', scss)
+
+ from .database import User, Role
+ from .auth import forms as auth_forms
+ user_datastore = SQLAlchemyUserDatastore(db, User, Role)
+ security.init_app(app, user_datastore, register_form=auth_forms.ExtendedRegister)
+
+ # Blueprints
+ app.register_blueprint(auth.bp)
+
+ print(app.url_map)
+
+ return app
diff --git a/app/auth/__init__.py b/app/auth/__init__.py
new file mode 100644
index 0000000..52f0cd7
--- /dev/null
+++ b/app/auth/__init__.py
@@ -0,0 +1,3 @@
+from flask import Blueprint
+
+bp = Blueprint('auth', __name__, url_prefix='/auth')
diff --git a/app/auth/forms.py b/app/auth/forms.py
new file mode 100644
index 0000000..4814f48
--- /dev/null
+++ b/app/auth/forms.py
@@ -0,0 +1,7 @@
+from flask_security.forms import RegisterForm
+from wtforms import StringField
+from wtforms.validators import DataRequired
+
+class ExtendedRegister(RegisterForm):
+ pref_name = StringField('Preferred Name', [DataRequired()])
+
diff --git a/app/config.py b/app/config.py
new file mode 100644
index 0000000..29eb2ef
--- /dev/null
+++ b/app/config.py
@@ -0,0 +1,16 @@
+import toml
+
+def load_config(path):
+ contents = toml.load(path)
+
+ result = {
+ 'SQLALCHEMY_DATABASE_URI': contents['database']['postgres_url'],
+ 'SECRET_KEY': contents['server']['secret_key'],
+ 'MAIL_SERVER': contents['email']['smtp_server'],
+ 'MAIL_PORT': contents['email']['smtp_port'],
+ 'MAIL_USE_TLS': contents['email']['smtp_tls'],
+ 'SECURITY_REGISTERABLE': True,
+ 'SECURITY_PASSWORD_SALT': contents['server']['secret_key']
+ }
+
+ return result
diff --git a/app/database.py b/app/database.py
new file mode 100644
index 0000000..24e1930
--- /dev/null
+++ b/app/database.py
@@ -0,0 +1,25 @@
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
+from sqlalchemy.orm import relationship, backref
+from . import db
+from flask_security.core import RoleMixin, UserMixin
+
+class User(db.Model, UserMixin):
+ id = Column(String, primary_key=True)
+ email = Column(String, unique=True, nullable=False)
+ password = Column(String, nullable=False)
+ pref_name = Column(String, nullable=False)
+ last_login = Column(DateTime, nullable=False)
+ fs_uniquifier = Column(String, unique=True, nullable=False)
+ active = Column(Boolean, nullable=False)
+ roles = relationship('Role', secondary='roles_users',backref=backref('users', lazy='dynamic'))
+
+class Role(db.Model, RoleMixin):
+ id = Column(String, primary_key=True)
+ name = Column(String, unique=True)
+ description = Column(String)
+
+class RolesUsers(db.Model):
+ __tablename__ = "roles_users"
+ id = Column(Integer(), primary_key=True)
+ user_id = Column('user_id', String(), ForeignKey('user.id'))
+ role_id = Column('role_id', String(), ForeignKey('role.id'))
diff --git a/app/static/gen/style.css b/app/static/gen/style.css
new file mode 100644
index 0000000..6dd2994
--- /dev/null
+++ b/app/static/gen/style.css
@@ -0,0 +1,46 @@
+body {
+ background: #282828;
+ color: #ebdbb2;
+ font-family: monospace;
+}
+
+a a:active, a:visited {
+ color: #458588;
+}
+
+.container {
+ margin: auto;
+ width: 60%;
+}
+
+button {
+ border-radius: 8px;
+ background-color: #458588;
+ border-color: #458588;
+ border: none;
+ margin: 0.5rem;
+}
+
+button.accent {
+ background-color: #d79921;
+ border-color: #d79921;
+}
+
+form {
+ width: 40%;
+}
+
+label,
+input {
+ display: inline-block;
+}
+
+label {
+ width: 40%;
+ text-align: right;
+}
+
+label + input {
+ width: 40%;
+ margin: 0 30% 0 4%;
+}
diff --git a/scss/style.scss b/app/static/scss/style.scss
index ede6466..d7f50f7 100644
--- a/scss/style.scss
+++ b/app/static/scss/style.scss
@@ -49,7 +49,7 @@ label {
}
label+input {
- width: 40%
+ width: 40%;
margin: 0 30% 0 4%;
}
diff --git a/migrations-old/README b/migrations-old/README
new file mode 100644
index 0000000..0e04844
--- /dev/null
+++ b/migrations-old/README
@@ -0,0 +1 @@
+Single-database configuration for Flask.
diff --git a/migrations-old/alembic.ini b/migrations-old/alembic.ini
new file mode 100644
index 0000000..ec9d45c
--- /dev/null
+++ b/migrations-old/alembic.ini
@@ -0,0 +1,50 @@
+# A generic, single database configuration.
+
+[alembic]
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic,flask_migrate
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[logger_flask_migrate]
+level = INFO
+handlers =
+qualname = flask_migrate
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/migrations-old/env.py b/migrations-old/env.py
new file mode 100644
index 0000000..68feded
--- /dev/null
+++ b/migrations-old/env.py
@@ -0,0 +1,91 @@
+from __future__ import with_statement
+
+import logging
+from logging.config import fileConfig
+
+from flask import current_app
+
+from alembic import context
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+logger = logging.getLogger('alembic.env')
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+config.set_main_option(
+ 'sqlalchemy.url',
+ str(current_app.extensions['migrate'].db.get_engine().url).replace(
+ '%', '%%'))
+target_metadata = current_app.extensions['migrate'].db.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(
+ url=url, target_metadata=target_metadata, literal_binds=True
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+
+ # this callback is used to prevent an auto-migration from being generated
+ # when there are no changes to the schema
+ # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
+ def process_revision_directives(context, revision, directives):
+ if getattr(config.cmd_opts, 'autogenerate', False):
+ script = directives[0]
+ if script.upgrade_ops.is_empty():
+ directives[:] = []
+ logger.info('No changes in schema detected.')
+
+ connectable = current_app.extensions['migrate'].db.get_engine()
+
+ with connectable.connect() as connection:
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ process_revision_directives=process_revision_directives,
+ **current_app.extensions['migrate'].configure_args
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/migrations-old/script.py.mako b/migrations-old/script.py.mako
new file mode 100644
index 0000000..2c01563
--- /dev/null
+++ b/migrations-old/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade():
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+ ${downgrades if downgrades else "pass"}
diff --git a/migrations/20220719122322_peer.sql b/migrations/20220719122322_peer.sql
deleted file mode 100644
index c06c574..0000000
--- a/migrations/20220719122322_peer.sql
+++ /dev/null
@@ -1,11 +0,0 @@
--- Add migration script here
-CREATE TABLE peers (
- id TEXT PRIMARY KEY,
- addr CIDR NOT NULL,
- public_key TEXT NOT NULL
-);
-CREATE TABLE networks (
- id TEXT PRIMARY KEY,
- subnet CIDR NOT NULL,
- description TEXT
-)
diff --git a/migrations/20220719152547_users.sql b/migrations/20220719152547_users.sql
deleted file mode 100644
index 4407bcc..0000000
--- a/migrations/20220719152547_users.sql
+++ /dev/null
@@ -1,8 +0,0 @@
--- Add migration script here
-CREATE TABLE users (
- id TEXT PRIMARY KEY,
- email TEXT NOT NULL,
- pref_name TEXT NOT NULL,
- pw_hash TEXT NOT NULL,
- last_login TIMESTAMP
-);
diff --git a/migrations/README b/migrations/README
new file mode 100644
index 0000000..0e04844
--- /dev/null
+++ b/migrations/README
@@ -0,0 +1 @@
+Single-database configuration for Flask.
diff --git a/migrations/alembic.ini b/migrations/alembic.ini
new file mode 100644
index 0000000..ec9d45c
--- /dev/null
+++ b/migrations/alembic.ini
@@ -0,0 +1,50 @@
+# A generic, single database configuration.
+
+[alembic]
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic,flask_migrate
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[logger_flask_migrate]
+level = INFO
+handlers =
+qualname = flask_migrate
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/migrations/env.py b/migrations/env.py
new file mode 100644
index 0000000..68feded
--- /dev/null
+++ b/migrations/env.py
@@ -0,0 +1,91 @@
+from __future__ import with_statement
+
+import logging
+from logging.config import fileConfig
+
+from flask import current_app
+
+from alembic import context
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+logger = logging.getLogger('alembic.env')
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+config.set_main_option(
+ 'sqlalchemy.url',
+ str(current_app.extensions['migrate'].db.get_engine().url).replace(
+ '%', '%%'))
+target_metadata = current_app.extensions['migrate'].db.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline():
+ """Run migrations in 'offline' mode.
+
+ This configures the context with just a URL
+ and not an Engine, though an Engine is acceptable
+ here as well. By skipping the Engine creation
+ we don't even need a DBAPI to be available.
+
+ Calls to context.execute() here emit the given string to the
+ script output.
+
+ """
+ url = config.get_main_option("sqlalchemy.url")
+ context.configure(
+ url=url, target_metadata=target_metadata, literal_binds=True
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+def run_migrations_online():
+ """Run migrations in 'online' mode.
+
+ In this scenario we need to create an Engine
+ and associate a connection with the context.
+
+ """
+
+ # this callback is used to prevent an auto-migration from being generated
+ # when there are no changes to the schema
+ # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
+ def process_revision_directives(context, revision, directives):
+ if getattr(config.cmd_opts, 'autogenerate', False):
+ script = directives[0]
+ if script.upgrade_ops.is_empty():
+ directives[:] = []
+ logger.info('No changes in schema detected.')
+
+ connectable = current_app.extensions['migrate'].db.get_engine()
+
+ with connectable.connect() as connection:
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ process_revision_directives=process_revision_directives,
+ **current_app.extensions['migrate'].configure_args
+ )
+
+ with context.begin_transaction():
+ context.run_migrations()
+
+
+if context.is_offline_mode():
+ run_migrations_offline()
+else:
+ run_migrations_online()
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
new file mode 100644
index 0000000..2c01563
--- /dev/null
+++ b/migrations/script.py.mako
@@ -0,0 +1,24 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
+
+
+def upgrade():
+ ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+ ${downgrades if downgrades else "pass"}
diff --git a/migrations/versions/81173c1d4f0a_initial_migration.py b/migrations/versions/81173c1d4f0a_initial_migration.py
new file mode 100644
index 0000000..f96ff21
--- /dev/null
+++ b/migrations/versions/81173c1d4f0a_initial_migration.py
@@ -0,0 +1,90 @@
+"""Initial migration
+
+Revision ID: 81173c1d4f0a
+Revises:
+Create Date: 2022-07-25 13:13:29.135474
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '81173c1d4f0a'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('role',
+ sa.Column('id', sa.String(), nullable=False),
+ sa.Column('name', sa.String(), nullable=True),
+ sa.Column('description', sa.String(), nullable=True),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('name')
+ )
+ op.create_table('user',
+ sa.Column('id', sa.String(), nullable=False),
+ sa.Column('email', sa.String(), nullable=False),
+ sa.Column('password', sa.String(), nullable=False),
+ sa.Column('pref_name', sa.String(), nullable=False),
+ sa.Column('last_login', sa.DateTime(), nullable=False),
+ sa.Column('fs_uniquifier', sa.String(), nullable=False),
+ sa.Column('active', sa.Boolean(), nullable=False),
+ sa.PrimaryKeyConstraint('id'),
+ sa.UniqueConstraint('email'),
+ sa.UniqueConstraint('fs_uniquifier')
+ )
+ op.drop_table('enroll_requests')
+ op.drop_table('_sqlx_migrations')
+ op.drop_table('networks')
+ op.drop_table('peers')
+ op.drop_table('users')
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('users',
+ sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('email', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('pref_name', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('pw_hash', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('last_login', postgresql.TIMESTAMP(), autoincrement=False, nullable=True),
+ sa.PrimaryKeyConstraint('id', name='users_pkey'),
+ postgresql_ignore_search_path=False
+ )
+ op.create_table('peers',
+ sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('addr', postgresql.CIDR(), autoincrement=False, nullable=False),
+ sa.Column('public_key', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('owner', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.ForeignKeyConstraint(['owner'], ['users.id'], name='peers_owner_fkey'),
+ sa.PrimaryKeyConstraint('id', name='peers_pkey')
+ )
+ op.create_table('networks',
+ sa.Column('id', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('subnet', postgresql.CIDR(), autoincrement=False, nullable=False),
+ sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True),
+ sa.PrimaryKeyConstraint('id', name='networks_pkey')
+ )
+ op.create_table('_sqlx_migrations',
+ sa.Column('version', sa.BIGINT(), autoincrement=False, nullable=False),
+ sa.Column('description', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('installed_on', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=False),
+ sa.Column('success', sa.BOOLEAN(), autoincrement=False, nullable=False),
+ sa.Column('checksum', postgresql.BYTEA(), autoincrement=False, nullable=False),
+ sa.Column('execution_time', sa.BIGINT(), autoincrement=False, nullable=False),
+ sa.PrimaryKeyConstraint('version', name='_sqlx_migrations_pkey')
+ )
+ op.create_table('enroll_requests',
+ sa.Column('token', sa.TEXT(), autoincrement=False, nullable=False),
+ sa.Column('expires', postgresql.TIMESTAMP(), server_default=sa.text("(CURRENT_TIMESTAMP + ((30)::double precision * '00:01:00'::interval))"), autoincrement=False, nullable=False),
+ sa.Column('confirmed', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False),
+ sa.PrimaryKeyConstraint('token', name='enroll_requests_pkey')
+ )
+ op.drop_table('user')
+ op.drop_table('role')
+ # ### end Alembic commands ###
diff --git a/migrations/versions/946e687d2d42_roles_users.py b/migrations/versions/946e687d2d42_roles_users.py
new file mode 100644
index 0000000..08ebbe2
--- /dev/null
+++ b/migrations/versions/946e687d2d42_roles_users.py
@@ -0,0 +1,35 @@
+"""roles_users
+
+Revision ID: 946e687d2d42
+Revises: 81173c1d4f0a
+Create Date: 2022-07-25 14:30:56.122119
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '946e687d2d42'
+down_revision = '81173c1d4f0a'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('roles_users',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('user_id', sa.String(), nullable=True),
+ sa.Column('role_id', sa.String(), nullable=True),
+ sa.ForeignKeyConstraint(['role_id'], ['role.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('id')
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_table('roles_users')
+ # ### end Alembic commands ###
diff --git a/src/build.rs b/src/build.rs
deleted file mode 100644
index d7d6882..0000000
--- a/src/build.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use ructe::{Ructe, RucteError};
-
-fn main() -> Result<(), RucteError> {
- let mut ructe = Ructe::from_env()?;
- let mut statics = ructe.statics()?;
- statics.add_files("statics")?;
- statics.add_sass_file("scss/style.scss")?;
-
- ructe.compile_templates("templates")
-}
diff --git a/src/config.rs b/src/config.rs
deleted file mode 100644
index c153085..0000000
--- a/src/config.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use serde::Deserialize;
-use tracing::error;
-
-use crate::errors::ServiceError;
-use std::fs;
-use std::str;
-
-#[derive(Deserialize)]
-pub struct Config {
- pub server: ServerConfig,
- pub database: DbConfig,
- pub email: EmailConfig,
-}
-
-#[derive(Deserialize)]
-pub struct ServerConfig {
- pub bind_addr: String,
- pub admin_email: String
-}
-
-#[derive(Deserialize, Clone)]
-pub struct DbConfig {
- pub postgres_url: String,
- pub max_connections: u32,
-}
-
-#[derive(Deserialize)]
-pub struct EmailConfig {
- pub smtp_server: String,
- pub smtp_port: u16,
- pub smtp_tls: bool,
- pub smtp_starttls: bool,
- pub smtp_username: Option<String>,
- pub smtp_password: Option<String>,
- pub email_from: String,
- pub email_helo: String,
-}
-
-impl Config {
- pub fn init(path: String) -> Result<Config, ServiceError> {
- if let Ok(c) = fs::read(path) {
- if c.len() == 0 {
- error!("Config file empty.");
- return Err(ServiceError::MissingConfig);
- } else {
- let config: Config = toml::from_str(str::from_utf8(&c).unwrap())?;
- return Ok(config);
- }
- } else {
- error!("Unable to read from config file.");
- return Err(ServiceError::MissingConfig);
- }
- }
-}
diff --git a/src/errors.rs b/src/errors.rs
deleted file mode 100644
index dcc0ae8..0000000
--- a/src/errors.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use axum::body;
-use axum::response::{IntoResponse, Response, Html};
-use hyper::StatusCode;
-use thiserror::Error;
-
-#[derive(Error, Debug)]
-pub enum ServiceError {
- #[error("No config file")]
- MissingConfig,
- #[error("Invalid config file: {0}")]
- InvalidConfig(#[from] toml::de::Error),
- #[error("Not Authorized")]
- NotAuthorized,
- #[error("Not Found")]
- NotFound,
- #[error("Axum: {0}")]
- Axum(#[from] axum::Error),
- #[error("SQL: {0}")]
- Sql(#[from] sqlx::Error),
- #[error("Bcrypt: {0}")]
- Bcrypt(#[from] bcrypt::BcryptError),
- #[error("Parse Error: {0}")]
- Parse(String),
- #[error("Email: {0}")]
- Email(String),
-}
-
-pub type StringResult<T = String> = Result<T, ServiceError>;
-pub type HtmlResult<T = Html<String>> = Result<T, ServiceError>;
-pub type JsonResult<T> = Result<T, ServiceError>;
-
-impl IntoResponse for ServiceError {
- fn into_response(self) -> axum::response::Response {
- let body = body::boxed(body::Full::from(self.to_string()));
-
- let status = match self {
- ServiceError::NotFound => StatusCode::NOT_FOUND,
- ServiceError::NotAuthorized => StatusCode::UNAUTHORIZED,
- _ => StatusCode::INTERNAL_SERVER_ERROR,
- };
- Response::builder().status(status).body(body).unwrap()
- }
-}
-
-impl From<ipnetwork::IpNetworkError> for ServiceError {
- fn from(e: ipnetwork::IpNetworkError) -> Self {
- Self::Parse(e.to_string())
- }
-}
diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs
deleted file mode 100644
index ab72bc8..0000000
--- a/src/handlers/auth.rs
+++ /dev/null
@@ -1,117 +0,0 @@
-use std::sync::Arc;
-
-use axum::{response::{IntoResponse, Html, Redirect}, Form, Extension};
-use axum_extra::extract::{PrivateCookieJar, cookie::Cookie};
-use serde::Deserialize;
-use sqlx::{query, query_as, pool::PoolConnection, Postgres};
-use tracing::{debug, instrument};
-use crate::{errors::ServiceError, State, models::DbUser};
-use chrono::prelude::*;
-
-#[derive(Deserialize, Debug)]
-pub struct RegisterForm {
- pub email: String,
- pub prefname: String,
- pub password: String,
- #[serde(rename="password-confirm")]
- pub password_confirm: String
-}
-
-#[derive(Deserialize)]
-pub struct LoginForm {
- pub email: String,
- pub password: String,
-}
-
-pub async fn login() -> impl IntoResponse {
- let mut buf = Vec::new();
- crate::templates::login_html(&mut buf).unwrap();
-
- Html(buf)
-}
-
-pub async fn login_post(Form(login): Form<LoginForm>, state: Extension<Arc<State>>, jar: PrivateCookieJar) -> Result<(PrivateCookieJar, Redirect), ServiceError> {
- let mut conn = state.conn.acquire().await?;
-
- let user: DbUser = query_as("SELECT * FROM users WHERE email=$1").bind(login.email)
- .fetch_one(&mut conn)
- .await?;
-
- if bcrypt::verify(login.password, &user.pw_hash)? {
- debug!("Logged in ID {} (email {})", user.id, user.email);
- query("UPDATE users SET last_login=$1 WHERE id=$2").bind(Utc::now()).bind(user.id.clone())
- .execute(&mut conn)
- .await?;
-
- let updated_jar = jar.add(Cookie::build("user-id", user.id.clone())
- .path("/")
- .finish());
-
- Ok((updated_jar, Redirect::to("/")))
- } else {
- let updated_jar = jar;
- Ok((updated_jar, Redirect::to("/dash/auth/login")))
- }
-}
-
-pub async fn register() -> impl IntoResponse {
- let mut buf = Vec::new();
- crate::templates::register_html(&mut buf).unwrap();
-
- Html(buf)
-}
-
-pub async fn register_post(Form(reg): Form<RegisterForm>, state: Extension<Arc<State>>) -> impl IntoResponse {
- if reg.password_confirm == reg.password {
- let hash = match bcrypt::hash(reg.password, bcrypt::DEFAULT_COST) {
- Ok(h) => h,
- Err(e) => {
- return Err(ServiceError::NotAuthorized);
- }
- };
-
- let mut conn = state.conn.acquire().await?;
- let ulid = ulid::Ulid::new();
-
- query("INSERT INTO users (id, email, pref_name, pw_hash, last_login) VALUES ($1, $2, $3, $4, $5)").bind(ulid.to_string()).bind(reg.email.clone()).bind(reg.prefname).bind(hash).bind(Utc::now())
- .execute(&mut conn)
- .await?;
-
- }
-
- Ok(Redirect::to("/dash/auth/login"))
-}
-
-pub async fn logout_post(jar: PrivateCookieJar) -> Result<(PrivateCookieJar, Redirect), ServiceError> {
- if let Some(id) = jar.get("user-id") {
- debug!("Found user {}", id);
-
- let updated_jar = jar.remove(id);
-
- Ok((updated_jar, Redirect::to("/dash/auth/login")))
- } else {
- Ok((jar, Redirect::to("/")))
- }
-}
-
-#[instrument]
-pub async fn get_user_or_403(jar: PrivateCookieJar, conn: &mut PoolConnection<Postgres>) -> Result<DbUser, ServiceError> {
- debug!("Starting middleware get_user_or_403");
- debug!("Displaying all cookies");
- for c in jar.iter() {
- debug!("{}={}", c.name(), c.value());
- }
- if let Some(id) = jar.get("user-id") {
- debug!("Found user {}", id);
-
- let user: DbUser = query_as("SELECT * FROM users WHERE id=$1").bind(id.value())
- .fetch_one(conn)
- .await?;
-
- Ok(user)
-
- } else {
- debug!("No user found");
- Err(ServiceError::NotAuthorized)
- }
-}
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
deleted file mode 100644
index 24db540..0000000
--- a/src/handlers/mod.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use axum::{Router, routing::{get, post}};
-
-pub mod auth;
-mod nets;
-
-pub async fn gen_routers() -> Router {
-
- Router::new()
- .nest("/auth", auth_routes().await)
- .nest("/nets", net_routes().await)
-}
-
-async fn auth_routes() -> Router {
-
- Router::new()
- .route("/login", get(auth::login).post(auth::login_post))
- .route("/register", get(auth::register).post(auth::register_post))
- .route("/logout", post(auth::logout_post))
-}
-
-async fn net_routes() -> Router {
- Router::new()
- .route("/new", get(nets::new).post(nets::new_post))
-}
diff --git a/src/handlers/nets.rs b/src/handlers/nets.rs
deleted file mode 100644
index 6010787..0000000
--- a/src/handlers/nets.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use std::sync::Arc;
-
-use axum::{Extension, Form};
-use axum::response::{Html, IntoResponse, Redirect};
-use axum_extra::extract::PrivateCookieJar;
-use sqlx::query;
-use sqlx::types::ipnetwork::IpNetwork;
-use serde::Deserialize;
-
-use crate::State;
-
-use crate::errors::{HtmlResult, ServiceError};
-
-use super::auth::get_user_or_403;
-
-#[derive(Deserialize)]
-pub struct NewNetForm {
- pub subnet: String,
- pub description: String,
-}
-
-pub async fn new(jar: PrivateCookieJar, state: Extension<Arc<State>>) -> Result<Html<Vec<u8>>, ServiceError> {
- let mut conn = state.conn.acquire().await?;
-
- let _ = get_user_or_403(jar, &mut conn).await?;
-
- let mut buf = Vec::new();
- crate::templates::new_net_html(&mut buf).unwrap();
-
- Ok(Html(buf))
-}
-
-pub async fn new_post(Form(new): Form<NewNetForm>, jar: PrivateCookieJar, state: Extension<Arc<State>>) -> Result<Redirect, ServiceError> {
- let mut conn = state.conn.acquire().await?;
-
- let _ = get_user_or_403(jar, &mut conn).await?;
-
- let id = ulid::Ulid::new();
-
- let cidr: IpNetwork = match new.subnet.parse() {
- Ok(c) => c,
- Err(e) => {
- return Err(ServiceError::Parse(e.to_string()));
- }
- };
-
- query("INSERT INTO networks (subnet, description, id) VALUES ($1, $2, $3)").bind(cidr).bind(new.description).bind(id.to_string()).execute(&mut conn).await?;
-
- Ok(Redirect::to("/"))
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 6ebe145..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-mod config;
-mod errors;
-mod handlers;
-mod models;
-mod utils;
-
-use std::{net::SocketAddr, str::FromStr, sync::Arc, time::Duration};
-
-use axum::body;
-use axum::extract::Path;
-use axum::{error_handling::HandleErrorLayer, routing::get, BoxError, Extension, Router};
-use axum::response::{Html, IntoResponse, Response};
-use axum_extra::extract::{PrivateCookieJar, SignedCookieJar};
-use axum_extra::extract::cookie::Key;
-use errors::{StringResult, HtmlResult};
-use hyper::StatusCode;
-use models::{Peer, Network};
-use sqlx::query_as;
-use sqlx::{PgPool, postgres::PgPoolOptions};
-use tower::ServiceBuilder;
-use tower_http::trace::TraceLayer;
-use tracing::{error, info, debug, trace};
-use crate::errors::ServiceError;
-use tracing_subscriber::prelude::*;
-use crate::models::DbUser;
-use crate::handlers::auth::get_user_or_403;
-
-pub struct State {
- pub config: config::Config,
- pub conn: PgPool
-}
-
-#[tokio::main]
-async fn main() {
- color_eyre::install().unwrap();
- tracing_subscriber::registry()
- .with(tracing_subscriber::EnvFilter::new("debug"))
- .with(tracing_subscriber::fmt::layer())
- .init();
- let config = match config::Config::init("/etc/nccd/config.toml".to_owned()) {
- Ok(c) => c,
- Err(e) => {
- error!("Config Error: {:?}", e);
- std::process::exit(1);
- }
- };
-
- let bind_addr = config.server.bind_addr.clone();
-
- let db_config = config.database.clone();
-
- let conn = PgPoolOptions::new()
- .max_connections(db_config.max_connections)
- .connect(&db_config.postgres_url)
- .await.unwrap();
-
- let shared_state = Arc::new(State { config, conn });
-
- let key = load_or_gen_keypair().unwrap();
-
- let app = Router::new()
- .route("/health", get(health_check))
- .route("/", get(index))
- .nest("/dash", handlers::gen_routers().await)
- .route("/static/:name", get(statics))
- .layer(
- ServiceBuilder::new()
- .layer(HandleErrorLayer::new(|error: BoxError| async move {
- if error.is::<tower::timeout::error::Elapsed>() {
- Ok(StatusCode::REQUEST_TIMEOUT)
- } else {
- Err((
- StatusCode::INTERNAL_SERVER_ERROR,
- format!("Unhandled internal error: {}", error),
- ))
- }
- }))
- .timeout(Duration::from_secs(10))
- .layer(TraceLayer::new_for_http())
- .into_inner(),
- )
- .layer(Extension(shared_state))
- .layer(Extension(key));
-
- let addr = match SocketAddr::from_str(&bind_addr) {
- Ok(a) => a,
- Err(e) => {
- error!("Invalid bind addr: {:?}", e);
- std::process::exit(1);
- }
- };
-
- info!("Listening on {}", addr);
-
- axum::Server::bind(&addr)
- .serve(app.into_make_service())
- .await
- .unwrap();
-}
-
-async fn health_check() -> &'static str {
- "OK"
-}
-
-#[axum_macros::debug_handler]
-async fn index(state: Extension<Arc<State>>, jar: PrivateCookieJar) -> HtmlResult {
- let mut conn = state.conn.acquire().await?;
- let user: Option<DbUser> = match get_user_or_403(jar, &mut conn).await {
- Ok(u) => Some(u),
- Err(_) => None,
- };
-
- let peers: Vec<Peer> = query_as("SELECT * FROM peers").fetch_all(&mut conn).await?;
- let nets: Vec<Network> = query_as("SELECT * FROM networks").fetch_all(&mut conn).await?;
-
- let mut buf = Vec::new();
- crate::templates::index_html(&mut buf, user, peers, nets).unwrap();
-
- match String::from_utf8(buf) {
- Ok(s) => Ok(Html(s)),
- Err(_) => Err(ServiceError::NotFound),
- }
-}
-
-async fn test_email() -> Result<(), ServiceError> {
- utils::send_email("csalter@carathe.dev".to_string(), "Test Email".to_string(), "Hi, test".to_string()).await?;
-
- Ok(())
-}
-
-async fn statics(Path(name): Path<String>) -> Result<Response, ServiceError> {
- for s in templates::statics::STATICS {
- trace!("Name: {}\nContents:\n{:?}\n\n", s.name, s.content);
- }
-
- match templates::statics::StaticFile::get(&name) {
- Some(s) => match String::from_utf8(s.content.to_vec()) {
- Ok(c) => {
- let body = body::boxed(body::Full::from(c));
-
- Ok(Response::builder()
- .header("Content-Type", "text/css")
- .status(StatusCode::OK)
- .body(body).unwrap())
- },
- Err(_) => Err(ServiceError::NotFound),
- },
- None => Err(ServiceError::NotFound),
- }
-}
-
-use std::fs::{self, File};
-fn load_or_gen_keypair() -> Result<Key, ServiceError> {
- let kp: Key;
- let mut file = match File::open(".keypair") {
- Ok(f) => f,
- Err(_) => {
- debug!("File does not exist, creating at .keypair");
- File::create(".keypair").unwrap()
- }
- };
- if let Ok(c) = fs::read(".keypair") {
- if c.len() == 0 {
- debug!("No keypair found. Generating...");
- let key = Key::generate();
- fs::write(".keypair", key.master().as_ref()).unwrap();
- debug!("Written keypair {:?} to .keypair", key.master().as_ref());
- kp = key;
- } else {
- debug!("Found keypair file, contents: {:?}", c);
- kp = Key::from(&c);
- debug!("Loaded keypair from file");
- }
- } else {
- debug!("Generating new keypair");
- let key = Key::generate();
- fs::write(".keypair", key.master().as_ref()).unwrap();
- debug!("Written keypair {:?} to .keypair", key.master().as_ref());
- kp = key;
- }
- Ok(kp)
-}
-
-
-include!(concat!(env!("OUT_DIR"), "/templates.rs"));
diff --git a/src/models.rs b/src/models.rs
deleted file mode 100644
index 2295154..0000000
--- a/src/models.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use sqlx::{FromRow, types::ipnetwork::IpNetwork};
-
-#[derive(Debug, FromRow, Clone)]
-pub struct DbUser {
- pub id: String,
- pub email: String,
- pub pref_name: String,
- pub pw_hash: String,
- pub last_login: chrono::NaiveDateTime
-}
-
-#[derive(Debug, FromRow, Clone)]
-pub struct Peer {
- pub id: String,
- pub addr: IpNetwork,
- pub public_key: String,
-}
-
-#[derive(Debug, FromRow, Clone)]
-pub struct Network {
- pub id: String,
- pub subnet: IpNetwork,
- pub description: Option<String>
-}
diff --git a/src/utils.rs b/src/utils.rs
deleted file mode 100644
index e723db8..0000000
--- a/src/utils.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use lettre::{Message, transport::smtp::{authentication::Credentials, extension::ClientId}, transport::smtp::AsyncSmtpTransport, AsyncTransport, Tokio1Executor};
-
-use crate::{errors::ServiceError, config};
-
-pub async fn send_email(to: String, subject: String, content: String) -> Result<(), ServiceError> {
- let config = config::Config::init("/etc/nccd/config.toml".to_string()).unwrap();
- let email = if let Ok(e) = Message::builder()
- .from(config.email.email_from.parse().unwrap())
- .to(to.parse().unwrap())
- .subject(subject)
- .body(content) {
- e
- } else {
- return Err(ServiceError::Email("Invalid email content".to_string()));
- };
- let mailer: AsyncSmtpTransport<Tokio1Executor>;
- let helo = ClientId::Domain(config.email.email_helo);
- if let Some(u) = config.email.smtp_username {
- let creds = Credentials::new(u, config.email.smtp_password.unwrap());
- if config.email.smtp_starttls {
- mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&config.email.smtp_server)
- .unwrap()
- .credentials(creds)
- .hello_name(helo)
- .build();
- } else {
- mailer = AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&config.email.smtp_server)
- .credentials(creds)
- .hello_name(helo)
- .build();
- }
- } else {
- if config.email.smtp_tls && config.email.smtp_starttls {
- mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&config.email.smtp_server).unwrap().hello_name(helo)
- .build();
- } else if config.email.smtp_tls {
- mailer = AsyncSmtpTransport::<Tokio1Executor>::relay(&config.email.smtp_server).unwrap().hello_name(helo).build();
- } else {
- mailer = AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&config.email.smtp_server).hello_name(helo).build();
- }
- }
-
- if let Err(e) = mailer.test_connection().await {
- return Err(ServiceError::Email(e.to_string()));
- } else {
- if let Err(e) = mailer.send(email).await {
- return Err(ServiceError::Email(e.to_string()));
- }
- }
- Ok(())
-}
diff --git a/templates/footer.rs.html b/templates/footer.rs.html
deleted file mode 100644
index 41c4d4e..0000000
--- a/templates/footer.rs.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
-@()
-
-
- </div>
- </body>
-</html>
diff --git a/templates/header.rs.html b/templates/header.rs.html
deleted file mode 100644
index 9384bcb..0000000
--- a/templates/header.rs.html
+++ /dev/null
@@ -1,14 +0,0 @@
-@use super::statics::style_css;
-@()
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <title>NCCd Dashboard</title>
- <link rel="stylesheet" href="/static/@style_css.name"/>
-
- <meta name="viewport" content="width=device-width,initial-scale=1.0">
- </head>
-
- <body>
-
- <div class="container">
diff --git a/templates/index.rs.html b/templates/index.rs.html
deleted file mode 100644
index e686b01..0000000
--- a/templates/index.rs.html
+++ /dev/null
@@ -1,71 +0,0 @@
-@use super::{header_html, footer_html};
-@use crate::models::{DbUser, Peer, Network};
-
-@(user: Option<DbUser>, peers: Vec<Peer>, nets: Vec<Network>)
-@:header_html()
- <h1>NCCd (Network Communications Control Daemon)</h1>
-
- @if user.is_some() {
- <h3>Welcome @user.clone().unwrap().pref_name</h3>
- <table>
- <tr>
- <th>Key</th>
- <th>Value</th>
- </tr>
- <tr>
- <td>ID</td>
- <td>@user.clone().unwrap().id</td>
- </tr>
- <tr>
- <td>Email</td>
- <td>@user.clone().unwrap().email</td>
- </tr>
- <tr>
- <td>Preferred Name</td>
- <td>@user.clone().unwrap().pref_name</td>
- </tr>
- <tr>
- <td>Last Login</td>
- <td>@(user.clone().unwrap().last_login)Z</td>
- </tr>
- </table>
-
- <hr>
-
- <h3>Configured Peers</h3>
- <table>
- <tr>
- <th>ID</th>
- <th>Address</th>
- <th>Public Key</th>
- </tr>
- @for p in peers {
- <tr>
- <td>@p.id</td>
- <td>@p.addr</td>
- <td>@p.public_key</td>
- </tr>
- }
- </table>
- <hr>
- <h3>Configured subnets (<a href="/dash/nets/new">New</a>)</h3>
- <table>
- <tr>
- <th>ID</th>
- <th>Subnet CIDR</th>
- <th>Description</th>
- </tr>
- @for n in nets {
- <tr>
- <td>@n.id</td>
- <td>@n.subnet</td>
- <td>@if n.description.is_some() { @n.description.unwrap()
- } else { None }</td>
- </tr>
- }
- </table>
- } else {
-
- <h3>Please <a href="/dash/auth/login">Log in</a></h3>
- }
-@:footer_html()
diff --git a/templates/login.rs.html b/templates/login.rs.html
deleted file mode 100644
index 122e2b8..0000000
--- a/templates/login.rs.html
+++ /dev/null
@@ -1,22 +0,0 @@
-@use super::{header_html, footer_html};
-
-@()
-
-@:header_html()
-<h1>NCCd Login</h1>
-<form method="POST">
- <div>
- <label for="username">Email</label>
- <input type="text" name="email">
- </div>
- <br/>
- <div>
- <label for="password">Password</label>
- <input type="password" name="password">
- </div>
- <div>
- <button type="submit" class="accent">Submit</button>
-</form>
-
-Or try <a href="/dash/auth/register">creating an account</a>
-@:footer_html()
diff --git a/templates/new_net.rs.html b/templates/new_net.rs.html
deleted file mode 100644
index 4628432..0000000
--- a/templates/new_net.rs.html
+++ /dev/null
@@ -1,21 +0,0 @@
-@use super::{header_html, footer_html};
-
-@()
-
-@:header_html()
-
-<h1>New Subnet</h1>
-
-<form method="POST">
- <div>
- <label for="subnet">Subnet CIDR</label>
- <input type="text" name="subnet" required>
- </div>
- <div>
- <label for="description">Description</label>
- <textarea name="description"></textarea>
- </div>
- <button type="submit" class="accent">Submit</button>
-</form>
-
-@:footer_html()
diff --git a/templates/register.rs.html b/templates/register.rs.html
deleted file mode 100644
index 5c2e3f2..0000000
--- a/templates/register.rs.html
+++ /dev/null
@@ -1,27 +0,0 @@
-@use super::{header_html, footer_html};
-
-@()
-
-@:header_html()
-<h1>Create your NCCd Account</h1>
-<form method="POST" action="/dash/auth/register">
- <div>
- <label for="email">Email</label>
- <input type="text" id="email" name="email">
- </div>
- <div>
- <label for="prefname">Preferred name</label>
- <input type="text" id="prefname" name="prefname">
- </div>
- <div>
- <label for="password">Password</label>
- <input type="password" id="password" name="password">
- </div>
- <div>
- <label for="password-confirm">Confirm password</label>
- <input type="password" id="password-confirm" name="password-confirm">
- </div>
- <input type="submit" class="accent">Submit</input>
-</form>
-Or try <a href="/dash/auth/login">logging in</a>
-@:footer_html()