Phased out nginx in favor of running django under daphne with asgi.

Adjusted the docker image in favor of this, & fixed urls.py which was
missing the static request responder.
This commit is contained in:
th3r00t
2020-09-11 16:50:17 -04:00
parent 0391bb94a4
commit 68d453edc8
10 changed files with 80 additions and 161 deletions

8
configure vendored
View File

@@ -29,10 +29,10 @@ def set_secret(config=load_config()):
def init_django_database():
cmds = [
'python manage.py makemigrations',
'python manage.py makemigrations interface',
'python manage.py migrate',
'python manage.py migrate interface',
'python3 manage.py makemigrations',
'python3 manage.py makemigrations interface',
'python3 manage.py migrate',
'python3 manage.py migrate interface',
]
os.chdir("src")
for cmd in cmds:

17
docker/Dockerfile vendored
View File

@@ -12,25 +12,16 @@
FROM ubuntu
EXPOSE 8000
EXPOSE 1337
RUN apt-get update -y
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential python3 python3-dev python3-pip python3-venv nginx-full
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y cron
RUN echo "* * * * * cd /pyshelf/ && python3 importBooks >> /var/log/cron.log 2>&1" > import_books_scheduler.cron && crontab import_books_scheduler.cron
COPY ./docker/pyshelf_nginx.conf /etc/nginx/sites-available/pyshelf_nginx.conf
RUN ln -s /etc/nginx/sites-available/pyshelf_nginx.conf /etc/nginx/sites-enabled/
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential python3 python3-dev python3-pip python3-venv
COPY . /pyshelf
COPY ./docker/config.json /pyshelf/config.json
WORKDIR /pyshelf/
RUN python3 -m pip install -r requirements.txt
RUN python3 configure
WORKDIR /pyshelf/
ENTRYPOINT cron start \
&& python3 configure \
# && python3 importBooks \
&& nginx -g "daemon on;" \
&& uwsgi --ini uwsgi.ini
ENTRYPOINT daphne --root-path=/pyshelf/src/interface frontend.asgi:application

15
docker/config.json vendored
View File

@@ -1 +1,14 @@
{"TITLE": "pyShelf E-Book Server", "VERSION": "Docker", "BOOKPATH": "/books", "DB_HOST": "db", "DB_PORT": "5432", "DATABASE": "pyshelf", "USER": "pyshelf", "PASSWORD": "pyshelf", "BOOKSHELF": "data/shelf.json", "ALLOWED_HOSTS": "*", "SECRET": ""}
{
"TITLE": "pyShelf E-Book Server",
"VERSION": "Docker",
"BOOKPATH": "/books",
"DB_HOST": "db",
"DB_PORT": "5432",
"DATABASE": "pyshelf",
"USER": "pyshelf",
"PASSWORD": "pyshelf",
"BOOKSHELF": "data/shelf.json",
"ALLOWED_HOSTS": "*",
"SECRET": "",
"BUILD_MODE": "production"
}

View File

@@ -23,4 +23,5 @@ jsonpickle
django-widget-tweaks
loguru
ptvsd
pudb
pudb
daphne

View File

@@ -9,8 +9,6 @@ class Config:
"""
Main System Configuration
"""
def __init__(self, root):
"""
Initialize main configuration options
@@ -19,8 +17,10 @@ class Config:
self._fp = "config.json"
self._cp = pathlib.Path.joinpath(root, self._fp)
self._data = self.open_file()
try: self.logger
except AttributeError: self.logger = self.get_logger()
try:
self.logger
except AttributeError:
self.logger = self.get_logger()
self.book_path = self._data["BOOKPATH"]
self.TITLE = self._data["TITLE"]
self.VERSION = self._data["VERSION"]
@@ -37,12 +37,14 @@ class Config:
self.db_user = self._data["USER"]
self.db_pass = self._data["PASSWORD"]
self.SECRET = self._data["SECRET"]
self.debug_build_mode = (self._data["BUILD_MODE"].casefold() == "debug")
self.build_mode = self._data["BUILD_MODE"]
def get_logger(self):
_logger = logger
_logger.add(pathlib.PurePath(self.root, 'data','pyshelf.log'),
rotation="2 MB", enqueue=True, colorize=True)
_logger.add(pathlib.PurePath(self.root, 'data', 'pyshelf.log'),
rotation="2 MB",
enqueue=True,
colorize=True)
return _logger
def open_file(self):

16
src/frontend/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for asgi 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', 'frontend.settings')
application = get_asgi_application()

View File

@@ -21,38 +21,36 @@ CUR_DIR = Path.cwd()
PRG_DIR = CUR_DIR.parts[0:-1]
PRG_DIR = Path(*PRG_DIR)
CONFIG = Config(PRG_DIR)
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = CONFIG.SECRET
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TEMPLATE_DEBUG = CONFIG.debug_build_mode
BUILD_MODE = CONFIG.build_mode
if BUILD_MODE == 'debug':
DEBUG = TEMPLATE_DEBUG = True
else:
DEBUG = TEMPLATE_DEBUG = False
if DEBUG is True:
print("DEBUG build mode is ON")
from pudb.remote import set_trace
else:
print("Production Mode Set")
ALLOWED_HOSTS = CONFIG.allowed_hosts
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"interface",
"interface.templatetags",
"debug_toolbar",
"widget_tweaks"
"django.contrib.admin", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.sessions",
"django.contrib.messages", "django.contrib.staticfiles", "interface",
"interface.templatetags", "debug_toolbar", "widget_tweaks"
]
AUTH_USER_MODEL = "interface.User"
MIDDLEWARE = [
@@ -90,9 +88,9 @@ TEMPLATES = [
},
]
WSGI_APPLICATION = "frontend.wsgi.application"
# WSGI_APPLICATION = "frontend.wsgi.application"
WSGI_APPLICATION = 'asgi.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
@@ -115,14 +113,23 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
"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",
},
{"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/2.2/topics/i18n/
@@ -136,7 +143,6 @@ USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
LOGIN_REDIRECT_URL = 'home'

View File

@@ -19,6 +19,7 @@ from django.contrib.auth import views as auth_views
from django.contrib.auth.models import User
from django.shortcuts import HttpResponse
from django.urls import include, path, re_path
from django.conf.urls.static import static
from interface import views
urlpatterns = [
@@ -67,7 +68,7 @@ urlpatterns = [
auth_views.PasswordResetCompleteView.as_view(),
name='password_reset_complete',
),
]
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:
import debug_toolbar

View File

@@ -1,111 +0,0 @@
# Generated by Django 3.0.7 on 2020-07-23 16:01
from django.conf import settings
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0011_update_proxy_permissions'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('facebook', models.CharField(max_length=255, null=True)),
('twitter', models.CharField(max_length=255, null=True)),
('ulvl', models.IntegerField(default=1)),
('sponsorid', models.IntegerField(null=True)),
('matrixid', models.CharField(max_length=255, null=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.CreateModel(
name='Books',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.TextField()),
('author', models.CharField(max_length=255, null=True)),
('categories', models.TextField(null=True)),
('cover', models.BinaryField(editable=True, null=True)),
('pages', models.IntegerField(null=True)),
('progress', models.IntegerField(null=True)),
('file_name', models.TextField()),
('description', models.TextField(null=True)),
('identifier', models.CharField(max_length=255, null=True)),
('publisher', models.TextField(null=True)),
('date', models.DateField(null=True)),
('rights', models.CharField(max_length=255, null=True)),
('tags', models.TextField(null=True)),
],
options={
'db_table': 'books',
},
),
migrations.CreateModel(
name='Navigation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('link', models.CharField(max_length=255, null=True)),
('category', models.CharField(max_length=255, null=True)),
('parent_id', models.IntegerField(null=True)),
('alt', models.CharField(max_length=255, null=True)),
('type', models.IntegerField(null=True)),
('socket', models.CharField(max_length=255)),
],
options={
'db_table': 'navigation',
},
),
migrations.CreateModel(
name='Favorites',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('book', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='interface.Books')),
('user', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'favorites',
},
),
migrations.CreateModel(
name='Collections',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('collection', models.CharField(max_length=255)),
('book_id', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='interface.Books')),
],
options={
'db_table': 'collections',
},
),
]