From dddc230f033c1fe15ce611b2dc47c30942186e54 Mon Sep 17 00:00:00 2001 From: EDeev Date: Thu, 9 Apr 2026 13:56:41 +0300 Subject: [PATCH] lab-2: docker compose, nginx, answers --- lab-1/http.restbook | 1 + lab-1/lab1/__init__.py | 0 lab-1/lab1/asgi.py | 16 +++ lab-1/lab1/settings.py | 132 +++++++++++++++++ lab-1/lab1/urls.py | 13 ++ lab-1/lab1/wsgi.py | 16 +++ lab-1/manage.py | 22 +++ lab-1/quiz/__init__.py | 0 lab-1/quiz/admin.py | 4 + lab-1/quiz/apps.py | 5 + lab-1/quiz/gentestdata.py | 18 +++ lab-1/quiz/migrations/0001_initial.py | 26 ++++ lab-1/quiz/migrations/__init__.py | 0 lab-1/quiz/models.py | 15 ++ lab-1/quiz/serializers.py | 7 + lab-1/quiz/tests.py | 3 + lab-1/quiz/views.py | 9 ++ lab-1/requirements.txt | 7 + lab-2/DOCKER_GUIDE.md | 79 +++++++++++ lab-2/http.restbook | 1 + lab-2/local/backend/Dockerfile | 11 ++ lab-2/local/backend/lab1/__init__.py | 0 lab-2/local/backend/lab1/asgi.py | 16 +++ lab-2/local/backend/lab1/settings.py | 133 ++++++++++++++++++ lab-2/local/backend/lab1/urls.py | 13 ++ lab-2/local/backend/lab1/wsgi.py | 16 +++ lab-2/local/backend/manage.py | 22 +++ lab-2/local/backend/quiz/__init__.py | 0 lab-2/local/backend/quiz/admin.py | 4 + lab-2/local/backend/quiz/apps.py | 5 + lab-2/local/backend/quiz/gentestdata.py | 18 +++ .../backend/quiz/migrations/0001_initial.py | 26 ++++ .../local/backend/quiz/migrations/__init__.py | 0 lab-2/local/backend/quiz/models.py | 15 ++ lab-2/local/backend/quiz/serializers.py | 7 + lab-2/local/backend/quiz/tests.py | 3 + lab-2/local/backend/quiz/views.py | 9 ++ lab-2/local/backend/requirements.txt | 8 ++ lab-2/local/docker-compose.yaml | 45 ++++++ lab-2/local/nginx/Dockerfile | 3 + .../nginx/templates/default.conf.template | 27 ++++ lab-2/web_lite/docker-compose.yaml | 27 ++++ lab-2/web_pg/docker-compose.yaml | 44 ++++++ 43 files changed, 826 insertions(+) create mode 100644 lab-1/http.restbook create mode 100644 lab-1/lab1/__init__.py create mode 100644 lab-1/lab1/asgi.py create mode 100644 lab-1/lab1/settings.py create mode 100644 lab-1/lab1/urls.py create mode 100644 lab-1/lab1/wsgi.py create mode 100644 lab-1/manage.py create mode 100644 lab-1/quiz/__init__.py create mode 100644 lab-1/quiz/admin.py create mode 100644 lab-1/quiz/apps.py create mode 100644 lab-1/quiz/gentestdata.py create mode 100644 lab-1/quiz/migrations/0001_initial.py create mode 100644 lab-1/quiz/migrations/__init__.py create mode 100644 lab-1/quiz/models.py create mode 100644 lab-1/quiz/serializers.py create mode 100644 lab-1/quiz/tests.py create mode 100644 lab-1/quiz/views.py create mode 100644 lab-1/requirements.txt create mode 100644 lab-2/DOCKER_GUIDE.md create mode 100644 lab-2/http.restbook create mode 100644 lab-2/local/backend/Dockerfile create mode 100644 lab-2/local/backend/lab1/__init__.py create mode 100644 lab-2/local/backend/lab1/asgi.py create mode 100644 lab-2/local/backend/lab1/settings.py create mode 100644 lab-2/local/backend/lab1/urls.py create mode 100644 lab-2/local/backend/lab1/wsgi.py create mode 100644 lab-2/local/backend/manage.py create mode 100644 lab-2/local/backend/quiz/__init__.py create mode 100644 lab-2/local/backend/quiz/admin.py create mode 100644 lab-2/local/backend/quiz/apps.py create mode 100644 lab-2/local/backend/quiz/gentestdata.py create mode 100644 lab-2/local/backend/quiz/migrations/0001_initial.py create mode 100644 lab-2/local/backend/quiz/migrations/__init__.py create mode 100644 lab-2/local/backend/quiz/models.py create mode 100644 lab-2/local/backend/quiz/serializers.py create mode 100644 lab-2/local/backend/quiz/tests.py create mode 100644 lab-2/local/backend/quiz/views.py create mode 100644 lab-2/local/backend/requirements.txt create mode 100644 lab-2/local/docker-compose.yaml create mode 100644 lab-2/local/nginx/Dockerfile create mode 100644 lab-2/local/nginx/templates/default.conf.template create mode 100644 lab-2/web_lite/docker-compose.yaml create mode 100644 lab-2/web_pg/docker-compose.yaml diff --git a/lab-1/http.restbook b/lab-1/http.restbook new file mode 100644 index 0000000..6b42869 --- /dev/null +++ b/lab-1/http.restbook @@ -0,0 +1 @@ +[{"kind":2,"language":"rest-book","value":"GET http://localhost:8000/api/quiz/","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:20:40 GMT","Content-Type":"application/json","Content-Length":"37","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{}},"data":{"quiz":"http://localhost/api/quiz/"}}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:20:40 GMT","Content-Type":"application/json","Content-Length":"37","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{}},"data":{"quiz":"http://localhost/api/quiz/"}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":2,"language":"rest-book","value":"POST http://localhost:8000/api/quiz/\r\nContent-Type: application/json\r\n\r\n{\r\n \"title\": \"Название\",\r\n \"description\": \"Описание\",\r\n \"author\": \"Автор\",\r\n \"time_limit\": 40,\r\n \"is_published\": false\r\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":405,"statusText":"Method Not Allowed","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:20:56 GMT","Content-Type":"application/json","Content-Length":"55","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":120}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Название","description":"Описание","author":"Автор","time_limit":40,"is_published":false}},"data":{"detail":"Метод \"POST\" не разрешен."}}},{"mime":"text/x-json","value":{"status":405,"statusText":"Method Not Allowed","headers":{"Allow":"GET, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:20:56 GMT","Content-Type":"application/json","Content-Length":"55","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":120}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Название","description":"Описание","author":"Автор","time_limit":40,"is_published":false}},"data":{"detail":"Метод \"POST\" не разрешен."}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":2,"language":"rest-book","value":"GET http://localhost:8000/api/quiz/8/","outputs":[{"mime":"application/vnd.code.notebook.error","value":{"name":"Error","message":"{}"}}]},{"kind":2,"language":"rest-book","value":"PUT http://localhost:8000/api/quiz/8/\r\nContent-Type: application/json\r\n\r\n{\r\n \"title\": \"Новое Название\",\r\n \"description\": \"Новое Описание\",\r\n \"author\": \"Новый Автор\",\r\n \"time_limit\": 40,\r\n \"is_published\": true\r\n}","outputs":[{"mime":"application/vnd.code.notebook.error","value":{"name":"Error","message":"{}"}}]},{"kind":2,"language":"rest-book","value":"DELETE http://localhost:8000/api/quiz/8/","outputs":[{"mime":"application/vnd.code.notebook.error","value":{"name":"Error","message":"{}"}}]}] \ No newline at end of file diff --git a/lab-1/lab1/__init__.py b/lab-1/lab1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-1/lab1/asgi.py b/lab-1/lab1/asgi.py new file mode 100644 index 0000000..1351cca --- /dev/null +++ b/lab-1/lab1/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for lab1 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/6.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lab1.settings') + +application = get_asgi_application() diff --git a/lab-1/lab1/settings.py b/lab-1/lab1/settings.py new file mode 100644 index 0000000..b0a6aa3 --- /dev/null +++ b/lab-1/lab1/settings.py @@ -0,0 +1,132 @@ +""" +Django settings for lab1 project. + +Generated by 'django-admin startproject' using Django 6.0.3. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/6.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure--xqwhdo2$)n*y3yk=d*vmwx6nuwt8h=m5@_og_uzj+11^hu7))' + +# 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', + + 'rest_framework', + 'quiz', +] + +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 = 'lab1.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'lab1.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/6.0/ref/settings/#databases + +import os + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('POSTGRES_DB', 'lab_db'), + 'USER': os.environ.get('POSTGRES_USER', 'django'), + 'PASSWORD': os.environ.get('POSTGRES_PASSWORD', '03042004'), + 'HOST': os.environ.get('POSTGRES_HOST', 'localhost'), + 'PORT': os.environ.get('POSTGRES_PORT', 5432), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/6.0/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/6.0/topics/i18n/ + +LANGUAGE_CODE = 'ru-ru' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/6.0/howto/static-files/ + +STATIC_URL = 'static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' + +# Default primary key field type +# https://docs.djangoproject.com/en/6.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/lab-1/lab1/urls.py b/lab-1/lab1/urls.py new file mode 100644 index 0000000..e617532 --- /dev/null +++ b/lab-1/lab1/urls.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from django.urls import path, include +from rest_framework import routers +from quiz.views import QuizViewSet + +router = routers.DefaultRouter() +router.register(r'quiz', QuizViewSet) + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include(router.urls)), +] + diff --git a/lab-1/lab1/wsgi.py b/lab-1/lab1/wsgi.py new file mode 100644 index 0000000..4716070 --- /dev/null +++ b/lab-1/lab1/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for lab1 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/6.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lab1.settings') + +application = get_wsgi_application() diff --git a/lab-1/manage.py b/lab-1/manage.py new file mode 100644 index 0000000..cfc0053 --- /dev/null +++ b/lab-1/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', 'lab1.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/lab-1/quiz/__init__.py b/lab-1/quiz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-1/quiz/admin.py b/lab-1/quiz/admin.py new file mode 100644 index 0000000..3847636 --- /dev/null +++ b/lab-1/quiz/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from .models import Quiz + +admin.site.register(Quiz) diff --git a/lab-1/quiz/apps.py b/lab-1/quiz/apps.py new file mode 100644 index 0000000..eaa49da --- /dev/null +++ b/lab-1/quiz/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class QuizConfig(AppConfig): + name = 'quiz' diff --git a/lab-1/quiz/gentestdata.py b/lab-1/quiz/gentestdata.py new file mode 100644 index 0000000..cd9d892 --- /dev/null +++ b/lab-1/quiz/gentestdata.py @@ -0,0 +1,18 @@ +import random +from .models import Quiz +from django.db import transaction +from faker import Faker + +fk = Faker() + +def gentestdata(): + with transaction.atomic(): + for i in range(100): + new_quiz = Quiz() + new_quiz.title = fk.sentence(nb_words=4) + new_quiz.description = fk.paragraph() + new_quiz.author = fk.name() + new_quiz.time_limit = random.randint(5, 60) + new_quiz.is_published = random.random() > 0.5 + new_quiz.save() + print('Сделана генерация') diff --git a/lab-1/quiz/migrations/0001_initial.py b/lab-1/quiz/migrations/0001_initial.py new file mode 100644 index 0000000..6e07c29 --- /dev/null +++ b/lab-1/quiz/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 6.0.3 on 2026-03-05 08:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Quiz', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('description', models.TextField()), + ('author', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('time_limit', models.IntegerField(help_text='Ограничение по времени в минутах')), + ('is_published', models.BooleanField(default=False)), + ], + ), + ] diff --git a/lab-1/quiz/migrations/__init__.py b/lab-1/quiz/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-1/quiz/models.py b/lab-1/quiz/models.py new file mode 100644 index 0000000..3936c59 --- /dev/null +++ b/lab-1/quiz/models.py @@ -0,0 +1,15 @@ +from django.db import models + +# Create your models here. + +class Quiz(models.Model): + title = models.CharField(max_length=200) + description = models.TextField() + author = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + time_limit = models.IntegerField(help_text='Ограничение по времени в минутах') + is_published = models.BooleanField(default=False) + + def __str__(self): + return self.title + diff --git a/lab-1/quiz/serializers.py b/lab-1/quiz/serializers.py new file mode 100644 index 0000000..08dac96 --- /dev/null +++ b/lab-1/quiz/serializers.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from .models import Quiz + +class QuizSerializer(serializers.ModelSerializer): + class Meta: + model = Quiz + fields = '__all__' \ No newline at end of file diff --git a/lab-1/quiz/tests.py b/lab-1/quiz/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/lab-1/quiz/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/lab-1/quiz/views.py b/lab-1/quiz/views.py new file mode 100644 index 0000000..a7c4b53 --- /dev/null +++ b/lab-1/quiz/views.py @@ -0,0 +1,9 @@ +from django.shortcuts import render + +from rest_framework import viewsets +from .models import Quiz +from .serializers import QuizSerializer + +class QuizViewSet(viewsets.ModelViewSet): + queryset = Quiz.objects.all() + serializer_class = QuizSerializer diff --git a/lab-1/requirements.txt b/lab-1/requirements.txt new file mode 100644 index 0000000..1f33642 --- /dev/null +++ b/lab-1/requirements.txt @@ -0,0 +1,7 @@ +djangorestframework +markdown +django-filter +django-cors-headers +psycopg[binary,pool] +faker +gunicorn diff --git a/lab-2/DOCKER_GUIDE.md b/lab-2/DOCKER_GUIDE.md new file mode 100644 index 0000000..19423a6 --- /dev/null +++ b/lab-2/DOCKER_GUIDE.md @@ -0,0 +1,79 @@ +# Docker Lab 2 + +Авторизация в реестре: `docker login dcr.deev.su` + +## 1. Запуск локально (lab-2/local) + +```bash +cd lab-2/local +docker compose up --build + +docker compose up # Запуск +docker compose down # Остановка +``` + +После запуска: +- API: http://localhost/api/quiz/ +- Админка: http://localhost/admin/ + + +## 2. Запуск из реестра с внешней БД (lab-2/web_lite) + +Образы с `dcr.deev.su`. PostgreSQL на `95.31.185.229`. + +```bash +cd lab-2/web_lite +docker compose pull +docker compose up + +docker compose down # Остановить +``` + +После запуска: +- API: http://localhost/api/quiz/ +- Админка: http://localhost/admin/ + + +## 3. Запуск из реестра с локальной БД (lab-2/web_pg) + +Образы с `dcr.deev.su`. + +```bash +cd lab-2/web_pg +docker compose pull +docker compose up + +docker compose down # Остановить +``` + +После запуска: +- API: http://localhost/api/quiz/ +- Админка: http://localhost/admin/ + + +## Обновление образов в реестре + +### После обновления образов +```bash +cd lab-2/web_pg +docker compose pull +docker compose up + +cd lab-2/web_lite +docker compose pull +docker compose up +``` + +### Backend +```bash +cd lab-2/local +docker build -t dcr.deev.su/edeev/lab2-aad/backend:latest ./backend +docker push dcr.deev.su/edeev/lab2-aad/backend:latest +``` + +### Nginx +```bash +cd lab-2/local +docker build -t dcr.deev.su/edeev/lab2-aad/nginx:latest ./nginx +docker push dcr.deev.su/edeev/lab2-aad/nginx:latest +``` \ No newline at end of file diff --git a/lab-2/http.restbook b/lab-2/http.restbook new file mode 100644 index 0000000..7909112 --- /dev/null +++ b/lab-2/http.restbook @@ -0,0 +1 @@ +[{"kind":2,"language":"rest-book","value":"GET http://localhost/admin/","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Date":"Thu, 02 Apr 2026 11:35:15 GMT","Expires":"Thu, 02 Apr 2026 11:35:15 GMT","Cache-Control":"max-age=0, no-cache, no-store, must-revalidate, private","Content-Type":"text/html; charset=utf-8","Content-Length":"4281","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Set-Cookie":["csrftoken=tyWyIaQrKh39ticGRqa5ufUwAeZVeEAg; expires=Thu, 01 Apr 2027 11:35:15 GMT; Max-Age=31449600; Path=/; SameSite=Lax"],"Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/admin/login/?next=/admin/","timeout":10000,"headers":{}},"data":"\n\n\n\nВойти | Административный сайт Django\n\n\n \n \n\n\n \n \n\n\n\n\n\n\n\n \n \n \n\n\n\n\n\nК основному\n\n
\n\n \n \n \n
\n
\n \n\n\n \n\n\n\n\n
\n \n \n
\n \n \n \n \n\n
\n \n \n \n
\n \n \n \n \n
\n \n \n \n \n\n\n\n\n
\n\n\n\n
\n
\n \n \n
\n
\n \n \n \n
\n \n \n
\n \n
\n
\n\n
\n\n \n
\n
\n \n
\n
\n
\n
\n\n\n\n\n \n \n \n\n\n\n\n\n"}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Date":"Thu, 02 Apr 2026 11:35:15 GMT","Expires":"Thu, 02 Apr 2026 11:35:15 GMT","Cache-Control":"max-age=0, no-cache, no-store, must-revalidate, private","Content-Type":"text/html; charset=utf-8","Content-Length":"4281","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Set-Cookie":["csrftoken=tyWyIaQrKh39ticGRqa5ufUwAeZVeEAg; expires=Thu, 01 Apr 2027 11:35:15 GMT; Max-Age=31449600; Path=/; SameSite=Lax"],"Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/admin/login/?next=/admin/","timeout":10000,"headers":{}},"data":"\n\n\n\nВойти | Административный сайт Django\n\n\n \n \n\n\n \n \n\n\n\n\n\n\n\n \n \n \n\n\n\n\n\nК основному\n\n
\n\n \n \n \n
\n
\n \n\n\n \n\n\n\n\n
\n \n \n
\n \n \n \n \n\n
\n \n \n \n
\n \n \n \n \n
\n \n \n \n \n\n\n\n\n
\n\n\n\n
\n
\n \n \n
\n
\n \n \n \n
\n \n \n
\n \n
\n
\n\n
\n\n \n
\n
\n \n
\n
\n
\n
\n\n\n\n\n \n \n \n\n\n\n\n\n"}},{"mime":"text/html","value":"\n\n\n\nВойти | Административный сайт Django\n\n\n \n \n\n\n \n \n\n\n\n\n\n\n\n \n \n \n\n\n\n\n\nК основному\n\n
\n\n \n \n \n
\n
\n \n\n\n \n\n\n\n\n
\n \n \n
\n \n \n \n \n\n
\n \n \n \n
\n \n \n \n \n
\n \n \n \n \n\n\n\n\n
\n\n\n\n
\n
\n \n \n
\n
\n \n \n \n
\n \n \n
\n \n
\n
\n\n
\n\n \n
\n
\n \n
\n
\n
\n
\n\n\n\n\n \n \n \n\n\n\n\n\n"}]},{"kind":2,"language":"rest-book","value":"GET http://localhost/api/quiz/","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, POST, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:15 GMT","Content-Type":"application/json","Content-Length":"546","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{}},"data":[{"id":4,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:34:52.936636Z","time_limit":40,"is_published":false},{"id":5,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:35:11.663629Z","time_limit":40,"is_published":false},{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}]}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, POST, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:15 GMT","Content-Type":"application/json","Content-Length":"546","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{}},"data":[{"id":4,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:34:52.936636Z","time_limit":40,"is_published":false},{"id":5,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:35:11.663629Z","time_limit":40,"is_published":false},{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}]}},{"mime":"text/html","value":"[object Object],[object Object],[object Object]"}]},{"kind":2,"language":"rest-book","value":"POST http://localhost/api/quiz/\r\nContent-Type: application/json\r\n\r\n{\r\n \"title\": \"Название\",\r\n \"description\": \"Описание\",\r\n \"author\": \"Автор\",\r\n \"time_limit\": 40,\r\n \"is_published\": false\r\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":201,"statusText":"Created","headers":{"Allow":"GET, POST, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:11 GMT","Content-Type":"application/json","Content-Length":"170","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":120}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Название","description":"Описание","author":"Автор","time_limit":40,"is_published":false}},"data":{"id":5,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:35:11.663629Z","time_limit":40,"is_published":false}}},{"mime":"text/x-json","value":{"status":201,"statusText":"Created","headers":{"Allow":"GET, POST, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:11 GMT","Content-Type":"application/json","Content-Length":"170","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":120}},"request":{"method":"POST","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Название","description":"Описание","author":"Автор","time_limit":40,"is_published":false}},"data":{"id":5,"title":"Название","description":"Описание","author":"Автор","created_at":"2026-04-02T11:35:11.663629Z","time_limit":40,"is_published":false}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":2,"language":"rest-book","value":"GET http://localhost/api/quiz/3/","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:14 GMT","Content-Type":"application/json","Content-Length":"202","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/3/","timeout":10000,"headers":{}},"data":{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:14 GMT","Content-Type":"application/json","Content-Length":"202","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"GET","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/3/","timeout":10000,"headers":{}},"data":{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":2,"language":"rest-book","value":"PUT http://localhost/api/quiz/3/\r\nContent-Type: application/json\r\n\r\n{\r\n \"title\": \"Новое Название\",\r\n \"description\": \"Новое Описание\",\r\n \"author\": \"Новый Автор\",\r\n \"time_limit\": 40,\r\n \"is_published\": true\r\n}","outputs":[{"mime":"x-application/rest-book","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:13 GMT","Content-Type":"application/json","Content-Length":"202","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":152}},"request":{"method":"PUT","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/3/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","time_limit":40,"is_published":true}},"data":{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}}},{"mime":"text/x-json","value":{"status":200,"statusText":"OK","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:13 GMT","Content-Type":"application/json","Content-Length":"202","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json","User-Agent":"axios/0.21.4","Content-Length":152}},"request":{"method":"PUT","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/3/","timeout":10000,"headers":{"Content-Type":"application/json"},"data":{"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","time_limit":40,"is_published":true}},"data":{"id":3,"title":"Новое Название","description":"Новое Описание","author":"Новый Автор","created_at":"2026-04-02T11:34:33.081567Z","time_limit":40,"is_published":true}}},{"mime":"text/html","value":"[object Object]"}]},{"kind":2,"language":"rest-book","value":"DELETE http://localhost/api/quiz/2/","outputs":[{"mime":"x-application/rest-book","value":{"status":204,"statusText":"No Content","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:12 GMT","Content-Length":"0","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"DELETE","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/2/","timeout":10000,"headers":{}},"data":""}},{"mime":"text/x-json","value":{"status":204,"statusText":"No Content","headers":{"Allow":"GET, PUT, PATCH, DELETE, HEAD, OPTIONS","Date":"Thu, 02 Apr 2026 11:35:12 GMT","Content-Length":"0","Server":"nginx/1.29.7","X-Frame-Options":"DENY","Connection":"close"},"config":{"timeout":10000,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","headers":{"Accept":"application/json, text/plain, */*","User-Agent":"axios/0.21.4"}},"request":{"method":"DELETE","httpVersion":"1.1","responseUrl":"http://localhost/api/quiz/2/","timeout":10000,"headers":{}},"data":""}},{"mime":"text/html","value":""}]}] \ No newline at end of file diff --git a/lab-2/local/backend/Dockerfile b/lab-2/local/backend/Dockerfile new file mode 100644 index 0000000..3eec765 --- /dev/null +++ b/lab-2/local/backend/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.12-slim + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . diff --git a/lab-2/local/backend/lab1/__init__.py b/lab-2/local/backend/lab1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-2/local/backend/lab1/asgi.py b/lab-2/local/backend/lab1/asgi.py new file mode 100644 index 0000000..1351cca --- /dev/null +++ b/lab-2/local/backend/lab1/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for lab1 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/6.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lab1.settings') + +application = get_asgi_application() diff --git a/lab-2/local/backend/lab1/settings.py b/lab-2/local/backend/lab1/settings.py new file mode 100644 index 0000000..91da8aa --- /dev/null +++ b/lab-2/local/backend/lab1/settings.py @@ -0,0 +1,133 @@ +""" +Django settings for lab1 project. + +Generated by 'django-admin startproject' using Django 6.0.3. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/6.0/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure--xqwhdo2$)n*y3yk=d*vmwx6nuwt8h=m5@_og_uzj+11^hu7))' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] +CSRF_TRUSTED_ORIGINS = ['http://localhost'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + 'rest_framework', + 'quiz', +] + +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 = 'lab1.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'lab1.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/6.0/ref/settings/#databases + +import os + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': os.environ.get('POSTGRES_DB', 'lab_db'), + 'USER': os.environ.get('POSTGRES_USER', 'django'), + 'PASSWORD': os.environ.get('POSTGRES_PASSWORD', 'password'), + 'HOST': os.environ.get('POSTGRES_HOST', 'postgres_service'), + 'PORT': os.environ.get('POSTGRES_PORT', 5432), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/6.0/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/6.0/topics/i18n/ + +LANGUAGE_CODE = 'ru-ru' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/6.0/howto/static-files/ + +STATIC_URL = 'static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' + +# Default primary key field type +# https://docs.djangoproject.com/en/6.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/lab-2/local/backend/lab1/urls.py b/lab-2/local/backend/lab1/urls.py new file mode 100644 index 0000000..e617532 --- /dev/null +++ b/lab-2/local/backend/lab1/urls.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from django.urls import path, include +from rest_framework import routers +from quiz.views import QuizViewSet + +router = routers.DefaultRouter() +router.register(r'quiz', QuizViewSet) + +urlpatterns = [ + path('admin/', admin.site.urls), + path('api/', include(router.urls)), +] + diff --git a/lab-2/local/backend/lab1/wsgi.py b/lab-2/local/backend/lab1/wsgi.py new file mode 100644 index 0000000..4716070 --- /dev/null +++ b/lab-2/local/backend/lab1/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for lab1 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/6.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lab1.settings') + +application = get_wsgi_application() diff --git a/lab-2/local/backend/manage.py b/lab-2/local/backend/manage.py new file mode 100644 index 0000000..cfc0053 --- /dev/null +++ b/lab-2/local/backend/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', 'lab1.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/lab-2/local/backend/quiz/__init__.py b/lab-2/local/backend/quiz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-2/local/backend/quiz/admin.py b/lab-2/local/backend/quiz/admin.py new file mode 100644 index 0000000..3847636 --- /dev/null +++ b/lab-2/local/backend/quiz/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from .models import Quiz + +admin.site.register(Quiz) diff --git a/lab-2/local/backend/quiz/apps.py b/lab-2/local/backend/quiz/apps.py new file mode 100644 index 0000000..eaa49da --- /dev/null +++ b/lab-2/local/backend/quiz/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class QuizConfig(AppConfig): + name = 'quiz' diff --git a/lab-2/local/backend/quiz/gentestdata.py b/lab-2/local/backend/quiz/gentestdata.py new file mode 100644 index 0000000..cd9d892 --- /dev/null +++ b/lab-2/local/backend/quiz/gentestdata.py @@ -0,0 +1,18 @@ +import random +from .models import Quiz +from django.db import transaction +from faker import Faker + +fk = Faker() + +def gentestdata(): + with transaction.atomic(): + for i in range(100): + new_quiz = Quiz() + new_quiz.title = fk.sentence(nb_words=4) + new_quiz.description = fk.paragraph() + new_quiz.author = fk.name() + new_quiz.time_limit = random.randint(5, 60) + new_quiz.is_published = random.random() > 0.5 + new_quiz.save() + print('Сделана генерация') diff --git a/lab-2/local/backend/quiz/migrations/0001_initial.py b/lab-2/local/backend/quiz/migrations/0001_initial.py new file mode 100644 index 0000000..6e07c29 --- /dev/null +++ b/lab-2/local/backend/quiz/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 6.0.3 on 2026-03-05 08:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Quiz', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('description', models.TextField()), + ('author', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('time_limit', models.IntegerField(help_text='Ограничение по времени в минутах')), + ('is_published', models.BooleanField(default=False)), + ], + ), + ] diff --git a/lab-2/local/backend/quiz/migrations/__init__.py b/lab-2/local/backend/quiz/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab-2/local/backend/quiz/models.py b/lab-2/local/backend/quiz/models.py new file mode 100644 index 0000000..3936c59 --- /dev/null +++ b/lab-2/local/backend/quiz/models.py @@ -0,0 +1,15 @@ +from django.db import models + +# Create your models here. + +class Quiz(models.Model): + title = models.CharField(max_length=200) + description = models.TextField() + author = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + time_limit = models.IntegerField(help_text='Ограничение по времени в минутах') + is_published = models.BooleanField(default=False) + + def __str__(self): + return self.title + diff --git a/lab-2/local/backend/quiz/serializers.py b/lab-2/local/backend/quiz/serializers.py new file mode 100644 index 0000000..08dac96 --- /dev/null +++ b/lab-2/local/backend/quiz/serializers.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from .models import Quiz + +class QuizSerializer(serializers.ModelSerializer): + class Meta: + model = Quiz + fields = '__all__' \ No newline at end of file diff --git a/lab-2/local/backend/quiz/tests.py b/lab-2/local/backend/quiz/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/lab-2/local/backend/quiz/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/lab-2/local/backend/quiz/views.py b/lab-2/local/backend/quiz/views.py new file mode 100644 index 0000000..a7c4b53 --- /dev/null +++ b/lab-2/local/backend/quiz/views.py @@ -0,0 +1,9 @@ +from django.shortcuts import render + +from rest_framework import viewsets +from .models import Quiz +from .serializers import QuizSerializer + +class QuizViewSet(viewsets.ModelViewSet): + queryset = Quiz.objects.all() + serializer_class = QuizSerializer diff --git a/lab-2/local/backend/requirements.txt b/lab-2/local/backend/requirements.txt new file mode 100644 index 0000000..c40c3d2 --- /dev/null +++ b/lab-2/local/backend/requirements.txt @@ -0,0 +1,8 @@ +Django +djangorestframework +markdown +django-filter +django-cors-headers +psycopg[binary,pool] +faker +gunicorn diff --git a/lab-2/local/docker-compose.yaml b/lab-2/local/docker-compose.yaml new file mode 100644 index 0000000..d080e0b --- /dev/null +++ b/lab-2/local/docker-compose.yaml @@ -0,0 +1,45 @@ +name: lab2-local + +services: + postgres_service: + # https://hub.docker.com/_/postgres + image: postgres:alpine + env_file: + - .env + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 10s + timeout: 30s + retries: 5 + volumes: + - postgres_data:/var/lib/postgresql/data + + backend_service: + build: ./backend + command: > # python manage.py createsuperuser --noinput && + sh -c "python manage.py migrate && + python manage.py shell -c 'from quiz.gentestdata import gentestdata; gentestdata()' && + python manage.py collectstatic --no-input && + gunicorn lab1.wsgi:application --bind 0.0.0.0:8000" + volumes: + - static_volume:/app/staticfiles + env_file: + - .env + depends_on: + postgres_service: + condition: service_healthy + restart: unless-stopped + + nginx_service: + build: ./nginx + ports: + - "80:80" + volumes: + - static_volume:/staticfiles + depends_on: + - backend_service + restart: unless-stopped + +volumes: + static_volume: + postgres_data: diff --git a/lab-2/local/nginx/Dockerfile b/lab-2/local/nginx/Dockerfile new file mode 100644 index 0000000..8e67fdc --- /dev/null +++ b/lab-2/local/nginx/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:latest + +COPY templates /etc/nginx/templates/ diff --git a/lab-2/local/nginx/templates/default.conf.template b/lab-2/local/nginx/templates/default.conf.template new file mode 100644 index 0000000..0b00f97 --- /dev/null +++ b/lab-2/local/nginx/templates/default.conf.template @@ -0,0 +1,27 @@ +server { + listen 80; + + resolver 127.0.0.11 valid=30s; + + location /static/ { + alias /staticfiles/; + } + + location /admin/ { + set $backend backend_service; + proxy_pass http://$backend:8000$request_uri; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_redirect off; + } + + location /api/ { + set $backend backend_service; + proxy_pass http://$backend:8000$request_uri; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_redirect off; + } +} diff --git a/lab-2/web_lite/docker-compose.yaml b/lab-2/web_lite/docker-compose.yaml new file mode 100644 index 0000000..a757eb2 --- /dev/null +++ b/lab-2/web_lite/docker-compose.yaml @@ -0,0 +1,27 @@ +name: lab2-web-lite + +services: + backend_service: + image: dcr.deev.su/edeev/lab2-aad/backend:latest + command: > + sh -c "python manage.py migrate && + python manage.py collectstatic --no-input && + gunicorn lab1.wsgi:application --bind 0.0.0.0:8000" + volumes: + - static_volume:/app/staticfiles + env_file: + - .env + restart: unless-stopped + + nginx_service: + image: dcr.deev.su/edeev/lab2-aad/nginx:latest + ports: + - "80:80" + volumes: + - static_volume:/staticfiles + depends_on: + - backend_service + restart: unless-stopped + +volumes: + static_volume: diff --git a/lab-2/web_pg/docker-compose.yaml b/lab-2/web_pg/docker-compose.yaml new file mode 100644 index 0000000..2609ab9 --- /dev/null +++ b/lab-2/web_pg/docker-compose.yaml @@ -0,0 +1,44 @@ +name: lab2-web-pg + +services: + postgres_service: + image: postgres:alpine + env_file: + - .env + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 10s + timeout: 30s + retries: 5 + volumes: + - postgres_data:/var/lib/postgresql/data + + backend_service: + image: dcr.deev.su/edeev/lab2-aad/backend:latest + command: > # python manage.py createsuperuser --noinput && + sh -c "python manage.py migrate && + python manage.py shell -c 'from quiz.gentestdata import gentestdata; gentestdata()' && + python manage.py collectstatic --no-input && + gunicorn lab1.wsgi:application --bind 0.0.0.0:8000" + volumes: + - static_volume:/app/staticfiles + env_file: + - .env + depends_on: + postgres_service: + condition: service_healthy + restart: unless-stopped + + nginx_service: + image: dcr.deev.su/edeev/lab2-aad/nginx:latest + ports: + - "80:80" + volumes: + - static_volume:/staticfiles + depends_on: + - backend_service + restart: unless-stopped + +volumes: + static_volume: + postgres_data: