Compare commits

...

6 commits

41 changed files with 205 additions and 1646 deletions

View file

@ -1,4 +1,4 @@
FROM python:3.11.9-bookworm FROM python:3.13.0-bullseye
ENV PYTHONBUFFERED=1 ENV PYTHONBUFFERED=1
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
@ -6,12 +6,8 @@ ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /code WORKDIR /code
COPY . /code/ COPY . /code/
ADD . /code/ ADD . /code/
COPY start.sh /code/ COPY scripts/ /code/scripts/
RUN chmod +x /code/start.sh RUN chmod +x /code/scripts/start.sh
# Fix permissions with /tmp
RUN chown root:root /tmp
RUN chmod 1777 /tmp
# Install packages # Install packages
RUN apt update && apt install -y graphviz libgraphviz-dev graphviz-dev wget zip chromium chromium-driver firefox-esr RUN apt update && apt install -y graphviz libgraphviz-dev graphviz-dev wget zip chromium chromium-driver firefox-esr
@ -26,4 +22,4 @@ RUN chmod +x /usr/bin/geckodriver
# Expose port 8000 for the web server # Expose port 8000 for the web server
EXPOSE 8000 EXPOSE 8000
ENTRYPOINT [ "/code/start.sh" ] ENTRYPOINT [ "/code/scripts/start.sh" ]

43
Pipfile
View file

@ -1,43 +0,0 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
django = "*"
djangorestframework = "*"
python-dotenv = "*"
whitenoise = "*"
djoser = "*"
django-cors-headers = "*"
drf-spectacular = {version = "*", extras = ["sidecar"]}
django-extra-fields = "*"
pillow = "*"
psycopg2 = "*"
django-simple-history = "*"
django-unfold = "*"
django-resized = "*"
stripe = "*"
celery = "*"
selenium = "*"
undetected-chromedriver = "*"
2captcha-python = "*"
python-whois = "*"
django-celery-beat = "*"
flower = "*"
kombu = "*"
redis = "*"
django-storages = "*"
django-extensions = "*"
django-celery-results = "*"
pygraphviz = "*"
gunicorn = "*"
django-silk = "*"
django-redis = "*"
granian = "*"
black = "*"
[dev-packages]
[requires]
python_version = "3.11"

1407
Pipfile.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from .models import CustomUser from .models import CustomUser

View file

@ -1,10 +1,11 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.urls import reverse
from django_resized import ResizedImageField
from django.utils import timezone
from datetime import timedelta from datetime import timedelta
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django_resized import ResizedImageField
class CustomUser(AbstractUser): class CustomUser(AbstractUser):
# first_name inherited from base user class # first_name inherited from base user class

View file

@ -1,13 +1,12 @@
from djoser.serializers import UserSerializer as BaseUserSerializer
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from accounts.models import CustomUser from accounts.models import CustomUser
from drf_extra_fields.fields import Base64ImageField
from user_groups.serializers import SimpleUserGroupSerializer
from django.core.cache import cache
from django.core import exceptions as django_exceptions
from rest_framework.settings import api_settings
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.core import exceptions as django_exceptions
from django.core.cache import cache
from djoser.serializers import UserSerializer as BaseUserSerializer
from rest_framework import serializers
from rest_framework.serializers import ImageField, ModelSerializer
from rest_framework.settings import api_settings
from user_groups.serializers import SimpleUserGroupSerializer
# There can be multiple subject instances with the same name, only differing in course, year level, and semester. We filter them here # There can be multiple subject instances with the same name, only differing in course, year level, and semester. We filter them here
@ -19,7 +18,7 @@ class SimpleCustomUserSerializer(ModelSerializer):
class CustomUserSerializer(BaseUserSerializer): class CustomUserSerializer(BaseUserSerializer):
avatar = Base64ImageField() avatar = ImageField()
class Meta(BaseUserSerializer.Meta): class Meta(BaseUserSerializer.Meta):
model = CustomUser model = CustomUser

View file

@ -1,10 +1,12 @@
import json
import os
from config.settings import ROOT_DIR, SEED_DATA, get_secret
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.dispatch import receiver from django.dispatch import receiver
from config.settings import SEED_DATA, ROOT_DIR, get_secret from django_celery_beat.models import CrontabSchedule, PeriodicTask
from django_celery_beat.models import PeriodicTask, CrontabSchedule
from .models import CustomUser from .models import CustomUser
import os
import json
# Function to fill in users table with test data on dev/staging # Function to fill in users table with test data on dev/staging

View file

@ -1,6 +1,6 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from accounts import views from accounts import views
from django.urls import include, path
from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
router.register(r"users", views.CustomUserViewSet, basename="users") router.register(r"users", views.CustomUserViewSet, basename="users")

View file

@ -1,6 +1,7 @@
import re
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import re
class UppercaseValidator(object): class UppercaseValidator(object):

View file

@ -1,15 +1,15 @@
from rest_framework import status
from accounts.models import CustomUser
from accounts import serializers from accounts import serializers
from rest_framework.decorators import action from accounts.models import CustomUser
from rest_framework.response import Response
from djoser.conf import settings
from djoser.views import UserViewSet as DjoserUserViewSet
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.core.cache import cache
from djoser import signals from djoser import signals
from djoser.compat import get_user_email from djoser.compat import get_user_email
from django.core.cache import cache from djoser.conf import settings
from djoser.views import UserViewSet as DjoserUserViewSet
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class CustomUserViewSet(DjoserUserViewSet): class CustomUserViewSet(DjoserUserViewSet):

View file

@ -1,13 +1,13 @@
from config.settings import DEBUG, MEDIA_ROOT, SERVE_MEDIA
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path, include from django.urls import include, path
from drf_spectacular.views import ( from drf_spectacular.views import (
SpectacularAPIView, SpectacularAPIView,
SpectacularRedocView, SpectacularRedocView,
SpectacularSwaggerView, SpectacularSwaggerView,
) )
from django.contrib import admin
from config.settings import DEBUG, SERVE_MEDIA, MEDIA_ROOT
urlpatterns = [ urlpatterns = [
path("accounts/", include("accounts.urls")), path("accounts/", include("accounts.urls")),

View file

@ -1,5 +1,5 @@
from django.urls import path
from billing import views from billing import views
from django.urls import path
urlpatterns = [ urlpatterns = [
path("", views.BillingHistoryView.as_view()), path("", views.BillingHistoryView.as_view()),

View file

@ -1,12 +1,12 @@
from rest_framework import status from datetime import datetime
from rest_framework.views import APIView
from rest_framework.response import Response import stripe
from rest_framework.permissions import IsAuthenticated
from config.settings import STRIPE_SECRET_KEY from config.settings import STRIPE_SECRET_KEY
from django.core.cache import cache from django.core.cache import cache
from datetime import datetime from rest_framework import status
import stripe from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
# Make sure to set your secret key # Make sure to set your secret key
stripe.api_key = STRIPE_SECRET_KEY stripe.api_key = STRIPE_SECRET_KEY

View file

@ -1,6 +1,6 @@
from celery import Celery
import os import os
from celery import Celery
# Set the default Django settings module for the 'celery' program. # Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")

View file

@ -1,7 +1,8 @@
import os
from datetime import timedelta from datetime import timedelta
from pathlib import Path from pathlib import Path
from dotenv import load_dotenv, find_dotenv # Python dotenv
import os from dotenv import find_dotenv, load_dotenv # Python dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
# Backend folder (/backend) # Backend folder (/backend)

View file

@ -1,4 +1,4 @@
from django.urls import path, include from django.urls import include, path
urlpatterns = [ urlpatterns = [
path("api/v1/", include("api.urls")), path("api/v1/", include("api.urls")),

View file

@ -1,5 +1,5 @@
from djoser import email
from django.utils import timezone from django.utils import timezone
from djoser import email
class ActivationEmail(email.ActivationEmail): class ActivationEmail(email.ActivationEmail):

View file

@ -1,5 +1,6 @@
from unfold.admin import ModelAdmin
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin
from .models import Notification from .models import Notification

View file

@ -1,5 +1,5 @@
from rest_framework import serializers
from notifications.models import Notification from notifications.models import Notification
from rest_framework import serializers
class NotificationSerializer(serializers.ModelSerializer): class NotificationSerializer(serializers.ModelSerializer):

View file

@ -1,7 +1,7 @@
from django.dispatch import receiver
from django.db.models.signals import post_save
from notifications.models import Notification
from django.core.cache import cache from django.core.cache import cache
from django.db.models.signals import post_save
from django.dispatch import receiver
from notifications.models import Notification
# Template for running actions after user have paid for a subscription # Template for running actions after user have paid for a subscription

View file

@ -1,4 +1,4 @@
from django.urls import path, include from django.urls import include, path
from notifications.views import NotificationViewSet from notifications.views import NotificationViewSet
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter

View file

@ -1,8 +1,8 @@
from rest_framework import viewsets from django.core.cache import cache
from notifications.models import Notification from notifications.models import Notification
from notifications.serializers import NotificationSerializer from notifications.serializers import NotificationSerializer
from rest_framework import viewsets
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from django.core.cache import cache
class NotificationViewSet(viewsets.ModelViewSet): class NotificationViewSet(viewsets.ModelViewSet):

View file

@ -1,7 +1,6 @@
from django.urls import path from django.urls import path
from payments import views from payments import views
urlpatterns = [ urlpatterns = [
path("checkout_session/", views.StripeCheckoutView.as_view()), path("checkout_session/", views.StripeCheckoutView.as_view()),
path("webhook/", views.stripe_webhook_view, name="Stripe Webhook"), path("webhook/", views.stripe_webhook_view, name="Stripe Webhook"),

View file

@ -1,31 +1,32 @@
import json
import logging
import stripe
from accounts.models import CustomUser
from config.settings import ( from config.settings import (
FRONTEND_ADDRESS,
FRONTEND_PORT,
STRIPE_SECRET_KEY, STRIPE_SECRET_KEY,
STRIPE_SECRET_WEBHOOK, STRIPE_SECRET_WEBHOOK,
URL_SCHEME, URL_SCHEME,
FRONTEND_ADDRESS,
FRONTEND_PORT,
)
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
import logging
import stripe
from subscriptions.models import SubscriptionPlan, UserSubscription
from accounts.models import CustomUser
from rest_framework.decorators import api_view
from subscriptions.tasks import get_user_subscription
import json
from emails.templates import (
SubscriptionAvailedEmail,
SubscriptionRefundedEmail,
SubscriptionCancelledEmail,
) )
from django.core.cache import cache from django.core.cache import cache
from payments.serializers import CheckoutSerializer from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from emails.templates import (
SubscriptionAvailedEmail,
SubscriptionCancelledEmail,
SubscriptionRefundedEmail,
)
from payments.serializers import CheckoutSerializer
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from subscriptions.models import SubscriptionPlan, UserSubscription
from subscriptions.tasks import get_user_subscription
stripe.api_key = STRIPE_SECRET_KEY stripe.api_key = STRIPE_SECRET_KEY

View file

@ -1,8 +1,9 @@
from unfold.admin import ModelAdmin
from django.contrib import admin from django.contrib import admin
from .models import SearchResult from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import RangeDateFilter from unfold.contrib.filters.admin import RangeDateFilter
from .models import SearchResult
@admin.register(SearchResult) @admin.register(SearchResult)
class SearchResultAdmin(ModelAdmin): class SearchResultAdmin(ModelAdmin):

View file

@ -1,4 +1,5 @@
from celery import shared_task from celery import shared_task
from .models import SearchResult from .models import SearchResult
@ -6,7 +7,7 @@ from .models import SearchResult
autoretry_for=(Exception,), retry_kwargs={"max_retries": 0, "countdown": 5} autoretry_for=(Exception,), retry_kwargs={"max_retries": 0, "countdown": 5}
) )
def create_search_result(title, link): def create_search_result(title, link):
if SearchResult.objects.filter(title=title, link=link).exists(): if SearchResult.objects.filter(title=title).exists():
return "SearchResult entry already exists" return "SearchResult entry already exists"
else: else:
SearchResult.objects.create(title=title, link=link) SearchResult.objects.create(title=title, link=link)

View file

@ -1,7 +1,7 @@
from django.db import models
from accounts.models import CustomUser from accounts.models import CustomUser
from user_groups.models import UserGroup from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from user_groups.models import UserGroup
class StripePrice(models.Model): class StripePrice(models.Model):

View file

@ -1,6 +1,6 @@
from rest_framework import serializers
from subscriptions.models import SubscriptionPlan, UserSubscription, StripePrice
from accounts.serializers import SimpleCustomUserSerializer from accounts.serializers import SimpleCustomUserSerializer
from rest_framework import serializers
from subscriptions.models import StripePrice, SubscriptionPlan, UserSubscription
class SimpleStripePriceSerializer(serializers.ModelSerializer): class SimpleStripePriceSerializer(serializers.ModelSerializer):

View file

@ -1,9 +1,10 @@
from django.dispatch import receiver
from django.db.models.signals import post_migrate, post_save
from .models import UserSubscription, StripePrice, SubscriptionPlan
from django.core.cache import cache
from config.settings import STRIPE_SECRET_KEY
import stripe import stripe
from config.settings import STRIPE_SECRET_KEY
from django.core.cache import cache
from django.db.models.signals import post_migrate, post_save
from django.dispatch import receiver
from .models import StripePrice, SubscriptionPlan, UserSubscription
stripe.api_key = STRIPE_SECRET_KEY stripe.api_key = STRIPE_SECRET_KEY

View file

@ -3,8 +3,8 @@ from celery import shared_task
@shared_task @shared_task
def get_user_subscription(user_id): def get_user_subscription(user_id):
from subscriptions.models import UserSubscription
from accounts.models import CustomUser from accounts.models import CustomUser
from subscriptions.models import UserSubscription
USER = CustomUser.objects.get(id=user_id) USER = CustomUser.objects.get(id=user_id)

View file

@ -1,6 +1,6 @@
from django.urls import path, include from django.urls import include, path
from subscriptions import views
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from subscriptions import views
router = DefaultRouter() router = DefaultRouter()
router.register(r"plans", views.SubscriptionPlanViewset, basename="Subscription Plans") router.register(r"plans", views.SubscriptionPlanViewset, basename="Subscription Plans")

View file

@ -1,11 +1,11 @@
from django.core.cache import cache
from rest_framework import viewsets
from rest_framework.permissions import AllowAny, IsAuthenticated
from subscriptions.models import SubscriptionPlan, UserSubscription
from subscriptions.serializers import ( from subscriptions.serializers import (
SubscriptionPlanSerializer, SubscriptionPlanSerializer,
UserSubscriptionSerializer, UserSubscriptionSerializer,
) )
from subscriptions.models import SubscriptionPlan, UserSubscription
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework import viewsets
from django.core.cache import cache
class SubscriptionPlanViewset(viewsets.ModelViewSet): class SubscriptionPlanViewset(viewsets.ModelViewSet):

View file

@ -1,8 +1,9 @@
from django.contrib import admin from django.contrib import admin
from unfold.admin import ModelAdmin from unfold.admin import ModelAdmin
from .models import UserGroup
from unfold.contrib.filters.admin import RangeDateFilter from unfold.contrib.filters.admin import RangeDateFilter
from .models import UserGroup
@admin.register(UserGroup) @admin.register(UserGroup)
class UserGroupAdmin(ModelAdmin): class UserGroupAdmin(ModelAdmin):

View file

@ -1,7 +1,7 @@
import stripe
from config.settings import STRIPE_SECRET_KEY
from django.db import models from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from config.settings import STRIPE_SECRET_KEY
import stripe
stripe.api_key = STRIPE_SECRET_KEY stripe.api_key = STRIPE_SECRET_KEY

View file

@ -1,4 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from .models import UserGroup from .models import UserGroup

View file

@ -1,13 +1,15 @@
from subscriptions.models import SubscriptionPlan import json
import os
import stripe
from accounts.models import CustomUser from accounts.models import CustomUser
from .models import UserGroup from config.settings import ROOT_DIR, STRIPE_SECRET_KEY
from subscriptions.tasks import get_user_group_subscription
from django.db.models.signals import m2m_changed, post_migrate from django.db.models.signals import m2m_changed, post_migrate
from django.dispatch import receiver from django.dispatch import receiver
from config.settings import STRIPE_SECRET_KEY, ROOT_DIR from subscriptions.models import SubscriptionPlan
import os from subscriptions.tasks import get_user_group_subscription
import json
import stripe from .models import UserGroup
stripe.api_key = STRIPE_SECRET_KEY stripe.api_key = STRIPE_SECRET_KEY

View file

@ -1,13 +1,13 @@
from celery import shared_task from celery import shared_task
from search_results.tasks import create_search_result
from selenium.webdriver.common.by import By
from webdriver.utils import ( from webdriver.utils import (
setup_webdriver,
selenium_action_template,
google_search,
get_element, get_element,
get_elements, get_elements,
google_search,
selenium_action_template,
setup_webdriver,
) )
from selenium.webdriver.common.by import By
from search_results.tasks import create_search_result
# Task template # Task template

View file

@ -2,21 +2,20 @@
Settings file to hold constants and functions Settings file to hold constants and functions
""" """
import os
import random
import undetected_chromedriver as uc
from config.settings import CAPTCHA_TESTING, USE_PROXY, get_secret
from selenium import webdriver
from selenium.webdriver import FirefoxOptions
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.keys import Keys
from config.settings import get_secret
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import FirefoxOptions from selenium.webdriver.support.ui import WebDriverWait
from selenium import webdriver
import undetected_chromedriver as uc
from config.settings import USE_PROXY, CAPTCHA_TESTING
from config.settings import get_secret
from twocaptcha import TwoCaptcha from twocaptcha import TwoCaptcha
from whois import whois from whois import whois
from whois.parser import PywhoisError from whois.parser import PywhoisError
import os
import random
def take_snapshot(driver, filename="dump.png"): def take_snapshot(driver, filename="dump.png"):
@ -249,7 +248,7 @@ def execute_selenium_elements(driver, timeout, elements):
element["default"]["key"], element["default"]["key"],
timeout=timeout, timeout=timeout,
) )
except Exception as e: except Exception:
print(f"Failed to find primary element") print(f"Failed to find primary element")
# If that fails, try to get the failover one # If that fails, try to get the failover one
print("Trying to find legacy element") print("Trying to find legacy element")

6
pyproject.toml Normal file
View file

@ -0,0 +1,6 @@
[tool.isort]
profile = "black"
[tool.autoflake]
check = true
imports = ["django", "requests", "urllib3"]

View file

@ -1,110 +1,107 @@
-i https://pypi.org/simple -i https://pypi.org/simple
2captcha-python==1.2.5 2captcha-python==1.5.0
amqp==5.2.0; python_version >= '3.6' amqp==5.2.0; python_version >= '3.6'
asgiref==3.8.1; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8'
async-timeout==4.0.3; python_full_version < '3.11.3' async-timeout==4.0.3; python_full_version < '3.11.3'
attrs==23.2.0; python_version >= '3.7' attrs==24.2.0; python_version >= '3.7'
autobahn==23.6.2; python_version >= '3.9' autobahn==24.4.2; python_version >= '3.9'
automat==22.10.0 automat==24.8.1
autopep8==2.1.0; python_version >= '3.8' autopep8==2.3.1; python_version >= '3.8'
billiard==4.2.0; python_version >= '3.7' billiard==4.2.1; python_version >= '3.7'
celery==5.4.0 celery==5.4.0
certifi==2024.2.2; python_version >= '3.6' certifi==2024.8.30; python_version >= '3.6'
cffi==1.16.0; cffi==1.17.1;
charset-normalizer==3.3.2; python_full_version >= '3.7.0' charset-normalizer==3.4.0; python_full_version >= '3.7.0'
click==8.1.7; python_version >= '3.7' click==8.1.7; python_version >= '3.7'
click-didyoumean==0.3.1; python_full_version >= '3.6.2' click-didyoumean==0.3.1; python_full_version >= '3.6.2'
click-plugins==1.1.1 click-plugins==1.1.1
click-repl==0.3.0; python_version >= '3.6' click-repl==0.3.0; python_version >= '3.6'
colorama==0.4.6; colorama==0.4.6;
constantly==23.10.4; python_version >= '3.8' constantly==23.10.4; python_version >= '3.8'
cron-descriptor==1.4.3 cron-descriptor==1.4.5
cryptography==42.0.7; python_version >= '3.7' cryptography==43.0.3; python_version >= '3.7'
defusedxml==0.8.0rc2; python_version >= '3.6' defusedxml==0.8.0rc2; python_version >= '3.6'
django==5.0.6 django==5.1.2
django-celery-beat==2.6.0 django-celery-beat==2.7.0
django-celery-results==2.5.1 django-celery-results==2.5.1
django-cors-headers==4.3.1 django-cors-headers==4.6.0
django-extensions==3.2.3 django-extensions==3.2.3
django-extra-fields==3.0.2
django-redis==5.4.0 django-redis==5.4.0
django-resized==1.0.2 django-resized==1.0.3
django-silk==5.1.0 django-silk==5.2.0
django-simple-history==3.5.0 django-simple-history==3.7.0
django-storages==1.14.3 django-storages==1.14.4
django-templated-mail==1.1.1 django-templated-mail==1.1.1
django-timezone-field==6.1.0; python_version >= '3.8' and python_version < '4.0' django-timezone-field==7.0; python_version >= '3.8' and python_version < '4.0'
django-unfold==0.22.0 django-unfold==0.40.0
djangorestframework==3.15.1 djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1; python_version >= '3.8' djangorestframework-simplejwt==5.3.1; python_version >= '3.8'
djoser==2.2.2 djoser==2.2.3
drf-spectacular[sidecar]==0.27.2 drf-spectacular[sidecar]==0.27.2
drf-spectacular-sidecar==2024.5.1 drf-spectacular-sidecar==2024.7.1
flower==2.0.1 flower==2.0.1
gprof2dot==2022.7.29; python_version >= '2.7' gprof2dot==2024.6.6; python_version >= '2.7'
granian==1.4.1
h11==0.14.0; python_version >= '3.7' h11==0.14.0; python_version >= '3.7'
humanize==4.9.0; python_version >= '3.8' humanize==4.11.0; python_version >= '3.8'
hyperlink==21.0.0 hyperlink==21.0.0
idna==3.7; python_version >= '3.5' idna==3.10; python_version >= '3.5'
incremental==22.10.0 incremental==24.7.2
inflection==0.5.1; python_version >= '3.5' inflection==0.5.1; python_version >= '3.5'
jsonschema==4.22.0; python_version >= '3.8' jsonschema==4.23.0; python_version >= '3.8'
jsonschema-specifications==2023.12.1; python_version >= '3.8' jsonschema-specifications==2024.10.1; python_version >= '3.8'
kombu==5.3.7 kombu==5.4.2
msgpack==1.0.8; python_version >= '3.8' msgpack==1.1.0; python_version >= '3.8'
oauthlib==3.2.2; python_version >= '3.6' oauthlib==3.2.2; python_version >= '3.6'
outcome==1.3.0.post0; python_version >= '3.7' outcome==1.3.0.post0; python_version >= '3.7'
packaging==24.0; python_version >= '3.7' packaging==24.1; python_version >= '3.7'
pillow==10.3.0 pillow==11.0.0
prometheus-client==0.20.0; python_version >= '3.8' prometheus-client==0.21.0; python_version >= '3.8'
prompt-toolkit==3.0.43; python_full_version >= '3.7.0' prompt-toolkit==3.0.48; python_full_version >= '3.7.0'
psycopg2==2.9.9 psycopg2-binary==2.9.10; platform_system == 'Linux'
pyasn1==0.6.0; python_version >= '3.8' pyasn1==0.6.1; python_version >= '3.8'
pyasn1-modules==0.4.0; python_version >= '3.8' pyasn1-modules==0.4.1; python_version >= '3.8'
pycodestyle==2.11.1; python_version >= '3.8' pycodestyle==2.12.1; python_version >= '3.8'
pycparser==2.22; python_version >= '3.8' pycparser==2.22; python_version >= '3.8'
pygraphviz==1.13 pygraphviz==1.14; platform_system == 'Linux'
pyjwt==2.8.0; python_version >= '3.7' pyjwt==2.9.0; python_version >= '3.7'
pyopenssl==24.1.0 pyopenssl==24.2.1
pysocks==1.7.1 pysocks==1.7.1
python-crontab==3.0.0 python-crontab==3.2.0
python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
python-dotenv==1.0.1 python-dotenv==1.0.1
python-whois==0.9.4 python-whois==0.9.4
python3-openid==3.2.0 python3-openid==3.2.0
pytz==2024.1 pytz==2024.2
pyyaml==6.0.1; python_version >= '3.6' pyyaml==6.0.2; python_version >= '3.6'
redis==5.0.4 redis==5.2.0 #
referencing==0.35.1; python_version >= '3.8' referencing==0.35.1; python_version >= '3.8'
requests==2.31.0; python_version >= '3.7' requests==2.32.3; python_version >= '3.7'
requests-oauthlib==2.0.0; python_version >= '3.4' requests-oauthlib==2.0.0; python_version >= '3.4'
rpds-py==0.18.1; python_version >= '3.8' rpds-py==0.20.0; python_version >= '3.8'
selenium==4.20.0 selenium==4.26.0
service-identity==24.1.0 service-identity==24.2.0
setuptools==69.5.1; python_version >= '3.8' setuptools==75.3.0; python_version >= '3.8'
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
sniffio==1.3.1; python_version >= '3.7' sniffio==1.3.1; python_version >= '3.7'
social-auth-app-django==5.4.1; python_version >= '3.8' social-auth-app-django==5.4.2; python_version >= '3.8'
social-auth-core==4.5.4; python_version >= '3.8' social-auth-core==4.5.4; python_version >= '3.8'
sortedcontainers==2.4.0 sortedcontainers==2.4.0
sqlparse==0.5.0; python_version >= '3.8' sqlparse==0.5.1; python_version >= '3.8'
stripe==9.6.0 stripe==11.2.0
tornado==6.4; python_version >= '3.8' tornado==6.4.1; python_version >= '3.8'
trio==0.25.0; python_version >= '3.8' trio==0.27.0; python_version >= '3.8'
trio-websocket==0.11.1; python_version >= '3.7' trio-websocket==0.11.1; python_version >= '3.7'
twisted[tls]==24.3.0; python_full_version >= '3.8.0' twisted[tls]==24.10.0; python_full_version >= '3.8.0'
twisted-iocpsupport==1.0.4; platform_system == 'Windows'
txaio==23.1.1; python_version >= '3.7' txaio==23.1.1; python_version >= '3.7'
typing-extensions==4.11.0; python_version >= '3.8' typing-extensions==4.12.2; python_version >= '3.8'
tzdata==2024.1; python_version >= '2' tzdata==2024.2; python_version >= '2'
undetected-chromedriver==3.5.5 undetected-chromedriver==3.5.5
uritemplate==4.1.1; python_version >= '3.6' uritemplate==4.1.1; python_version >= '3.6'
urllib3[socks]==2.2.1; python_version >= '3.8' urllib3[socks]==2.2.3; python_version >= '3.8'
vine==5.1.0; python_version >= '3.6' vine==5.1.0; python_version >= '3.6'
wcwidth==0.2.13 wcwidth==0.2.13
websockets==12.0; python_version >= '3.8' websockets==13.1; python_version >= '3.8'
whitenoise==6.6.0 whitenoise==6.8.2
wsproto==1.2.0; python_full_version >= '3.7.0' wsproto==1.2.0; python_full_version >= '3.7.0'
zope-interface==6.3; python_version >= '3.7' zope-interface==7.1.1; python_version >= '3.7'
gunicorn==22.0.0 gunicorn==23.0.0

View file

@ -13,9 +13,7 @@ if [ "$RUN_TYPE" = "web" ]; then
if [ "$BACKEND_DEBUG" = 'True' ]; then if [ "$BACKEND_DEBUG" = 'True' ]; then
python manage.py runserver "0.0.0.0:8000" python manage.py runserver "0.0.0.0:8000"
else else
# Feel free to replace the WSGI server here with something else gunicorn --workers 8 --bind 0.0.0.0:8000 config.wsgi:application # Gunicorn
# gunicorn --workers 8 --bind 0.0.0.0:8000 config.wsgi:application # Gunicorn
python -m granian --host 0.0.0.0 --port 8000 --workers 8 --interface wsgi config.wsgi:application # Granian
fi fi
elif [ "$RUN_TYPE" = "worker" ]; then elif [ "$RUN_TYPE" = "worker" ]; then
celery -A config worker -l INFO -E --concurrency 1 celery -A config worker -l INFO -E --concurrency 1