diff --git a/.gitignore b/.gitignore index 55be276..8ca6934 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# ---> Python # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -21,7 +20,6 @@ parts/ sdist/ var/ wheels/ -share/python-wheels/ *.egg-info/ .installed.cfg *.egg @@ -40,17 +38,14 @@ 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 @@ -60,7 +55,6 @@ cover/ *.log local_settings.py db.sqlite3 -db.sqlite3-journal # Flask stuff: instance/ @@ -73,41 +67,16 @@ instance/ 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 +.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 - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ - -# Celery stuff +# celery beat schedule file celerybeat-schedule -celerybeat.pid # SageMath parsed files *.sage.py @@ -133,22 +102,18 @@ venv.bak/ # mypy .mypy_cache/ -.dmypy.json -dmypy.json -# Pyre type checker -.pyre/ +# Jetbrains PyCharm +.idea/ -# pytype static type analyzer -.pytype/ +# VSCode +.vscode/ +/export/ -# Cython debug symbols -cython_debug/ +# Project misc objects +debug.log -# PyCharm -# JetBrains specific template is maintainted 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/ +# Other Django stuff +/static/ +/media/ diff --git a/HighTower/__init__.py b/HighTower/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/HighTower/asgi.py b/HighTower/asgi.py new file mode 100644 index 0000000..758c87c --- /dev/null +++ b/HighTower/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for HighTower project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "HighTower.settings") + +application = get_asgi_application() diff --git a/HighTower/settings.py b/HighTower/settings.py new file mode 100644 index 0000000..6a76917 --- /dev/null +++ b/HighTower/settings.py @@ -0,0 +1,117 @@ +# Django settings for HighTower project. + +from pathlib import Path +from dotenv import dotenv_values +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent +config = dotenv_values(BASE_DIR / '.env') + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config["SECRET_KEY"] + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "hosts.apps.HostsConfig", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "HighTower.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "HighTower.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/3.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": + "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": + "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": + "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": + "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.1/topics/i18n/ + +LANGUAGE_CODE = "ru-ru" + +TIME_ZONE = "Europe/Moscow" + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.1/howto/static-files/ + +STATIC_URL = "/static/" + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/HighTower/urls.py b/HighTower/urls.py new file mode 100644 index 0000000..586c5cf --- /dev/null +++ b/HighTower/urls.py @@ -0,0 +1,10 @@ +"""HighTower URL Configuration""" + +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path("admin/", admin.site.urls), + path("", include('hosts.urls')), +] + diff --git a/HighTower/wsgi.py b/HighTower/wsgi.py new file mode 100644 index 0000000..789808a --- /dev/null +++ b/HighTower/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for HighTower project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "HighTower.settings") + +application = get_wsgi_application() diff --git a/README.md b/README.md index ba17953..da4a1c5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# HighTower +# HighTower Project + +Web hosts monitoring tool + +### Stack: Python(Django, requests) -Web hosts monitoring tool - - diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..6d230ae --- /dev/null +++ b/TODO.md @@ -0,0 +1,4 @@ +# TODO + +1. Проверка доменов на срок окончания регистрации. +2. Телеграм бот для сообщений о проблемах. diff --git a/hosts/__init__.py b/hosts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hosts/admin.py b/hosts/admin.py new file mode 100644 index 0000000..2b2b86d --- /dev/null +++ b/hosts/admin.py @@ -0,0 +1,27 @@ +import requests +from django.contrib import admin +from .models import Host + + +def toggle_check_hosts(queryset): + for item in queryset: + try: + r = requests.get(item.host_name) + if r.status_code == 200: + item.status = True + else: + item.status = False + except requests.RequestException: + item.status = False + item.save() + + +toggle_check_hosts.short_description = "Проверить" + + +@admin.register(Host) +class HostAdmin(admin.ModelAdmin): + list_display = ( + "host_name", "port", "status", "status_code", "created", "updated" + ) + actions = [toggle_check_hosts] diff --git a/hosts/apps.py b/hosts/apps.py new file mode 100644 index 0000000..f838cf5 --- /dev/null +++ b/hosts/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class HostsConfig(AppConfig): + name = "hosts" diff --git a/hosts/management/commands/check_hosts.py b/hosts/management/commands/check_hosts.py new file mode 100644 index 0000000..5aed38c --- /dev/null +++ b/hosts/management/commands/check_hosts.py @@ -0,0 +1,22 @@ +import requests +from django.core.management import BaseCommand +from ...models import Host + + +class Command(BaseCommand): + """ + Check Hosts Django Management Command + """ + def handle(self, *args, **kwargs): + hosts = Host.objects.all() + for host in hosts: + try: + r = requests.get(host.host_name) + if r.status_code == 200: + host.status = True + else: + host.status = False + + except requests.exceptions.RequestException: + host.status = False + host.save() diff --git a/hosts/migrations/__init__.py b/hosts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hosts/models.py b/hosts/models.py new file mode 100644 index 0000000..b0ae819 --- /dev/null +++ b/hosts/models.py @@ -0,0 +1,21 @@ +from django.db import models + + +class Host(models.Model): + """ + Host model + """ + host_name = models.URLField("Host", max_length=100) + port = models.PositiveSmallIntegerField("Port", default=80) + status = models.BooleanField("Status", default=True) + status_code = models.PositiveSmallIntegerField("Status Code", default=200) + created = models.DateTimeField("Created", auto_now_add=True) + updated = models.DateTimeField("Updated", auto_now=True) + + class Meta: + verbose_name = "Host" + verbose_name_plural = "Hosts" + ordering = ["-created"] + + def __str__(self): + return self.host_name diff --git a/hosts/tests.py b/hosts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/hosts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/hosts/urls.py b/hosts/urls.py new file mode 100644 index 0000000..3e03d40 --- /dev/null +++ b/hosts/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('hosts/', views.Index.as_view(), name='index'), + path('hosts//', views.HostDetail.as_view(), name='host-info'), + path('add/', views.HostCreate.as_view(), name='add'), +] diff --git a/hosts/utils.py b/hosts/utils.py new file mode 100644 index 0000000..e69de29 diff --git a/hosts/views.py b/hosts/views.py new file mode 100644 index 0000000..6929c6d --- /dev/null +++ b/hosts/views.py @@ -0,0 +1,23 @@ +from django.urls import reverse_lazy +from django.views.generic import ListView, DetailView +from django.views.generic.edit import CreateView + +from .models import Host + + +class Index(ListView): + model = Host + template_name = 'index.html' + paginate_by = 10 + + +class HostCreate(CreateView): + model = Host + fields = ['host_name', 'port'] + template_name = 'add_host.html' + success_url = reverse_lazy('index') + + +class HostDetail(DetailView): + model = Host + template_name = 'host_info.html' diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..60eb4cd --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "HighTower.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1673fb2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Django +requests +python-dotenv \ No newline at end of file diff --git a/templates/add_host.html b/templates/add_host.html new file mode 100644 index 0000000..7894d27 --- /dev/null +++ b/templates/add_host.html @@ -0,0 +1,10 @@ +{% extends 'base.html' %} +{% block content %} +
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..1427d9e --- /dev/null +++ b/templates/base.html @@ -0,0 +1,14 @@ + + + + + HighTower + + + +
+ {% block content %}{% endblock %} +
+ + diff --git a/templates/host_info.html b/templates/host_info.html new file mode 100644 index 0000000..2f648d6 --- /dev/null +++ b/templates/host_info.html @@ -0,0 +1,25 @@ +{% extends 'base.html' %} +{% block content %} +

{{ host.host_name }}:{{ host.port }}

+
+
+ + + + + + + + + +
Статус + {% if host.status == True %} + ON + {% else %} + OFF + {% endif %} +
Обновлен{{ host.updated|date:"d.m.Y." }}
+
+
+
+{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..529e30b --- /dev/null +++ b/templates/index.html @@ -0,0 +1,25 @@ +{% extends 'base.html' %} +{% block content %} + + + + + + + + {% for obj in object_list %} + + + + + + + {% endfor %} +
ХостПортСтатусИзменен
{{ obj.host_name }}{{ obj.port }} + {% if obj.status == True %} + ON + {% else %} + OFF + {% endif %} + {{ obj.updated|date:"d.m.Y." }}
+{% endblock %}