diff --git a/.gitignore b/.gitignore index 4fb9e1a..1662ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ __pycache__/ .pytest_cache .coverage + +/site +src/materia/docs diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..908cfcc --- /dev/null +++ b/docs/api.md @@ -0,0 +1,52 @@ +--- +hide: + - navigation + - toc +--- + + + + + diff --git a/docs/img/favicon.png b/docs/img/favicon.png new file mode 100644 index 0000000..d403418 Binary files /dev/null and b/docs/img/favicon.png differ diff --git a/docs/img/logo-full.png b/docs/img/logo-full.png new file mode 100644 index 0000000..fac4975 Binary files /dev/null and b/docs/img/logo-full.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a5fe87f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,12 @@ +# Materia + + + +

+ Materia +

+

+ Materia is easy and fast cloud storage +

diff --git a/docs/reference/app.md b/docs/reference/app.md new file mode 100644 index 0000000..b3d34d6 --- /dev/null +++ b/docs/reference/app.md @@ -0,0 +1 @@ +::: materia.app diff --git a/docs/reference/core.md b/docs/reference/core.md new file mode 100644 index 0000000..60a2cbb --- /dev/null +++ b/docs/reference/core.md @@ -0,0 +1 @@ +::: materia.core diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000..175fcef --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,5 @@ +# Reference + +Here's the reference or code API, the classes, functions, parameters, attributes, and +all the Materia parts. + diff --git a/docs/reference/models.md b/docs/reference/models.md new file mode 100644 index 0000000..ff7eef3 --- /dev/null +++ b/docs/reference/models.md @@ -0,0 +1 @@ +::: materia.models diff --git a/docs/reference/routers.md b/docs/reference/routers.md new file mode 100644 index 0000000..f59bbf8 --- /dev/null +++ b/docs/reference/routers.md @@ -0,0 +1 @@ +::: materia.routers diff --git a/docs/reference/security.md b/docs/reference/security.md new file mode 100644 index 0000000..a5dad5d --- /dev/null +++ b/docs/reference/security.md @@ -0,0 +1 @@ +::: materia.security diff --git a/docs/reference/tasks.md b/docs/reference/tasks.md new file mode 100644 index 0000000..db79d29 --- /dev/null +++ b/docs/reference/tasks.md @@ -0,0 +1 @@ +::: materia.tasks diff --git a/flake.nix b/flake.nix index 2fc5caa..7feca98 100644 --- a/flake.nix +++ b/flake.nix @@ -164,6 +164,49 @@ postgresql-devel = bonfire.packages.x86_64-linux.postgresql; redis-devel = bonfire.packages.x86_64-linux.redis; + + materia-devel = let + user = "materia"; + dataDir = "/var/lib/materia"; + entryPoint = pkgs.writeTextDir "entrypoint.sh" '' + materia start + ''; + in + pkgs.dockerTools.buildImage { + name = "materia"; + tag = "latest"; + + copyToRoot = pkgs.buildEnv { + name = "image-root"; + pathsToLink = ["/bin" "/etc" "/"]; + paths = with pkgs; [ + bash + self.packages.x86_64-linux.materia + entryPoint + ]; + }; + runAsRoot = with pkgs; '' + #!${runtimeShell} + ${dockerTools.shadowSetup} + groupadd -r ${user} + useradd -r -g ${user} --home-dir=${dataDir} ${user} + mkdir -p ${dataDir} + chown -R ${user}:${user} ${dataDir} + ''; + + config = { + Entrypoint = ["bash" "/entrypoint.sh"]; + StopSignal = "SIGINT"; + User = "${user}:${user}"; + WorkingDir = dataDir; + ExposedPorts = { + "54601/tcp" = {}; + }; + Env = [ + "MATERIA_APPLICATION__WORKING_DIRECTORY=${dataDir}" + ]; + }; + }; }; devShells.x86_64-linux.default = pkgs.mkShell { diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..30db284 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,89 @@ +site_name: Materia Documentation +site_description: Materia cloud storage +#site_url: +repo_name: L-Nafaryus/materia +repo_url: https://vcs.elnafo.ru/L-Nafaryus/materia +copyright: Copyright © 2024 L-Nafaryus + +theme: + name: material + features: + - content.code.annotate + - content.code.copy + # - content.code.select + - content.footnote.tooltips + - content.tabs.link + - content.tooltips + - navigation.footer + - navigation.indexes + - navigation.instant + - navigation.instant.prefetch + # - navigation.instant.preview + - navigation.instant.progress + - navigation.path + - navigation.tabs + - navigation.tabs.sticky + - navigation.top + - navigation.tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + logo: img/favicon.png + favicon: img/favicon.png + palette: + - media: "(prefers-color-scheme: light)" + scheme: slate + primary: deep purple + accent: deep purple + toggle: + icon: material/weather-sunny + name: Switch to light mode + - media: "(prefers-color-scheme: dark)" + scheme: default + primary: deep purple + accent: deep purple + toggle: + icon: material/weather-night + name: Switch to dark mode + + +plugins: +- search: +- mkdocstrings: + handlers: + python: + paths: [src] # search packages in the src folder + options: + extensions: + - griffe_typingdoc + #preload_modules: + #- sqlalchemy + #docstring_style: sphinx + show_submodules: true + show_source: true + show_if_no_docstring: true + show_symbol_type_heading: true + show_symbol_type_toc: true + show_root_heading: true + unwrap_annotated: true + merge_init_into_class: true + docstring_section_style: spacy + signature_crossrefs: true + inherited_members: true + members_order: source + separate_signature: true + filters: + - '!^_' + +nav: + - Materia: index.md + - Reference: + - reference/index.md + - reference/app.md + - reference/core.md + - reference/models.md + - reference/routers.md + - reference/security.md + - reference/tasks.md + - API: api.md diff --git a/pdm.lock b/pdm.lock index d69daa0..f17c69d 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:ba7a816a8bfe503b899a8eba3e5ca58e2d751a278c19b1c1db6e647f83fcd62d" +content_hash = "sha256:764de758c9c4b274659cef493a76abb117253e6ccf30dce9f4e61a74afce2228" [[package]] name = "aiofiles" @@ -157,6 +157,17 @@ files = [ {file = "authlib-1.3.1.tar.gz", hash = "sha256:7ae843f03c06c5c0debd63c9db91f9fda64fa62a42a77419fa15fbb7e7a58917"}, ] +[[package]] +name = "babel" +version = "2.16.0" +requires_python = ">=3.8" +summary = "Internationalization utilities" +groups = ["dev"] +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + [[package]] name = "bcrypt" version = "4.1.2" @@ -264,7 +275,7 @@ name = "certifi" version = "2024.7.4" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, @@ -310,7 +321,7 @@ name = "charset-normalizer" version = "3.3.2" requires_python = ">=3.7.0" summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, @@ -393,7 +404,6 @@ version = "0.4.6" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" summary = "Cross-platform colored terminal text." groups = ["default", "dev"] -marker = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -584,6 +594,19 @@ files = [ {file = "fastapi-0.112.0.tar.gz", hash = "sha256:d262bc56b7d101d1f4e8fc0ad2ac75bb9935fec504d2b7117686cec50710cf05"}, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +summary = "Copy your docs directly to the gh-pages branch." +groups = ["dev"] +dependencies = [ + "python-dateutil>=2.8.1", +] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + [[package]] name = "greenlet" version = "3.0.3" @@ -603,6 +626,35 @@ files = [ {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, ] +[[package]] +name = "griffe" +version = "1.2.0" +requires_python = ">=3.8" +summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +groups = ["dev"] +dependencies = [ + "colorama>=0.4", +] +files = [ + {file = "griffe-1.2.0-py3-none-any.whl", hash = "sha256:a8b2fcb1ecdc5a412e646b0b4375eb20a5d2eac3a11dd8c10c56967a4097663c"}, + {file = "griffe-1.2.0.tar.gz", hash = "sha256:1c9f6ef7455930f3f9b0c4145a961c90385d1e2cbc496f7796fbff560ec60d31"}, +] + +[[package]] +name = "griffe-typingdoc" +version = "0.2.6" +requires_python = ">=3.8" +summary = "Griffe extension for PEP 727 – Documentation Metadata in Typing." +groups = ["dev"] +dependencies = [ + "griffe>=0.49", + "typing-extensions>=4.7", +] +files = [ + {file = "griffe_typingdoc-0.2.6-py3-none-any.whl", hash = "sha256:2726e6cf1e986f42fe9cab4a95cef103a745327f035a32055816849ca47893e4"}, + {file = "griffe_typingdoc-0.2.6.tar.gz", hash = "sha256:852a17c1e2d29bbbf14a287e7cc5982669343cf60a4ea1618e486eb57aba3248"}, +] + [[package]] name = "gunicorn" version = "22.0.0" @@ -726,7 +778,7 @@ name = "idna" version = "3.7" requires_python = ">=3.5" summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, @@ -748,7 +800,7 @@ name = "jinja2" version = "3.1.4" requires_python = ">=3.7" summary = "A very fast and expressive template engine." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "MarkupSafe>=2.0", ] @@ -856,12 +908,23 @@ files = [ {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, ] +[[package]] +name = "markdown" +version = "3.7" +requires_python = ">=3.8" +summary = "Python implementation of John Gruber's Markdown." +groups = ["dev"] +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + [[package]] name = "markupsafe" version = "2.1.5" requires_python = ">=3.7" summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, @@ -888,6 +951,147 @@ dependencies = [ "loguru<1.0.0,>=0.7.2", ] +[[package]] +name = "mergedeep" +version = "1.3.4" +requires_python = ">=3.6" +summary = "A deep merge function for 🐍." +groups = ["dev"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +requires_python = ">=3.8" +summary = "Project documentation with Markdown." +groups = ["dev"] +dependencies = [ + "click>=7.0", + "colorama>=0.4; platform_system == \"Windows\"", + "ghp-import>=1.0", + "jinja2>=2.11.1", + "markdown>=3.3.6", + "markupsafe>=2.0.1", + "mergedeep>=1.3.4", + "mkdocs-get-deps>=0.2.0", + "packaging>=20.5", + "pathspec>=0.11.1", + "pyyaml-env-tag>=0.1", + "pyyaml>=5.1", + "watchdog>=2.0", +] +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.2.0" +requires_python = ">=3.8" +summary = "Automatically link across pages in MkDocs." +groups = ["dev"] +dependencies = [ + "Markdown>=3.3", + "markupsafe>=2.0.1", + "mkdocs>=1.1", +] +files = [ + {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, + {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +requires_python = ">=3.8" +summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +groups = ["dev"] +dependencies = [ + "mergedeep>=1.3.4", + "platformdirs>=2.2.0", + "pyyaml>=5.1", +] +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.34" +requires_python = ">=3.8" +summary = "Documentation that simply works" +groups = ["dev"] +dependencies = [ + "babel~=2.10", + "colorama~=0.4", + "jinja2~=3.0", + "markdown~=3.2", + "mkdocs-material-extensions~=1.3", + "mkdocs~=1.6", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.2", + "regex>=2022.4", + "requests~=2.26", +] +files = [ + {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, + {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown and MkDocs Material." +groups = ["dev"] +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.26.0" +requires_python = ">=3.8" +summary = "Automatic documentation from sources, for MkDocs." +groups = ["dev"] +dependencies = [ + "Jinja2>=2.11.1", + "Markdown>=3.6", + "MarkupSafe>=1.1", + "click>=7.0", + "mkdocs-autorefs>=1.2", + "mkdocs>=1.4", + "platformdirs>=2.2", + "pymdown-extensions>=6.3", +] +files = [ + {file = "mkdocstrings-0.26.0-py3-none-any.whl", hash = "sha256:1aa227fe94f88e80737d37514523aacd473fc4b50a7f6852ce41447ab23f2654"}, + {file = "mkdocstrings-0.26.0.tar.gz", hash = "sha256:ff9d0de28c8fa877ed9b29a42fe407cfe6736d70a1c48177aa84fcc3dc8518cd"}, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.10.9" +requires_python = ">=3.8" +summary = "A Python handler for mkdocstrings." +groups = ["dev"] +dependencies = [ + "griffe>=0.49", + "mkdocs-autorefs>=1.0", + "mkdocstrings>=0.25", +] +files = [ + {file = "mkdocstrings_python-1.10.9-py3-none-any.whl", hash = "sha256:cbe98710a6757dfd4dff79bf36cb9731908fb4c69dd2736b15270ae7a488243d"}, + {file = "mkdocstrings_python-1.10.9.tar.gz", hash = "sha256:f344aaa47e727d8a2dc911e063025e58e2b7fb31a41110ccc3902aa6be7ca196"}, +] + [[package]] name = "more-itertools" version = "10.3.0" @@ -932,6 +1136,16 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "paginate" +version = "0.5.7" +summary = "Divides large result sets into pages for easier browsing" +groups = ["dev"] +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -1166,6 +1380,17 @@ files = [ {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] +[[package]] +name = "pygments" +version = "2.18.0" +requires_python = ">=3.8" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["dev"] +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + [[package]] name = "pyjwt" version = "2.9.0" @@ -1177,6 +1402,21 @@ files = [ {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, ] +[[package]] +name = "pymdown-extensions" +version = "10.9" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown." +groups = ["dev"] +dependencies = [ + "markdown>=3.6", + "pyyaml", +] +files = [ + {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, + {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, +] + [[package]] name = "pyright" version = "1.1.374" @@ -1242,7 +1482,7 @@ name = "python-dateutil" version = "2.9.0.post0" requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" summary = "Extensions to the standard Python datetime module" -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "six>=1.5", ] @@ -1278,7 +1518,7 @@ name = "pyyaml" version = "6.0.1" requires_python = ">=3.6" summary = "YAML parser and emitter for Python" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, @@ -1290,6 +1530,20 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +requires_python = ">=3.6" +summary = "A custom YAML tag for referencing environment variables in YAML files. " +groups = ["dev"] +dependencies = [ + "pyyaml", +] +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + [[package]] name = "redis" version = "5.0.8" @@ -1317,12 +1571,37 @@ files = [ {file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"}, ] +[[package]] +name = "regex" +version = "2024.7.24" +requires_python = ">=3.8" +summary = "Alternative regular expression module, to replace re." +groups = ["dev"] +files = [ + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad"}, + {file = "regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440"}, + {file = "regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94"}, + {file = "regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38"}, + {file = "regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc"}, + {file = "regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908"}, + {file = "regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506"}, +] + [[package]] name = "requests" version = "2.32.3" requires_python = ">=3.8" summary = "Python HTTP for Humans." -groups = ["default"] +groups = ["default", "dev"] dependencies = [ "certifi>=2017.4.17", "charset-normalizer<4,>=2", @@ -1339,7 +1618,7 @@ name = "six" version = "1.16.0" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" summary = "Python 2 and 3 compatibility utilities" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1471,7 +1750,7 @@ name = "typing-extensions" version = "4.12.2" requires_python = ">=3.8" summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1493,7 +1772,7 @@ name = "urllib3" version = "2.2.2" requires_python = ">=3.8" summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["default"] +groups = ["default", "dev"] files = [ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, @@ -1579,6 +1858,33 @@ files = [ {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, ] +[[package]] +name = "watchdog" +version = "5.0.0" +requires_python = ">=3.9" +summary = "Filesystem events monitoring" +groups = ["dev"] +files = [ + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45"}, + {file = "watchdog-5.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bc16d448a74a929b896ed9578c25756b2125400b19b3258be8d9a681c7ae8e71"}, + {file = "watchdog-5.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7e6b0e9b8a9dc3865d65888b5f5222da4ba9c4e09eab13cff5e305e7b7e7248f"}, + {file = "watchdog-5.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4fe6780915000743074236b21b6c37419aea71112af62237881bc265589fe463"}, + {file = "watchdog-5.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0710e9502727f688a7e06d48078545c54485b3d6eb53b171810879d8223c362a"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9"}, + {file = "watchdog-5.0.0-py3-none-win32.whl", hash = "sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12"}, + {file = "watchdog-5.0.0-py3-none-win_amd64.whl", hash = "sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4"}, + {file = "watchdog-5.0.0-py3-none-win_ia64.whl", hash = "sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c"}, + {file = "watchdog-5.0.0.tar.gz", hash = "sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e"}, +] + [[package]] name = "watchfiles" version = "0.22.0" diff --git a/pyproject.toml b/pyproject.toml index 3c1c828..ce3cde2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ requires = ["pdm-backend"] build-backend = "pdm.backend" [project.scripts] -materia = "materia.main:main" +materia = "materia.app.cli:cli" [tool.pyright] reportGeneralTypeIssues = false @@ -70,6 +70,9 @@ dev = [ "pytest-asyncio>=0.23.7", "asgi-lifespan>=2.1.0", "pytest-cov>=5.0.0", + "mkdocs-material>=9.5.34", + "mkdocstrings-python>=1.10.9", + "griffe-typingdoc>=0.2.6", ] [tool.pdm.build] diff --git a/src/materia/app/app.py b/src/materia/app/app.py index bedf8b4..f4e17de 100644 --- a/src/materia/app/app.py +++ b/src/materia/app/app.py @@ -2,11 +2,13 @@ from contextlib import _AsyncGeneratorContextManager, asynccontextmanager import os import sys from typing import AsyncIterator, TypedDict, Self, Optional +from pathlib import Path import uvicorn from fastapi import FastAPI from fastapi.routing import APIRoute from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from materia.core import ( Config, Logger, @@ -78,8 +80,9 @@ class Application: async def prepare_working_directory(self): try: - self.logger.debug("Changing working directory") - os.chdir(self.config.application.working_directory.resolve()) + path = self.config.application.working_directory.resolve() + self.logger.debug(f"Changing working directory to {path}") + os.chdir(path) except FileNotFoundError as e: self.logger.error("Failed to change working directory: {}", e) sys.exit() @@ -117,7 +120,11 @@ class Application: self.backend = FastAPI( title="materia", version="0.1.0", - docs_url="/api/docs", + docs_url=None, + redoc_url=None, + swagger_ui_init_oauth=None, + swagger_ui_oauth2_redirect_url=None, + openapi_url="/api/openapi.json", lifespan=lifespan, ) self.backend.add_middleware( @@ -127,6 +134,7 @@ class Application: allow_methods=["*"], allow_headers=["*"], ) + self.backend.include_router(routers.docs.router) self.backend.include_router(routers.api.router) self.backend.include_router(routers.resources.router) self.backend.include_router(routers.root.router) diff --git a/src/materia/app/cli.py b/src/materia/app/cli.py index 0d506f8..dc9ce56 100644 --- a/src/materia/app/cli.py +++ b/src/materia/app/cli.py @@ -15,7 +15,8 @@ def cli(): @cli.command() @click.option("--config", type=Path) -def start(config: Path): +@click.option("--debug", "-d", is_flag=True, default=False, help="Enable debug output.") +def start(config: Path, debug: bool): config_path = config logger = Logger.new() @@ -48,6 +49,9 @@ def start(config: Path): logger.info("Using the default configuration.") config = Config() + if debug: + config.log.level = "debug" + async def main(): app = await Application.new(config) await app.start() diff --git a/src/materia/core/config.py b/src/materia/core/config.py index cc2dd9c..3e3ac42 100644 --- a/src/materia/core/config.py +++ b/src/materia/core/config.py @@ -150,7 +150,7 @@ class Repository(BaseModel): capacity: int = 5 << 30 -class Config(BaseSettings, env_prefix="materia_", env_nested_delimiter="_"): +class Config(BaseSettings, env_prefix="materia_", env_nested_delimiter="__"): application: Application = Application() log: Log = Log() server: Server = Server() diff --git a/src/materia/core/database.py b/src/materia/core/database.py index 70b5aaf..f020486 100644 --- a/src/materia/core/database.py +++ b/src/materia/core/database.py @@ -77,7 +77,9 @@ class Database: async with database.connection() as connection: await connection.rollback() except Exception as e: - raise DatabaseError(f"Failed to connect to database: {url}") from e + raise DatabaseError( + f"Failed to connect to database '{url}': {e}" + ) from e return database diff --git a/src/materia/routers/__init__.py b/src/materia/routers/__init__.py index 432e0a0..e42f683 100644 --- a/src/materia/routers/__init__.py +++ b/src/materia/routers/__init__.py @@ -1 +1 @@ -from materia.routers import middleware, api, resources, root +from materia.routers import middleware, api, resources, root, docs diff --git a/src/materia/routers/api/__init__.py b/src/materia/routers/api/__init__.py index 6b58e57..6581b92 100644 --- a/src/materia/routers/api/__init__.py +++ b/src/materia/routers/api/__init__.py @@ -1,11 +1,17 @@ -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException from materia.routers.api.auth import auth, oauth -from materia.routers.api import user, repository, directory, file +from materia.routers.api import docs, user, repository, directory, file router = APIRouter(prefix="/api") +router.include_router(docs.router) router.include_router(auth.router) router.include_router(oauth.router) router.include_router(user.router) router.include_router(repository.router) router.include_router(directory.router) router.include_router(file.router) + + +@router.get("/api/{catchall:path}", status_code=404, include_in_schema=False) +def not_found(): + raise HTTPException(status_code=404) diff --git a/src/materia/routers/api/auth/__init__.py b/src/materia/routers/api/auth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/materia/routers/api/docs.py b/src/materia/routers/api/docs.py new file mode 100644 index 0000000..38295cb --- /dev/null +++ b/src/materia/routers/api/docs.py @@ -0,0 +1,40 @@ +from fastapi import APIRouter, Request +from fastapi.responses import HTMLResponse + +router = APIRouter() + + +@router.get("/docs", response_class=HTMLResponse, include_in_schema=False) +async def rapidoc(request: Request): + return f""" + + + + + + + + + + + + + + """ diff --git a/src/materia/routers/docs.py b/src/materia/routers/docs.py new file mode 100644 index 0000000..ad5a860 --- /dev/null +++ b/src/materia/routers/docs.py @@ -0,0 +1,33 @@ +from fastapi import APIRouter, Request, Response, status, HTTPException, Depends +from fastapi.responses import HTMLResponse, FileResponse +from fastapi.templating import Jinja2Templates +from fastapi.staticfiles import StaticFiles +import mimetypes +from pathlib import Path +from materia.core.misc import optional +from materia.routers import middleware + +from materia import docs as materia_docs + +router = APIRouter() + +# templates = Jinja2Templates(directory=Path(materia_docs.__path__[0])) +# p = Path(__file__).parent.joinpath("..", "docs").resolve() +# router.mount("/docs", StaticFiles(directory="doces", html=True), name="docs") + + +@router.get("/docs/{catchall:path}", include_in_schema=False) +async def docs(request: Request, ctx: middleware.Context = Depends()): + docs_directory = Path(materia_docs.__path__[0]).resolve() + target = docs_directory.joinpath(request.path_params["catchall"]).resolve() + + if not optional(target.relative_to, docs_directory): + raise HTTPException(status.HTTP_403_FORBIDDEN) + + if target.is_dir() and (index := target.joinpath("index.html")).is_file(): + return FileResponse(index) + + if not target.is_file(): + raise HTTPException(status.HTTP_404_NOT_FOUND) + + return FileResponse(target) diff --git a/src/materia/routers/root.py b/src/materia/routers/root.py index ad0f4e1..ea60028 100644 --- a/src/materia/routers/root.py +++ b/src/materia/routers/root.py @@ -1,5 +1,5 @@ from pathlib import Path -from fastapi import APIRouter, Request +from fastapi import APIRouter, Request, HTTPException from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates @@ -13,6 +13,7 @@ else: templates = Jinja2Templates(directory=Path(materia_frontend.__path__[0]) / "dist") - @router.get("/{spa:path}", response_class=HTMLResponse) + @router.get("/{spa:path}", response_class=HTMLResponse, include_in_schema=False) async def root(request: Request): + # raise HTTPException(404) return templates.TemplateResponse(request, "base.html", {"view": "app"})