diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..89954f83 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,21 @@ +## Synopsis +Description about main changes and how that would affected on project. + +## Main changes + +- a +- b +- c +- d + +## Other important changes + +- a +- b +- c +- d + +### Closes issue and bug + +Closes ... +Closes ... \ No newline at end of file diff --git a/.github/workflows/docker-publish-image-stable.yml b/.github/workflows/docker-publish-image-stable.yml deleted file mode 100644 index 5c550c6a..00000000 --- a/.github/workflows/docker-publish-image-stable.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Build and publish docker image -on: - push: - tags: '*.*.*' - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}_stable - -jobs: - build-and-push-image: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Log in to the Container registry - uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/docker-publish-image-unstable.yml b/.github/workflows/docker-publish-image.yml similarity index 63% rename from .github/workflows/docker-publish-image-unstable.yml rename to .github/workflows/docker-publish-image.yml index 534771ce..8f70a9a4 100644 --- a/.github/workflows/docker-publish-image-unstable.yml +++ b/.github/workflows/docker-publish-image.yml @@ -1,11 +1,11 @@ name: Build and publish docker image on: push: - branches: [ develop ] + branches: [ master ] env: REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}_unstable + IMAGE_NAME: ${{ github.repository }}_docker jobs: build-and-push-image: @@ -16,7 +16,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 - name: Log in to the Container registry uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 @@ -25,20 +31,24 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - name: Get current date id: date run: echo "::set-output name=date::$(date +'%Y.%m.%d')" + - name: Extract metadata + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver, pattern={{raw}} + type=raw, value=${{ steps.date.outputs.date }} + - name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: context: . push: true - tags: ${{ steps.meta.outputs.tags }}_${{ steps.date.outputs.date }} + platforms: linux/amd64, linux/arm64 + tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7c4c71da..7ba6d239 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,7 +1,7 @@ name: Build and push docs on: push: - branches: [ master, develop ] + branches: [ master ] jobs: build-and-deploy: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a4601cb..03ded05e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,12 +23,20 @@ jobs: - name: Create config for server run: | - mv example_config.ini config.ini + cp example_config.ini config.ini - name: Run tests run: | pipenv run coverage run -m unittest discover tests + - name: Linting code + run: | + pipenv run flake8 --config ./setup.cfg --show-source --statistics + + - name: Type checking + run: | + pipenv run mypy --config=./setup.cfg . + - name: Push report to coveralls.io env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 1cfa01fd..1a3f8dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -137,7 +137,8 @@ dmypy.json .idea ## Morelia stuff -db_sqlite.db -config.ini +*.db +/config.ini +config.ini.BAK* ngrok.exe /docs/log/ diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 00000000..d4146776 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,8 @@ +## Version 0.3.0 + +- Added a module for managing the server config +- Added linter code checking for style compliance and mypy code checking for type hinting +- Application scripts for working with the server were combined into one manage.py +- Errors were closed To fix an error in the declaration of the variable obj in error.py #170 and Add tests for manage.py #96 +- Fixed a problem when building a multi platform docker image +- Minor fixes and improvements diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..7355c36a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,100 @@ +# How to contribute to MoreliaServer # + +## Support questions ## + +Please don't use the issue tracker for this. The issue tracker is a tool +to address bugs and feature requests in Morelia server itself. Use one of the +following resources for questions about using Morelia server or issues with your +own code: + +- The ``#general`` channel on our [Slack](https://moreliatalk.slack.com) chat. +- Ask on our [GitHub Discussions](https://github.com/MoreliaTalk/morelia_server/discussions). +- Ask for support in our telegram chatroom: +[Project MoreliaTalk](https://t.me/joinchat/LImHShzAmIWvpMxDTr5Vxw). + + +## Reporting issues ## + +Include the following information in your post: + +- Describe what you expected to happen. +- If possible, include a +[minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) to help us + identify the issue. This also helps check that the issue is not with + your own code. +- Describe what actually happened. Include the full traceback if there + was an exception. +- List your Python and Morelia server versions. If possible, check if this + issue is already fixed in the latest releases or the latest code in + the repository. + + +## Submitting patches ## + +If there is not an open issue for what you want to submit, prefer +opening one for discussion before working on a PR. You can work on any +issue that doesn't have an open PR linked to it or a maintainer assigned +to it. These show up in the sidebar. No need to ask if you can work on +an issue that interests you. + +Include the following in your patch: + +- All patches create in [develop](https://github.com/MoreliaTalk/morelia_server/tree/develop). +For example new branch name ``develop-#xxx``. Where xxx in name is the issue number. +- Use [Flake8](https://github.com/PyCQA/flake8) to format your code. +- Include tests if your patch adds or changes code. Make sure the test + not fails. +- Update any relevant docs pages and docstrings. Doc pages and + docstrings should be wrapped at 120 characters. +- Add an entry in ``CHANGES.md``. Use the same style as other + entries. Also include ``.. versionchanged::`` inline changelogs in + relevant docstrings. + +After you checking all steps for create patch you can create Pull Request (aka PR). Before that, read the +[rules](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request). + + +## Main code rules ## + +### Conventional Commits ### + +Commit messages should be of the following structure: +`(optional scope): [optional body] [optional footer]`: + +`` - for example `fix`, `feat`, `refactor`, `add`, `BREAKING CHANGE`, `docs`, `perf`, `test`. + +`(optional scope)` - context MUST BE a noun enclosed in parentheses, describing the part of the code base affected by +the commit, for example `fix(parser)` + +`` - a brief description of changes to the code + +`[optional body]` - additional description of changes to the code + +`[optional footer]` - meta-information about commit. For example, related pull-requests, discussions, issues, people, etc. + +Read more at [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) + +### Code style ### + +- If not specified below, the entire code is executed according to the [PEP8](https://www.python.org/dev/peps/pep-0008/) +standard. +- Length of string is limited to 79 characters. +- For name constant declaration MUST BE used UPPERCASE. +- Each import MUST BE on a separate line. +- Import templates (from import *) SHOULD NOT be used. +- `lower_case_with_underscores` style MUST BE used for function. +- `CapitalizedWords` style MUST BE used for classes. +- `lowercase` or `lower_case` stype MUST BE used for simple object (variable) +- Each function parameter MUST BE on a separate line. +- All transfers MUST BE vertically aligned +- For docstring MUST BE uses [Google style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) +of comment and docstring. +- Project uses automatic generation of documentation using [Sphinx](https://www.sphinx-doc.org/en/stable/), +for which use the following keywords in the docstrings: +`Args (alias of Parameters)`, `Arguments (alias of Parameters)`, `Attention`, `Attributes`, `Caution`, `Danger`, `Error` +, `Example`, `Examples`, `Hint`, `Important`, `Keyword Args (alias of Keyword Arguments)`, `Keyword Arguments`, `Methods` +`Note`, `Notes`, `Other Parameters`, `Parameters`, `Return (alias of Returns)`, `Returns`, `Raise (alias of Raises)`, +`Raises`, `References`, `See Also`, `Tip`, `Todo`, `Warning`, `Warnings (alias of Warning)`, `Warn (alias of Warns)`, +`Warns`, `Yield (alias of Yields)`, `Yields`. +- All new class (also class methods) and function MUST BE contained a docstring +- All code MUST BE documenting in sphinx-format *.rst files which contains in ./docs diff --git a/Dockerfile b/Dockerfile index 8dab21ca..139b59a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM python:3.10.2-slim-bullseye +FROM python:3.10.4-slim WORKDIR /morelia-server diff --git a/Pipfile b/Pipfile index 737604a4..eefe3f6b 100644 --- a/Pipfile +++ b/Pipfile @@ -5,10 +5,14 @@ verify_ssl = true [dev-packages] flake8 = "*" +flake8-import-order = "*" +flake8-docstrings = "*" +flake8-builtins = "*" pyflakes = "*" pycodestyle = "*" mccabe = "*" coveralls = "==3.3.1" +mypy = "==0.942" [packages] fastapi = "==0.71.0" @@ -29,5 +33,8 @@ win32-setctime = "*" sphinx = "==4.4.0" furo = "==2022.1.2" +[scripts] +manage = "python manage.py" + [requires] python_version = "3.10" diff --git a/Pipfile.lock b/Pipfile.lock index 474c5cac..7969640e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0628c267ce6c693fba4c09dc6e7555a5bf26b504ebfd00b0684590418b9cdd9e" + "sha256": "cdebbf48edc0a663594aa5e7d87faeb4f5b3496c6fdfe44a8e3f6fe26f06ab82" }, "pipfile-spec": 6, "requires": { @@ -209,7 +209,7 @@ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], - "markers": "python_version >= '3.5'", + "markers": "python_version >= '3'", "version": "==3.3" }, "imagesize": { @@ -358,7 +358,7 @@ "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf", "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba" ], - "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==2.4.0" }, "pyparsing": { @@ -681,12 +681,36 @@ "index": "pypi", "version": "==4.0.1" }, + "flake8-builtins": { + "hashes": [ + "sha256:09998853b2405e98e61d2ff3027c47033adbdc17f9fe44ca58443d876eb00f3b", + "sha256:7706babee43879320376861897e5d1468e396a40b8918ed7bccf70e5f90b8687" + ], + "index": "pypi", + "version": "==1.5.3" + }, + "flake8-docstrings": { + "hashes": [ + "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde", + "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b" + ], + "index": "pypi", + "version": "==1.6.0" + }, + "flake8-import-order": { + "hashes": [ + "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543", + "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92" + ], + "index": "pypi", + "version": "==0.18.1" + }, "idna": { "hashes": [ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" ], - "markers": "python_version >= '3.5'", + "markers": "python_version >= '3'", "version": "==3.3" }, "mccabe": { @@ -697,6 +721,42 @@ "index": "pypi", "version": "==0.6.1" }, + "mypy": { + "hashes": [ + "sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e", + "sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16", + "sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2", + "sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c", + "sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67", + "sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5", + "sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b", + "sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6", + "sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58", + "sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d", + "sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17", + "sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0", + "sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca", + "sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6", + "sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322", + "sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534", + "sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3", + "sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db", + "sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904", + "sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c", + "sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46", + "sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8", + "sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee" + ], + "index": "pypi", + "version": "==0.942" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, "pycodestyle": { "hashes": [ "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20", @@ -705,6 +765,14 @@ "index": "pypi", "version": "==2.8.0" }, + "pydocstyle": { + "hashes": [ + "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc", + "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4" + ], + "markers": "python_version >= '3.6'", + "version": "==6.1.1" + }, "pyflakes": { "hashes": [ "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c", @@ -721,6 +789,37 @@ "index": "pypi", "version": "==2.27.1" }, + "setuptools": { + "hashes": [ + "sha256:68e45d17c9281ba25dc0104eadd2647172b3472d9e01f911efa57965e8d51a36", + "sha256:a43bdedf853c670e5fed28e5623403bad2f73cf02f9a2774e91def6bda8265a7" + ], + "markers": "python_version >= '3.7'", + "version": "==62.3.2" + }, + "snowballstemmer": { + "hashes": [ + "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", + "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a" + ], + "version": "==2.2.0" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708", + "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376" + ], + "markers": "python_version >= '3.7'", + "version": "==4.2.0" + }, "urllib3": { "hashes": [ "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14", diff --git a/README.md b/README.md index 40aefbdb..bd9d7b5c 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ python -m pdb ./debug_server.py [Telegram](https://t.me/joinchat/LImHShzAmIWvpMxDTr5Vxw) - группа где обсуждаются насущные вопросы. -[Slack](www.moreliatalk.slack.com) - альтернативный вариант обсуждения проекта. +[Slack](moreliatalk.slack.com) - обсуждение проекта. ## Лицензия ## diff --git a/admin/admin.py b/admin/admin.py deleted file mode 100644 index a35ad82e..00000000 --- a/admin/admin.py +++ /dev/null @@ -1,156 +0,0 @@ -""" - Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. - Look at the file AUTHORS.md(located at the root of the project) to get the - full list. - - This file is part of Morelia Server. - - Morelia Server is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Morelia Server is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with Morelia Server. If not, see . -""" -from fastapi import Depends -from fastapi.applications import FastAPI -from fastapi.requests import Request -from fastapi.templating import Jinja2Templates -from pathlib import Path -from starlette.responses import RedirectResponse - -from mod.db.dbhandler import DBHandler -from config import DATABASE -from . import login -from . import logs -from . import control as manage - -app = FastAPI() - -templates = Jinja2Templates(Path(__file__).parent / "templates") - -app.include_router(login.router) -app.include_router(logs.router) -app.include_router(manage.router) - -db_connect = DBHandler(DATABASE.get('URI')) - - -@app.exception_handler(login.NotAuthenticatedException) -def not_login_exception_handler(request: Request, - exc: login.NotAuthenticatedException): - """ - Catches exception login.NotAuthenticatedException and redirects the user to the login page("/admin/login/") - - Args: - request(Request): request to the server - exc(login.NotAuthenticatedException): catchable user authentication error - - Returns: - (RedirectResponse): redirect response to /admin/login - """ - return RedirectResponse(url="/admin/login") - - -@app.get("/login") -def login_admin(request: Request): - """ - The function is triggered when a request is received for the /login address - and returns a response generated from login.html - - Args: - request(Request): request to the server - - Returns: - (templates.TemplateResponse): response generated from login.html template - - """ - return templates.TemplateResponse("login.html", { - "request": request - }) - - -@app.get("/") -def index_admin(request: Request, - user=Depends(login.login_manager)): - """ - Returns a response with the main page of the admin panel - - Args: - request(Request): request to the server - user: user authentication check - - Returns: - (templates.TemplateResponse): response generated from index_admin.html template - """ - return templates.TemplateResponse("index_admin.html", { - "request": request - }) - - -@app.get("/status") -def status_admin(request: Request, - user=Depends(login.login_manager)): - """ - Returns a response with the status page of the admin panel - - Args: - request(Request): request to the server - user: user authentication check - - Returns: - (templates.TemplateResponse): response generated from status_admin.html template - """ - dbquery = db_connect.get_table_count() - return templates.TemplateResponse("status_admin.html", - {"request": request, - "Messages_count": dbquery.message_count, - "Flows_count": dbquery.flow_count, - "Users_count": dbquery.user_count - }) - - -# TODO Полностью доделать(на данный момент управление сервером не работает) -# после того, как будут сделаны методы работы с бд -@app.get("/manage") -def manage_admin(request: Request, - user=Depends(login.login_manager)): - """ - Returns a response with the mange page of the admin panel - - Args: - request(Request): request to the server - user: user authentication check - - Returns: - (templates.TemplateResponse): response generated from manage_admin.html template - """ - dbquery = db_connect.get_all_user() - return templates.TemplateResponse("manage_admin.html", { - "request": request, - "users": dbquery.count() - }) - - -@app.get("/logs") -def manage_logs(request: Request, - user=Depends(login.login_manager)): - """ - Returns a response with the logs view page of the admin panel - - Args: - request(Request): request to the server - user: user authentication check - - Returns: - (templates.TemplateResponse): response generated from logs_admin.html template - """ - return templates.TemplateResponse("logs_admin.html", { - "request": request - }) diff --git a/admin/control.py b/admin/control.py index 9e74baf4..6cb79a04 100644 --- a/admin/control.py +++ b/admin/control.py @@ -1,23 +1,24 @@ """ - Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. - Look at the file AUTHORS.md(located at the root of the project) to get the - full list. +Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. +Look at the file AUTHORS.md(located at the root of the project) to get the +full list. - This file is part of Morelia Server. +This file is part of Morelia Server. - Morelia Server is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +Morelia Server is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - Morelia Server is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. +Morelia Server is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License - along with Morelia Server. If not, see . +You should have received a copy of the GNU Lesser General Public License +along with Morelia Server. If not, see . """ + from uuid import uuid4 from fastapi import APIRouter @@ -25,29 +26,34 @@ from starlette.requests import Request from starlette.responses import HTMLResponse +from mod.config.config import ConfigHandler from mod.db.dbhandler import DBHandler -from config import DATABASE + + +config = ConfigHandler() +config_option = config.read() router = APIRouter() -db_connect = DBHandler(DATABASE.get('URI')) +db_connect = DBHandler(config_option.uri) @router.post("/manage/delete_user") def delete_user(request: Request, uuid: str = Form(...)): """ - The function receives the request and admin uuid, delete admin user, - and returns a response, redirecting to the main page of the admin panel + Delete admin user. + After returns a response and redirecting to the main page of admin panel. - Args: - request(Request): request for server - uuid(str): uuid admin user + Args: + request(Request): request for server + uuid(str): uuid admin user - Returns: - (HTMLResponse): response redirecting to the main admin page + Returns: + (HTMLResponse): response redirecting to the main admin page """ + fake_uuid = str(uuid4().int) db_connect.update_user(uuid=uuid, diff --git a/admin/general.py b/admin/general.py new file mode 100644 index 00000000..1e38e8e7 --- /dev/null +++ b/admin/general.py @@ -0,0 +1,167 @@ +""" +Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. +Look at the file AUTHORS.md(located at the root of the project) to get the +full list. + +This file is part of Morelia Server. + +Morelia Server is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Morelia Server is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with Morelia Server. If not, see . +""" + +from pathlib import Path + +from fastapi import Depends +from fastapi.applications import FastAPI +from fastapi.requests import Request +from fastapi.responses import RedirectResponse +from fastapi.templating import Jinja2Templates + +from admin import control as manage +from admin import login +from admin import logs +from mod.config.config import ConfigHandler +from mod.db.dbhandler import DBHandler + +app = FastAPI() + +config = ConfigHandler() +config_option = config.read() + +templates = Jinja2Templates(Path(__file__).parent / "templates") + +app.include_router(login.router) +app.include_router(logs.router) +app.include_router(manage.router) + +db_connect = DBHandler(config_option.uri) + + +@app.exception_handler(login.NotAuthenticatedException) +def not_login_exception_handler(request: Request, + exc: login.NotAuthenticatedException): + """ + Catches exception NotAuthenticatedException and redirects to login page. + + Args: + request(Request): request to the server + exc(login.NotAuthenticatedException): catchable user authentication + error + + Returns: + (RedirectResponse): redirect response to /admin/login + """ + + return RedirectResponse(url="/admin/login") + + +@app.get("/login") +def login_admin(request: Request): + """ + Triggered when a request is received for the /login address. + Return a response generated from login.html + + Args: + request(Request): request to the server + + Returns: + (templates.TemplateResponse): response generated from login.html + template + """ + + return templates.TemplateResponse("login.html", + {"request": request}) + + +@app.get("/") +def index_admin(request: Request, + user=Depends(login.login_manager)): + """ + Returns a response with the main page of the admin panel. + + Args: + request(Request): request to the server + user: user authentication check + + Returns: + (templates.TemplateResponse): response generated from index_admin.html + qtemplate + """ + + return templates.TemplateResponse("index_admin.html", + {"request": request}) + + +@app.get("/status") +def status_admin(request: Request, + user=Depends(login.login_manager)): + """ + Returns a response with the status page of the admin panel. + + Args: + request(Request): request to the server + user: user authentication check + + Returns: + (templates.TemplateResponse): response generated from + status_admin.html template + """ + + dbquery = db_connect.get_table_count() + return templates.TemplateResponse("status_admin.html", + {"request": request, + "Messages_count": dbquery.message_count, + "Flows_count": dbquery.flow_count, + "Users_count": dbquery.user_count}) + + +# TODO Полностью доделать(на данный момент управление сервером не работает) +# после того, как будут сделаны методы работы с бд +@app.get("/manage") +def manage_admin(request: Request, + user=Depends(login.login_manager)): + """ + Returns a response with the mange page of the admin panel. + + Args: + request(Request): request to the server + user: user authentication check + + Returns: + (templates.TemplateResponse): response generated from + manage_admin.html template + """ + + dbquery = db_connect.get_all_user() + return templates.TemplateResponse("manage_admin.html", + {"request": request, + "users": dbquery.count()}) + + +@app.get("/logs") +def manage_logs(request: Request, + user=Depends(login.login_manager)): + """ + Returns a response with the logs view page of the admin panel. + + Args: + request(Request): request to the server + user: user authentication check + + Returns: + (templates.TemplateResponse): response generated from logs_admin.html + template + """ + + return templates.TemplateResponse("logs_admin.html", + {"request": request}) diff --git a/admin/login.py b/admin/login.py index 441cd8df..5bb60e2e 100644 --- a/admin/login.py +++ b/admin/login.py @@ -1,49 +1,51 @@ """ - Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. - Look at the file AUTHORS.md(located at the root of the project) to get the - full list. +Copyright (c) 2020 - present NekrodNIK, Stepan Skriabin, rus-ai and other. +Look at the file AUTHORS.md(located at the root of the project) to get the +full list. - This file is part of Morelia Server. +This file is part of Morelia Server. - Morelia Server is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +Morelia Server is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - Morelia Server is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. +Morelia Server is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public License - along with Morelia Server. If not, see . +You should have received a copy of the GNU Lesser General Public License +along with Morelia Server. If not, see . """ + from fastapi import APIRouter from fastapi import Depends from fastapi import Request -from fastapi_login import LoginManager from fastapi.security import OAuth2PasswordRequestForm +from fastapi_login import LoginManager from starlette.responses import HTMLResponse from mod import lib +from mod.config.config import ConfigHandler from mod.db.dbhandler import DBHandler -from config import DATABASE -from config import ADMIN -db_connect = DBHandler(DATABASE.get('URI')) +config = ConfigHandler() +config_option = config.read() + +db_connect = DBHandler(config_option.uri) router = APIRouter() class NotAuthenticatedException(Exception): """ - An exception occurs when the user's authorization data is missing or incorrect + Occurs when the user's authorization data is missing or incorrect. """ - pass -login_manager = LoginManager(ADMIN.get("SECRET_KEY"), +login_manager = LoginManager(config_option.secret_key, token_url="/login/token", use_cookie=True, use_header=False) @@ -51,17 +53,18 @@ class NotAuthenticatedException(Exception): login_manager.not_authenticated_exception = NotAuthenticatedException -@login_manager.user_loader() +@login_manager.user_loader() # type: ignore def get_admin_user_data(username: str): """ - The function of requesting data from the database and checking it against the username, if there is valid data, it returns it + Requesting data from the database and checking it against the username. Args: username(str): username admin user Returns: - (SQLObject) - admin user data from db + (SQLObject): admin user data from db """ + data = db_connect.get_admin_by_name(username=username) if data.count(): return data[0] @@ -70,9 +73,7 @@ def get_admin_user_data(username: str): @router.post("/login/token") def login_token(data: OAuth2PasswordRequestForm = Depends()): """ - The function receives the data - and returns a response that contains the admin token for the user, - valid for 15 minutes + Returns a response that contains the admin token, valid for 15 minutes. Args: data(OAuth2PasswordRequestForm): login data @@ -112,15 +113,15 @@ def login_token(data: OAuth2PasswordRequestForm = Depends()): @router.post("/login/logout") def logout(request: Request): """ - The function receives the request - and returns a response that contains the invalid admin token + Returns a response that contains the invalid admin token. - Args: - request(Request): request for server + Args: + request(Request): request for server - Returns: - (HTMLResponse): response with cookies embedded in it + Returns: + (HTMLResponse): response with cookies embedded in it """ + incorrect_token = "MoreliaTalk" response = HTMLResponse("""