diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8a732ea --- /dev/null +++ b/.dockerignore @@ -0,0 +1,35 @@ + +# You may want to customise this file depending on your Operating System +# and the editor that you use. +# +# We recommend that you use a Global Gitignore for files that are not related +# to the project. (https://help.github.com/articles/ignoring-files/#create-a-global-gitignore) + +# OS +# +# Ref: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# Ref: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Ref: https://github.com/github/gitignore/blob/master/Global/Linux.gitignore +.DS_STORE +Thumbs.db + +# Editors +# +# Ref: https://github.com/github/gitignore/blob/master/Global +# Ref: https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore +# Ref: https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore +.idea +.chrome +/*.log +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# Python +**/__pycache__ +.venv + +# Local Developer Notes +dev diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f1d5d86 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: https://EditorConfig.org +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Python +[*.py] +indent_style = tab +indent_size = 2 + +# YML +[*.(yml|yaml)] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9430679 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ + # Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.br filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Documents +*.pdf filter=lfs diff=lfs merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.psd filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text + +# Fonts +*.woff2 filter=lfs diff=lfs merge=lfs -text diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..7c2f850 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: +- repo: https://github.com/charliermarsh/ruff-pre-commit + # Ruff version. + rev: 'v0.0.259' + hooks: + - id: ruff +- repo: https://github.com/pre-commit/mirrors-mypy + rev: 'v1.1.1' + hooks: + - id: mypy +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: 'v4.4.0' + hooks: + - id: check-ast + - id: check-case-conflict + - id: check-merge-conflict + - id: check-yaml + - id: check-toml diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..e4d356b --- /dev/null +++ b/app/main.py @@ -0,0 +1,41 @@ +import secrets +from fastapi import FastAPI, HTTPException, Security +from fastapi.openapi.models import APIKey + +#################### +# - Utilities +#################### +async def g_api_key( + api_key_header: APIKey = Security(api_key_header), +) -> APIKey: + if api_key_header and secrets.compare_digest( + str(api_key_header), + settings.api_key.get_secret_value(), + ): + return api_key_header + + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="Wrong API key.", + ) + +#################### +# - Types +#################### +class PInstSupCategory(Enum) + +#################### +# - FastAPI App +#################### +app = FastAPI( + prefix="/v1", + dependencies=[Security(g_api_key)], +) + +@app.post("/report/support") +async def report_support( + category: + description: str, +) -> bool: + """Report an instance of granted Python Installation support.""" + return await d_old_app_passes() diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..096c90a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,133 @@ +[tool.poetry] +name = "docker-mailserver-gate" +version = "0.0.1" +description = "Low-level API abstraction of setup.sh in docker-mailserver, accessible by API key." +authors = ["Sofus Albert Høgsbro Rose "] +license = "AGPL3+" + +[tool.poetry.dependencies] +python = "3.11.*" +fastapi = "^0.94.0" +uvicorn = "^0.21.0" +pydantic = {extras = ["email"], version = "^1.10.6"} +docker = "^6.0.1" + +[tool.poetry.group.dev.dependencies] +devtools = "^0.10.0" +schemathesis = "^3.19.0" +pre-commit = "^3.2.1" +ruff = "^0.0.259" +tan = "^22.12.1" +mypy = "^1.1.1" + + + +#################### +# - Tooling: Static Analysis +#################### +[tool.black] +## - Actually uses tan, which is identical to black but with tab support. +## - Run Before ruff + +use-tabs = true +line-length = 79 +target-version = ["py311"] + +[tool.ruff] +## - Run After tan + +target-version = "py311" +line-length = 79 + +select = [ + "E", # pycodestyle ## General Purpose + "F", # pyflakes ## General Purpose + "PL", # Pylint ## General Purpose + + ## Code Quality + "TCH", # flake8-type-checking ## Type Checking Block Validator + "C90", # mccabe ## Avoid Too-Complex Functions + "ERA", # eradicate ## Ban Commented Code + "TRY", # tryceratops ## Exception Handling Style + "B", # flake8-bugbear ## Opinionated, Probable-Bug Patterns + #"N", # pep8-naming ## TODO: Force Good Naming Conventions + #"D", # pydocstyle ## TODO: Force docstrings + "SIM", # flake8-simplify ## Sanity-Check for Code Simplification + "SLF", # flake8-self ## Ban Private Member Access + "RUF", # Ruff-specific rules ## Extra Good-To-Have Rules + + ## Style + "I", # isort ## Force import Sorting + "UP", # pyupgrade ## Enforce Upgrade to Newer Python Syntaxes + "COM", # flake8-commas ## Enforce Trailing Commas + "Q", # flake8-quotes ## Finally - Quoting Style! + "PTH", # flake8-use-pathlib ## Enforce pathlib usage + "A", # flake8-builtins ## Prevent Builtin Shadowing + "C4", # flake8-comprehensions ## Check Compehension Appropriateness + "DTZ", # flake8-datetimez ## Ban naive Datetime Creation + "EM", # flake8-errmsg ## Check Exception String Formatting + "ISC", # flake8-implicit-str-concat ## Enforce Good String Literal Concat + "G", # flake8-logging-format ## Enforce Good Logging Practices + "INP", # flake8-no-pep420 ## Ban PEP420; Enforce __init__.py. + "PIE", # flake8-pie ## Misc Opinionated Checks + "T20", # flake8-print ## Ban print() + "RSE", # flake8-raise ## Check Niche Exception Raising Pattern + "RET", # flake8-return ## Enforce Good Returning + "ARG", # flake8-unused-arguments ## Ban Unused Arguments + + # Specific + "PT", # flake8-pytest-style ## pytest-Specific Checks +] + +ignore = [ + "B008", # FastAPI uses this for Depends(), Security(), etc. . + "E701", # class foo(Parent): pass or if simple: return are perfectly elegant +] +task-tags = ["TODO", "REF", "FIXME"] + +[tool.ruff.flake8-bugbear] +extend-immutable-calls = ["fastapi.Depends", "fastapi.Query"] + +[tool.ruff.pycodestyle] +ignore-overlong-task-comments = true + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.pylint] +max-args = 6 + + +#################### +# - Tooling: Type Checking +#################### +[tool.mypy] +plugins = [ + "pydantic.mypy" +] + +follow_imports = "silent" +warn_redundant_casts = true +warn_unused_ignores = true +disallow_any_generics = true +check_untyped_defs = true +no_implicit_reexport = true + +# for strict mypy: (this is the tricky one :-)) +disallow_untyped_defs = true + +[tool.pydantic-mypy] +# REF: https://docs.pydantic.dev/mypy_plugin/ +init_forbid_extra = true +init_typed = false +warn_required_dynamic_aliases = true +warn_untyped_fields = true + + + +#################### +# - Poetry Integration +#################### +[build-system] +requires = ["poetry>=1.0"] +build-backend = "poetry.masonry.api"