diff --git a/abonapp/pay_systems.py b/abonapp/pay_systems.py index 4835348..c159507 100644 --- a/abonapp/pay_systems.py +++ b/abonapp/pay_systems.py @@ -3,9 +3,11 @@ from django.utils import timezone from mydefs import safe_int, safe_float from .models import Abon, AllTimePayLog from django.db import DatabaseError +from django.conf import settings -from djing.settings import pay_SECRET as SECRET, pay_SERV_ID as SERV_ID +SECRET = getattr(settings, 'pay_SECRET') +SERV_ID = getattr(settings, 'pay_SERV_ID') #?ACT=1&PAY_ACCOUNT=960849&SERVICE_ID=y832r92y8f9e&PAY_ID=3561234&TRADE_POINT=377&SIGN=32e533a72389fe4e93746509f9d672f8 diff --git a/accounts_app/models.py b/accounts_app/models.py index 2508257..a5f5238 100644 --- a/accounts_app/models.py +++ b/accounts_app/models.py @@ -3,11 +3,14 @@ from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin from django.core.validators import RegexValidator from django.utils.translation import ugettext as _ - -from djing.settings import DEFAULT_PICTURE +from django.conf import settings from photo_app.models import Photo +DEFAULT_PICTURE = getattr(settings, 'DEFAULT_PICTURE', '/static/images/default-avatar.png') +TELEPHONE_REGEXP = getattr(settings, 'TELEPHONE_REGEXP', r'^\+[7,8,9,3]\d{10,11}$') + + class MyUserManager(BaseUserManager): def create_user(self, telephone, username, password=None): """ @@ -51,7 +54,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin): max_length=16, verbose_name=_('Telephone'), #unique=True, - validators=[RegexValidator('^\+[7,8,9,3]\d{10,11}$')] + validators=[RegexValidator(TELEPHONE_REGEXP)] ) avatar = models.ForeignKey(Photo, null=True, blank=True, on_delete=models.SET_NULL) email = models.EmailField(default='admin@example.ru') @@ -65,13 +68,12 @@ class UserProfile(AbstractBaseUser, PermissionsMixin): def get_short_name(self): return self.username or self.telephone - # Use UserManager to get the create_user method, etc. objects = MyUserManager() @property def is_staff(self): - "Is the user a member of staff?" + " Is the user a member of staff?" # Simplest possible answer: All admins are staff return self.is_admin diff --git a/agent/mod_mikrotik.py b/agent/mod_mikrotik.py index 103331f..dcb7069 100644 --- a/agent/mod_mikrotik.py +++ b/agent/mod_mikrotik.py @@ -7,11 +7,11 @@ from .core import BaseTransmitter, NasFailedResult, NasNetworkError from mydefs import ping from .structs import TariffStruct, AbonStruct, IpStruct from . import settings -from djing.settings import DEBUG +from django.conf import settings import re -#DEBUG=True +DEBUG = getattr(settings, 'DEBUG', False) LIST_USERS_ALLOWED = 'DjingUsersAllowed' LIST_USERS_BLOCKED = 'DjingUsersBlocked' diff --git a/chatbot/locale/ru/LC_MESSAGES/django.po b/chatbot/locale/ru/LC_MESSAGES/django.po index e6277c8..0543256 100644 --- a/chatbot/locale/ru/LC_MESSAGES/django.po +++ b/chatbot/locale/ru/LC_MESSAGES/django.po @@ -55,3 +55,6 @@ msgstr "Давай пинганём, напиши ip. Нужно будет по msgid "Yes, it's nice to meet% s, I will notify you about events in billing. Successful work;)" msgstr "Да, приятно познакомиться %s, я буду оповещать тебя о событиях в биллинге. Удачной работы ;)" + +msgid "Telegram bot token not found" +msgstr "Токен для бота Telegram не найден" diff --git a/chatbot/models.py b/chatbot/models.py index 550924f..27726ee 100644 --- a/chatbot/models.py +++ b/chatbot/models.py @@ -1,5 +1,7 @@ from django.db import models -from djing.settings import AUTH_USER_MODEL +from django.conf import settings + +AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL') class ChatException(Exception): diff --git a/chatbot/telebot.py b/chatbot/telebot.py index 160dc1a..5e9acb6 100755 --- a/chatbot/telebot.py +++ b/chatbot/telebot.py @@ -133,6 +133,8 @@ class DjingTelebot(helper.ChatHandler): # Просто отправляем текст оповещения указанному админу def send_notify(msg_text, account): try: + if token is None: + raise ChatException(_('Telegram bot token not found')) tb = TelegramBot.objects.get(user=account) tbot = Bot(token) tbot.sendMessage(tb.chat_id, msg_text) diff --git a/cron.py b/cron.py index 1369392..049080c 100755 --- a/cron.py +++ b/cron.py @@ -15,7 +15,7 @@ def main(): users = Abon.objects.all() for user in users: try: - # бдим за услугами абонента: просроченные отключить, заказанные подключить + # бдим за услугами абонента user.bill_service(user) # если нет ip то и нет смысла лезть в NAS diff --git a/devapp/views.py b/devapp/views.py index 46bab38..f332ef7 100644 --- a/devapp/views.py +++ b/devapp/views.py @@ -14,7 +14,7 @@ from .models import Device, Port, DeviceDBException from mydefs import pag_mn, res_success, res_error, only_admins, ping, order_helper from .forms import DeviceForm, PortForm from abonapp.models import AbonGroup, Abon -from djing.settings import DEFAULT_SNMP_PASSWORD +from django.conf import settings @login_required @@ -103,7 +103,7 @@ def dev(request, grp, devid=0): 'mac_addr': request.GET.get('mac'), 'comment': request.GET.get('c'), 'ip_address': request.GET.get('ip'), - 'man_passw': DEFAULT_SNMP_PASSWORD + 'man_passw': getattr(settings, 'DEFAULT_SNMP_PASSWORD', '') }) else: frm = DeviceForm(instance=devinst) diff --git a/djing/settings_example.py b/djing/settings_example.py index df00937..fc038d8 100644 --- a/djing/settings_example.py +++ b/djing/settings_example.py @@ -168,3 +168,5 @@ DHCP_TIMEOUT = 14400 DEFAULT_SNMP_PASSWORD = 'public' TELEGRAM_BOT_TOKEN = 'bot token' + +TELEPHONE_REGEXP = r'^\+[7,8,9,3]\d{10,11}$' diff --git a/docs/ats.md b/docs/ats.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/img/login.png b/docs/img/login.png new file mode 100644 index 0000000..10e7879 Binary files /dev/null and b/docs/img/login.png differ diff --git a/docs/install.md b/docs/install.md index c4ff50e..62ecd2a 100644 --- a/docs/install.md +++ b/docs/install.md @@ -2,7 +2,7 @@ Работа предполагается на python3. Я предпочитаю запускать wsgi сервер на связке uWSGI + Nginx, так что ставить будем соответствующие пакеты. -#####На Fedora25 нужные пакеты можно установить так: +##### На Fedora25 нужные пакеты можно установить так: Для начала подготовим систему, очистим и обновим пакеты. Процесс обновления долгий, так что можно пойти заварить себе чай :) ``` @@ -12,13 +12,18 @@ Затем установим зависимости ``` -# dnf -y install python3 python3-devel python3-pip python3-pillow mariadb uwsgi nginx redis net-snmp net-snmp-libs net-snmp-utils net-snmp-devel net-snmp-python git redhat-rpm-config +# dnf -y install python3 python3-devel python3-pip python3-pillow mariadb mariadb-devel uwsgi nginx uwsgi-plugin-python3 redis net-snmp net-snmp-libs net-snmp-utils net-snmp-devel net-snmp-python git redhat-rpm-config +``` + +Лучше чтоб версия python по умолчанию была третья: +``` +# ln -sf python3 /usr/bin/python ``` Условимся что путь к папке с проектом находится по адресу: */var/www/djing*. Дальше создадим каталок для web, затем обновляем pip и ставим проект через pip: ``` -# mkdir /vaw/www +# mkdir /var/www # cd /var/www # pip3 install --upgrade pip # git clone https://github.com/nerosketch/djing.git @@ -27,91 +32,254 @@ Скопируем конфиги из примеров в реальные: ``` -cd /var/www/djing -cp djing/settings_example.py djing/settings.py -cp agent/settings.py.example agent/settings.py +$ cd /var/www/djing +# cp djing/settings_example.py djing/settings.py +# cp agent/settings.py.example agent/settings.py ``` Затем отредактируйте конфиги для своих нужд. Для удобства я создаю пользователя и группу http:http, и всё что связано с web-сервером запускаю от имени http. ``` -groupadd -r http -useradd -l -M -r -d /dev/null -g http -s /sbin/nologin http -chown -R http:http /var/www -chown -R http:http /etc/nginx -chown -R http:http /etc/uwsgi.* +# groupadd -r http +# useradd -l -M -r -d /dev/null -g http -s /sbin/nologin http +# chown -R http:http /var/www +# chown -R http:http /etc/nginx +# chown -R http:http /etc/uwsgi.* +# chown -R http:http /run/uwsgi/ ``` ### Настройка WEB Сервера Конфиг Nginx на моём рабочем сервере выглядит так: +```nginx +user http; +worker_processes auto; +pid /run/nginx.pid; - user http; - worker_processes auto; - pid /run/nginx.pid; - events { - worker_connections 1024; - } - http { - sendfile on; - upstream djing { server unix:///run/uwsgi/djing.sock; } - - server { - listen 80; - server_name <ваш-домен>.com; - root /var/www/djing; - charset utf-8; - - # укажите где лежит ваш раздел с медиа для сайта - location /media { - alias /var/www/djing/media; - } - - # местоположение статики - location /static { - alias /var/www/djing/static; - } - - # тут надо указать путь куда у вас установился Django + путь к статике админки - # путь к Django тут: /usr/lib/python3.5/site-packages/django - # путь к статике соответственно: contrib/admin/static/admin - location /static/admin { - alias /usr/lib/python3.5/site-packages/django/contrib/admin/static/admin; - } - - # на корневом url / реагируем с помощью сокета проекта - # у нас он называется "djing": upstream djing { server ... - location / { - uwsgi_pass djing; - include uwsgi_params; - } +events { + worker_connections 1024; +} + +http { + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + upstream djing { server unix:///run/uwsgi/djing.sock; } + + server { + listen 80; + server_name <ваш-домен>.com; + root /var/www/djing; + charset utf-8; + + # укажите где лежит ваш раздел с медиа для сайта + location /media { + alias /var/www/djing/media; + } + + # местоположение статики + location /static { + alias /var/www/djing/static; + } + + # тут надо указать путь куда у вас установился Django + путь к статике админки + # путь к Django тут: /usr/lib/python3.5/site-packages/django + # путь к статике соответственно: contrib/admin/static/admin + location /static/admin { + alias /usr/lib/python3.5/site-packages/django/contrib/admin/static/admin; + } + + # на корневом url / реагируем с помощью сокета проекта + # у нас он называется "djing": upstream djing { server ... + location / { + uwsgi_pass djing; + include uwsgi_params; } } +} +``` Это минимальный конфиг Nginx для работы. Проверте файл /run/uwsgi/djing.sock на доступность пользователю http для чтения. Далее настраиваем uWSGI. Мой конфиг для uWSGI в режиме emperor: +> /etc/uwsgi.ini +```ini +[uwsgi] +uid = http +gid = http +pidfile = /run/uwsgi/uwsgi.pid +emperor = /etc/uwsgi.d +stats = /run/uwsgi/stats.sock +chmod-socket = 660 +emperor-tyrant = true +cap = setgid,setuid +``` + +Зададим конфиг для *uwsgi vassal*: +> /etc/uwsgi.d/djing.ini +```ini +[uwsgi] +chdir=/var/www/djing/ +module=djing.wsgi +master=True +processes=8 +;socket=/run/uwsgi/djing.sock +http-socket=:8000 +chmod-socket=664 +pidfile=/run/uwsgi/django-master.pid +vacuum=True +plugin=python3 +``` + +Примените к созданному файлу пользователя http: +> \# chown http:http /etc/uwsgi.d/djing.ini + +Перед пробой запуска отключим все ограничения фаервола: +> \# systemctl stop firewalld + +Или даже отключить, если вы отложите настройку *firewalld* на потом: +> \# systemctl disable firewalld + +Перед тем как попробовать запустить тестовый сервер скомпилируйте переводы: +> \$ ./manage.py compilemessages -l ru + +Попробуем запустить *uwsgi* и djing без Nginx: +> \# uwsgi --gid http --uid http /etc/uwsgi.d/djing.ini + +пробуем зайти в биллинг с браузера на <адрес сервера>:8000. Вам должен показаться диалог входа в систему: +![Login screenshot](./img/login.png) + +Для того чтоб uwsgi применял к своим файлам пользователя http, надо подредактировать системный юнит uwsgi, у меня он имеет такой путь: +> /usr/lib/systemd/system/uwsgi.service + +В нём надо чтоб chown менял пользователя на http, а не на uwsgi: +> ExecStartPre=/bin/chown -r http:http /run/uwsgi + +Теперь, если всё прошло успешно, поменяйте в конфиге */etc/uwsgi.d/djing.ini* сокет с http на unix socket: +Раскомментируйте это: +> socket=/run/uwsgi/djing.sock + +И закомментируйте эту строку: +> http-socket=:8000 + +Строка *http-socket=:8000* была для теста, чтоб посмотреть работает-ли uwsgi сам по себе. - [uwsgi] - uid = http - gid = http - pidfile = /run/uwsgi/uwsgi.pid - emperor = /etc/uwsgi.d - stats = /run/uwsgi/stats.sock - chmod-socket = 660 - emperor-tyrant = true - cap = setgid,setuid +Теперь можно попробовать запустить *nginx* и *uwsgi*. Ставим в **djing/settings.py** опцию **DEBUG = False**, и пробуем запустить нужные юниты: -У меня конфиг лежит по адресу /etc/uwsgi.ini +> \# systemctl start uwsgi\ +> \# systemctl start nginx +По умолчанию на fedora включено SELinux и вы не сможете зайти на сайт пока не настроите его. Для того, чтоб проверить всё +ли правильно мы настроили, отключите *SELinux* коммандой **setenforce 0* и попробуйте зайти. После успешного запуска вы +можете снова включить опцию и настроить её. + + +### Настраиваем биллинг +Все настройки биллинга находятся в файле *djing/settings.py*. Большинство опций вы можете найти в документации +[Django settings](https://docs.djangoproject.com/en/1.9/ref/settings). +Те опции, которые были добавлены мной в рамках проекта *djing*, описаны ниже в этом разделе документации по установке. + +#### djing/settings.py +**USE_TZ** — Это опция *Django*, но если вы не работаете в разных часовых диапазонах то я не рекомендую включать +эту опцию чтоб небыло путаницы со временем. Это связано с тем что я ещё не тестировал поведение работы со временем при +включённой опции *USE_TZ*. + +**ALLOWED_HOSTS** — Тоже опция *Django*, но важная для безопасности, укажите в списке возможные имена вашего сервера. +Подробнее в документации [Django settings](https://docs.djangoproject.com/en/1.9/ref/settings/#allowed-hosts). + +**DEFAULT_PICTURE** — Это путь к изображению по умолчанию, оно используется когда нужное изображение не найдено. + +**PAGINATION_ITEMS_PER_PAGE** — Количество выводимых элементов списка на странце с таблицей. Например, если поставить 30, +то на странице абонентов на одной странице будет выведено 30 строк абонентов. + +**pay_SERV_ID** — Эта опция, так же как и **pay_SECRET** опции для платёжной системы *AllTime24*, если вы используете любую +другую платёжную систему то можете удалить эти опции. + +**DIALING_MEDIA** — Путь, где биллинг сможет найти файлы записей asterisk чтоб вывести статистику звонков. +Подробнее читайте в описании работы с [АТС](./ats.ms). + +**DHCP_TIMEOUT** — Время аренды настроек DHCP в секундах. + +**DEFAULT_SNMP_PASSWORD** — Пароль snmp по умолчанию для устройств, чтоб при создании устройства он был заполнен в нужном поле. +Если нет такого пароля то оставьте пустым или None. + +**TELEPHONE_REGEXP** — Регулярное выражение для валидации номера телефона. + + +#### Создание БД +Подразумевается что сервер баз данных у вас уже есть, или вы его можете установить сами. +В конфиге настроить БД можно по инструкции [Django databases](https://docs.djangoproject.com/en/1.9/ref/settings/#databases). + +Убедитесть что вы в папке с проектом, комманда **pwd** должна выдать */var/www/djing*. +Чтоб создать бд, как описано в документации [Django admin \& migrate](https://docs.djangoproject.com/en/1.9/ref/django-admin/#migrate), +нужно запустить **./manage.py migrate** чтоб создать структуру БД. Вывод будет примерно таким: +``` +$ ./manage.py migrate +Operations to perform: + Apply all migrations: mapapp, contenttypes, dialing_app, django_messages, taskapp, photo_app, accounts_app, devapp, statistics, tariff_app, admin, sessions, chatbot, auth, abonapp +Running migrations: + Rendering model states... DONE + Applying mapapp.0001_initial... OK + Applying devapp.0001_initial... OK + Applying devapp.0002_auto_20160909_1018... OK + Applying devapp.0003_device_map_dot... OK + Applying photo_app.0001_initial... OK + Applying contenttypes.0001_initial... OK +... + Applying taskapp.0012_auto_20170407_0124... OK + Applying taskapp.0013_auto_20170413_1944... OK + Applying taskapp.0014_auto_20170416_1029... OK + Applying taskapp.0015_auto_20170816_1109... OK +``` + +После этого вам стоит создать супер пользователя чтоб зайти в систему. +``` +$ ./manage.py createsuperuser +``` +В интерактивном режиме ответьте на вопросы. +``` +$ ./manage.py createsuperuser +Username: username +Telephone: +12223334455 +Password: +Password (again): +Superuser created successfully. +``` +Обратите внимание на то что номер телефона это обязательное поле для заполнения. +Если у вас не выходит указать номер телефона, то проверте чтоб ваш телефон соответствовал регулярному выражению **^\+[7,8,9,3]\d{10,11}$**. +Если регулярное выражение вам не подхожит, то вы можете изенить его в настройках, см. опции в настройках выше. +После изменения настроек они не сразу вступят в силу, нужно перезагрузить код django, для этого перезапустите **uwsgi**: +> \# systemctl restart uwsgi + +Теперь произведите тестовый запуск: +> \# ./manage.py runserver 192.168.0.100:8000 + +Если не подтягивается статика то проверте чтоб опция **DEBUG** в настройках была **True**. + +При условии что адрес вашего сервера *192.168.0.100*, вы сможете открыть биллинг по адресу **http://192.168.0.100:8000/**. +Введите логин и пароль супер пользователя которого вы создали по инструкции выше. + +Если вы успешно зашли то можно пробовать запускать биллинг в рабочую обстановку. +В настройках смените переменную **DEBUG** на **False** и перезапустите *uwsgi*. ### Настраиваем демоны -Если ваша система работает с поддержкой *systemd* то в каталоге *systemd_units* проекта вы найдёте юниты для systemd. +Если ваша система работает с поддержкой [**systemd**](https://www.freedesktop.org/wiki/Software/systemd/) то в каталоге *systemd_units* проекта вы найдёте юниты для systemd. Скопируйте их в каталог юнитов systemd, у меня это путь */etc/systemd/system*. __Настоятельно рекомендую заглянуть внутрь этих юнитов__. Проверте пути исполняемых файлов, права и прочее. +Для запуска сервиса **djing_rotate.service** вам нужно сначала настроить сбор статистики по [netflow](./netflow.md). + +Перед включением юнита *djing_telebot.service* создайте Telegram бота и впишите в файл *djing/settings.py* в переменную *TELEGRAM_BOT_TOKEN* токен вашего бота. +С помощью этого бота вы будете получать различные сообщения из биллинга. Подробнее в инструкции к [модулю оповещений](./bot.md). + А теперь включим и запустим нужные демоны -``` +```shell # systemctl daemon-reload # systemctl enable djing_queue.service # systemctl start djing_queue.service @@ -120,5 +288,3 @@ __Настоятельно рекомендую заглянуть внутрь # systemctl enable djing_telebot.service # systemctl start djing_telebot.service ``` -Перед включением юнита *djing_telebot.service* создайте Telegram бота и впишите в файл *djing/settings.py* в переменную *TELEGRAM_BOT_TOKEN* токен вашего бота. -С помощью этого бота вы будете получать различные сообщения из биллинга. Подробнее в инструкции к [модулю оповещений](./docs/bot.md). diff --git a/mydefs.py b/mydefs.py index 5bd45fa..bbf0d1a 100644 --- a/mydefs.py +++ b/mydefs.py @@ -10,9 +10,12 @@ from django.http import HttpResponse, Http404, HttpResponseRedirect from django.shortcuts import redirect from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage from django.db import models -from djing.settings import PAGINATION_ITEMS_PER_PAGE, DEBUG +from django.conf import settings +PAGINATION_ITEMS_PER_PAGE = getattr(settings, 'PAGINATION_ITEMS_PER_PAGE', 10) +DEBUG = getattr(settings, 'DEBUG', False) + ip_addr_regex = r'^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' diff --git a/photo_app/models.py b/photo_app/models.py index abcd26f..6d0f8c6 100644 --- a/photo_app/models.py +++ b/photo_app/models.py @@ -5,8 +5,9 @@ import hashlib from django.db import models from PIL import Image +from django.conf import settings -from djing.settings import BASE_DIR +BASE_DIR = getattr(settings, 'BASE_DIR', '.') class Photo(models.Model): diff --git a/requirements.txt b/requirements.txt index c68afe4..7d485d0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,16 @@ Django==1.9 Pillow telepot + # for mac address field netaddr + # for testing required xmltodict xmltodict -PyMySQL + +# Django recommended mysql client +mysqlclient + easysnmp rq pid diff --git a/setup.py b/setup.py deleted file mode 100644 index 34eb9c3..0000000 --- a/setup.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env python3 -import os -import sys