Date: Sun, 9 Apr 2023 14:53:38 +0300
Subject: [PATCH 26/67] move serializers to dedicated file
---
foodcartapp/serializers.py | 17 +++++++++++++++++
foodcartapp/views.py | 18 ++----------------
2 files changed, 19 insertions(+), 16 deletions(-)
create mode 100644 foodcartapp/serializers.py
diff --git a/foodcartapp/serializers.py b/foodcartapp/serializers.py
new file mode 100644
index 000000000..d123896f6
--- /dev/null
+++ b/foodcartapp/serializers.py
@@ -0,0 +1,17 @@
+from rest_framework.serializers import ModelSerializer
+
+from .models import Order, ProductOrder
+
+
+class ProductOrderSerializer(ModelSerializer):
+ class Meta:
+ model = ProductOrder
+ fields = ['product', 'quantity']
+
+
+class OrderSerializer(ModelSerializer):
+ products = ProductOrderSerializer(many=True, allow_empty=False)
+
+ class Meta:
+ model = Order
+ fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products']
diff --git a/foodcartapp/views.py b/foodcartapp/views.py
index eb85d4c8d..ab4c5a1ab 100644
--- a/foodcartapp/views.py
+++ b/foodcartapp/views.py
@@ -3,9 +3,9 @@
from django.templatetags.static import static
from rest_framework.decorators import api_view
from rest_framework.response import Response
-from rest_framework.serializers import ModelSerializer
-from .models import Product, Order, ProductOrder
+from .models import Product, Order
+from .serializers import OrderSerializer
from places.models import Location
@@ -61,20 +61,6 @@ def product_list_api(request):
})
-class ProductOrderSerializer(ModelSerializer):
- class Meta:
- model = ProductOrder
- fields = ['product', 'quantity']
-
-
-class OrderSerializer(ModelSerializer):
- products = ProductOrderSerializer(many=True, allow_empty=False)
-
- class Meta:
- model = Order
- fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products']
-
-
@transaction.atomic
@api_view(['POST'])
def register_order(request):
From a42f3e5209cacc33608431eb17bf1f8ee31a6884 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 9 Apr 2023 15:22:02 +0300
Subject: [PATCH 27/67] fix renaming attribute
---
restaurateur/templates/order_items.html | 2 +-
restaurateur/views.py | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/restaurateur/templates/order_items.html b/restaurateur/templates/order_items.html
index a74ae11f9..0cbd73931 100644
--- a/restaurateur/templates/order_items.html
+++ b/restaurateur/templates/order_items.html
@@ -53,7 +53,7 @@ Необработанные заказы
{% else %}
Передан в ресторан:
- {{ order.restaurant }}
+ {{ order.assigned_restaurant }}
{% endif %}
diff --git a/restaurateur/views.py b/restaurateur/views.py
index cd7926336..e111e246a 100644
--- a/restaurateur/views.py
+++ b/restaurateur/views.py
@@ -102,20 +102,20 @@ def view_orders(request):
.prefetch_related('restaurant', 'product')
)
- addresses = [order.address for order in orders] + [pr.assigned_restaurant.address for pr in products_in_restaurants]
+ addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants]
locations = Location.objects.filter(address__in=addresses)
locations_by_address = {address: None for address in addresses}
for location in locations:
locations_by_address[location.address] = location
for pr in products_in_restaurants:
- pr.assigned_restaurant.location = locations_by_address.get(pr.assigned_restaurant.address)
+ pr.restaurant.location = locations_by_address.get(pr.restaurant.address)
for order in orders:
order.location = locations_by_address.get(order.address)
required_product_ids = [op.product.id for op in ordered_products if op.order == order]
order.available_restaurants = {
- copy(pr.assigned_restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids
+ copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids
}
for restaurant in order.available_restaurants:
try:
From 5b7d640807799e9d7aee851ee73c0ed894817548 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 9 Apr 2023 15:23:19 +0300
Subject: [PATCH 28/67] move instance saving code into serializer
---
foodcartapp/serializers.py | 23 ++++++++++++++++++++++-
foodcartapp/views.py | 18 +-----------------
2 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/foodcartapp/serializers.py b/foodcartapp/serializers.py
index d123896f6..07285c2b2 100644
--- a/foodcartapp/serializers.py
+++ b/foodcartapp/serializers.py
@@ -1,5 +1,6 @@
from rest_framework.serializers import ModelSerializer
+from places.models import Location
from .models import Order, ProductOrder
@@ -10,8 +11,28 @@ class Meta:
class OrderSerializer(ModelSerializer):
- products = ProductOrderSerializer(many=True, allow_empty=False)
+ products = ProductOrderSerializer(many=True, allow_empty=False, write_only=True)
class Meta:
model = Order
fields = ['firstname', 'lastname', 'phonenumber', 'address', 'products']
+
+ def create(self, validated_data):
+ products_in_order = self.validated_data['products']
+ order = Order.objects.create(
+ firstname=self.validated_data['firstname'],
+ lastname=self.validated_data['lastname'],
+ phonenumber=self.validated_data['phonenumber'],
+ address=self.validated_data['address'],
+ )
+ for product_order in products_in_order:
+ product = product_order['product']
+ order.products.add(
+ product,
+ through_defaults={
+ 'quantity': product_order['quantity'],
+ 'product_price': product.price,
+ }
+ )
+ Location.update_by_address(order.address)
+ return order
diff --git a/foodcartapp/views.py b/foodcartapp/views.py
index ab4c5a1ab..be6ae3fd6 100644
--- a/foodcartapp/views.py
+++ b/foodcartapp/views.py
@@ -66,21 +66,5 @@ def product_list_api(request):
def register_order(request):
serializer = OrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
- products_in_order = serializer.validated_data['products']
- order = Order.objects.create(
- firstname=serializer.validated_data['firstname'],
- lastname=serializer.validated_data['lastname'],
- phonenumber=serializer.validated_data['phonenumber'],
- address=serializer.validated_data['address'],
- )
- for product_order in products_in_order:
- product = product_order['product']
- order.products.add(
- product,
- through_defaults={
- 'quantity': product_order['quantity'],
- 'product_price': product.price,
- }
- )
- Location.update_by_address(order.address)
+ serializer.save()
return Response(serializer.data)
From e51bdc18a285e8b416f35109f14b2e7289e5e8ea Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 9 Apr 2023 15:25:10 +0300
Subject: [PATCH 29/67] make fields nullable
---
places/migrations/0002_auto_20230409_1224.py | 23 ++++++++++++++++++++
places/models.py | 8 +++++--
2 files changed, 29 insertions(+), 2 deletions(-)
create mode 100644 places/migrations/0002_auto_20230409_1224.py
diff --git a/places/migrations/0002_auto_20230409_1224.py b/places/migrations/0002_auto_20230409_1224.py
new file mode 100644
index 000000000..9a3221b36
--- /dev/null
+++ b/places/migrations/0002_auto_20230409_1224.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.15 on 2023-04-09 12:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('places', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='location',
+ name='latitude',
+ field=models.FloatField(blank=True, null=True, verbose_name='широта'),
+ ),
+ migrations.AlterField(
+ model_name='location',
+ name='longitude',
+ field=models.FloatField(blank=True, null=True, verbose_name='долгота'),
+ ),
+ ]
diff --git a/places/models.py b/places/models.py
index d42fc8429..66ebcb9b5 100644
--- a/places/models.py
+++ b/places/models.py
@@ -11,10 +11,14 @@ class Location(models.Model):
unique=True,
)
latitude = models.FloatField(
- 'широта'
+ 'широта',
+ blank=True,
+ null=True,
)
longitude = models.FloatField(
- 'долгота'
+ 'долгота',
+ blank=True,
+ null=True,
)
updated_at = models.DateTimeField(
'обновлено',
From 2ee90942675524c005eaacbdfe16f270178b0026 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 9 Apr 2023 15:27:20 +0300
Subject: [PATCH 30/67] create empty locations on geocoder fail
---
places/models.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/places/models.py b/places/models.py
index 66ebcb9b5..71beed5d0 100644
--- a/places/models.py
+++ b/places/models.py
@@ -41,4 +41,10 @@ def update_by_address(cls, address):
},
)
except (geopy.exc.GeocoderServiceError, AttributeError):
- pass
+ cls.objects.get_or_create(
+ address=address,
+ defaults={
+ 'latitude': None,
+ 'longitude': None,
+ },
+ )
From e22e2d86cccd2386d9772d8975a67327e4c69fc5 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 9 Apr 2023 15:28:14 +0300
Subject: [PATCH 31/67] change default value for DEBUG
---
star_burger/settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/star_burger/settings.py b/star_burger/settings.py
index 1f76e80d9..da06a16bb 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -13,7 +13,7 @@
SECRET_KEY = env('SECRET_KEY')
-DEBUG = env.bool('DEBUG', True)
+DEBUG = env.bool('DEBUG', False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', ['127.0.0.1', 'localhost'])
From f361cfd896789c3a99aefe5e1ed7e05c3eec0948 Mon Sep 17 00:00:00 2001
From: mavel
Date: Mon, 10 Apr 2023 17:39:55 +0300
Subject: [PATCH 32/67] move code into querysets
---
foodcartapp/models.py | 21 +++++++++++++++++++++
restaurateur/views.py | 23 ++++++++++-------------
2 files changed, 31 insertions(+), 13 deletions(-)
diff --git a/foodcartapp/models.py b/foodcartapp/models.py
index fffea8e6d..fc14f9b56 100644
--- a/foodcartapp/models.py
+++ b/foodcartapp/models.py
@@ -97,6 +97,15 @@ def __str__(self):
return self.name
+class RestaurantMenuItemQuerySet(models.QuerySet):
+ def include_products(self, products: list | models.QuerySet):
+ return (
+ self
+ .filter(product__in=[product.id for product in products], availability=True)
+ .prefetch_related('restaurant', 'product')
+ )
+
+
class RestaurantMenuItem(models.Model):
restaurant = models.ForeignKey(
Restaurant,
@@ -116,6 +125,8 @@ class RestaurantMenuItem(models.Model):
db_index=True
)
+ objects = RestaurantMenuItemQuerySet.as_manager()
+
class Meta:
verbose_name = 'пункт меню ресторана'
verbose_name_plural = 'пункты меню ресторана'
@@ -127,6 +138,11 @@ def __str__(self):
return f"{self.restaurant.name} - {self.product.name}"
+class ProductOrderQuerySet(models.QuerySet):
+ def in_orders(self, orders: models.QuerySet):
+ return self.filter(order__in=orders).prefetch_related('product', 'order')
+
+
class ProductOrder(models.Model):
product = models.ForeignKey(
'Product',
@@ -153,6 +169,8 @@ class ProductOrder(models.Model):
blank=True,
)
+ objects = ProductOrderQuerySet.as_manager()
+
def __str__(self):
return f'{self.product}, {self.quantity}'
@@ -161,6 +179,9 @@ class OrderQuerySet(models.QuerySet):
def with_totals(self):
return self.annotate(total=Sum(F('products_ordered__product_price') * F('products_ordered__quantity')))
+ def active(self):
+ return self.exclude(status=Order.Status.COMPLETE).prefetch_related('assigned_restaurant')
+
class Order(models.Model):
diff --git a/restaurateur/views.py b/restaurateur/views.py
index e111e246a..68a24ed60 100644
--- a/restaurateur/views.py
+++ b/restaurateur/views.py
@@ -94,28 +94,25 @@ def view_restaurants(request):
@user_passes_test(is_manager, login_url='restaurateur:login')
def view_orders(request):
- orders = Order.objects.exclude(status=Order.Status.COMPLETE).with_totals().prefetch_related('assigned_restaurant')
- ordered_products = ProductOrder.objects.filter(order__in=orders).prefetch_related('product', 'order')
- products_in_restaurants = (
- RestaurantMenuItem.objects
- .filter(product__in=[order.product for order in ordered_products], availability=True)
- .prefetch_related('restaurant', 'product')
- )
+ active_orders = Order.objects.active().with_totals()
+ active_product_orders = ProductOrder.objects.in_orders(active_orders)
+ ordered_products = set(product_order.product for product_order in active_product_orders)
+ ordered_menu_items = RestaurantMenuItem.objects.include_products(ordered_products)
- addresses = [order.address for order in orders] + [pr.restaurant.address for pr in products_in_restaurants]
+ addresses = [order.address for order in active_orders] + [pr.restaurant.address for pr in ordered_menu_items]
locations = Location.objects.filter(address__in=addresses)
locations_by_address = {address: None for address in addresses}
for location in locations:
locations_by_address[location.address] = location
- for pr in products_in_restaurants:
+ for pr in ordered_menu_items:
pr.restaurant.location = locations_by_address.get(pr.restaurant.address)
- for order in orders:
+ for order in active_orders:
order.location = locations_by_address.get(order.address)
- required_product_ids = [op.product.id for op in ordered_products if op.order == order]
+ required_product_ids = [op.product.id for op in active_product_orders if op.order == order]
order.available_restaurants = {
- copy(pr.restaurant) for pr in products_in_restaurants if pr.product.id in required_product_ids
+ copy(pr.restaurant) for pr in ordered_menu_items if pr.product.id in required_product_ids
}
for restaurant in order.available_restaurants:
try:
@@ -131,5 +128,5 @@ def view_orders(request):
)
return render(request, template_name='order_items.html', context={
- 'orders': orders,
+ 'orders': active_orders,
})
From 4047620173805476d243b0067046814bb4dad989 Mon Sep 17 00:00:00 2001
From: mavel
Date: Mon, 10 Apr 2023 17:57:21 +0300
Subject: [PATCH 33/67] clean up
---
restaurateur/views.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/restaurateur/views.py b/restaurateur/views.py
index 68a24ed60..142ba4c0b 100644
--- a/restaurateur/views.py
+++ b/restaurateur/views.py
@@ -105,14 +105,15 @@ def view_orders(request):
for location in locations:
locations_by_address[location.address] = location
- for pr in ordered_menu_items:
- pr.restaurant.location = locations_by_address.get(pr.restaurant.address)
+ for menu_item in ordered_menu_items:
+ menu_item.restaurant.location = locations_by_address.get(menu_item.restaurant.address)
for order in active_orders:
order.location = locations_by_address.get(order.address)
- required_product_ids = [op.product.id for op in active_product_orders if op.order == order]
+ required_product_ids = [po.product.id for po in active_product_orders if po.order == order]
order.available_restaurants = {
- copy(pr.restaurant) for pr in ordered_menu_items if pr.product.id in required_product_ids
+ copy(menu_item.restaurant) for menu_item in ordered_menu_items
+ if menu_item.product.id in required_product_ids
}
for restaurant in order.available_restaurants:
try:
From 54f28911e345ee9275e0fac092db371d264f6b8f Mon Sep 17 00:00:00 2001
From: mavel
Date: Mon, 10 Apr 2023 18:16:37 +0300
Subject: [PATCH 34/67] remove some code
---
restaurateur/views.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/restaurateur/views.py b/restaurateur/views.py
index 142ba4c0b..2b0e367ea 100644
--- a/restaurateur/views.py
+++ b/restaurateur/views.py
@@ -101,9 +101,7 @@ def view_orders(request):
addresses = [order.address for order in active_orders] + [pr.restaurant.address for pr in ordered_menu_items]
locations = Location.objects.filter(address__in=addresses)
- locations_by_address = {address: None for address in addresses}
- for location in locations:
- locations_by_address[location.address] = location
+ locations_by_address = {location.address: location for location in locations}
for menu_item in ordered_menu_items:
menu_item.restaurant.location = locations_by_address.get(menu_item.restaurant.address)
From e400a72d26c878f5361622d5cd9048bec98abf71 Mon Sep 17 00:00:00 2001
From: mavel
Date: Tue, 9 May 2023 02:38:10 +0300
Subject: [PATCH 35/67] add rollbar
---
requirements.txt | 1 +
star_burger/settings.py | 7 +++++++
2 files changed, 8 insertions(+)
diff --git a/requirements.txt b/requirements.txt
index b5cf774ad..24b698f04 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,3 +6,4 @@ wheel==0.38.4
django-phonenumber-field[phonenumbers]==7.0.2
djangorestframework==3.14.0
geopy~=2.3.0
+rollbar~=0.16.3
diff --git a/star_burger/settings.py b/star_burger/settings.py
index da06a16bb..3d73ade78 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -42,6 +42,7 @@
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'debug_toolbar.middleware.DebugToolbarMiddleware',
+ 'rollbar.contrib.django.middleware.RollbarNotifierMiddleware',
]
ROOT_URLCONF = 'star_burger.urls'
@@ -130,3 +131,9 @@
PHONENUMBER_DEFAULT_REGION = 'RU'
YANDEX_GEO_KEY = env.str('YANDEX_GEO_KEY')
+
+ROLLBAR = {
+ 'access_token': env.str('ROLLBAR_TOKEN'),
+ 'environment': env.str('ROLLBAR_ENV'),
+ 'root': BASE_DIR,
+}
From 3be63b8d6eac5f28be80e15adbe8ae7878f9a5f7 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 11 May 2023 01:36:44 +0300
Subject: [PATCH 36/67] use postgres
---
README.md | 30 ++++++++++++++++++++++++++++--
requirements.txt | 1 +
star_burger/settings.py | 7 ++++---
3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 60d4e5059..dfde50413 100644
--- a/README.md
+++ b/README.md
@@ -140,17 +140,43 @@ Parcel будет следить за файлами в каталоге `bundle
## Как запустить prod-версию сайта
-Собрать фронтенд:
+### Собрать фронтенд:
```sh
./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
```
-Настроить бэкенд: создать файл `.env` в каталоге `star_burger/` со следующими настройками:
+### Настроить бэкенд:
+
+Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812):
+```sh
+pip install psycopg2
+```
+
+Запустить Docker-контейнер с БД (замените все `...` на свои значения):
+```sh
+docker run -d \
+--name starburger-postgres \
+-p 5432:5432 \
+-e POSTGRES_USER=... \
+-e POSTGRES_PASSWORD=... \
+-e POSTGRES_DB=... \
+-v starburger-postgres:/var/lib/postgresql/data \
+postgres:14
+```
+
+Создать файл `.env` в каталоге `star_burger/` со следующими настройками:
- `DEBUG` — дебаг-режим. Поставьте `False`.
- `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте.
- `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts)
+- `DB_URL=postgresql://юзер:пароль@localhost:5432/имя-бд`
+
+Создать таблицы и импортировать данные в БД:
+```sh
+./manage.py migrate
+./manage.py loaddata data/db_dump.json
+```
## Цели проекта
diff --git a/requirements.txt b/requirements.txt
index 24b698f04..a72d46a29 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,4 @@ django-phonenumber-field[phonenumbers]==7.0.2
djangorestframework==3.14.0
geopy~=2.3.0
rollbar~=0.16.3
+psycopg2~=2.9.6
diff --git a/star_burger/settings.py b/star_burger/settings.py
index 3d73ade78..3f8a7e827 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -86,8 +86,9 @@
MEDIA_URL = '/media/'
DATABASES = {
- 'default': dj_database_url.config(
- default='sqlite:////{0}'.format(os.path.join(BASE_DIR, 'db.sqlite3'))
+ 'default': env.dj_db_url(
+ 'DB_URL',
+ default=f'sqlite:////{os.path.join(BASE_DIR, "db.sqlite3")}'
)
}
@@ -134,6 +135,6 @@
ROLLBAR = {
'access_token': env.str('ROLLBAR_TOKEN'),
- 'environment': env.str('ROLLBAR_ENV'),
+ 'environment': env.str('ROLLBAR_ENV', 'development'),
'root': BASE_DIR,
}
From 651ebac6fbbf4d39159ca985c245368c8b99ae9e Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 11 May 2023 01:45:35 +0300
Subject: [PATCH 37/67] fix errors in README
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index dfde50413..68c4c3d56 100644
--- a/README.md
+++ b/README.md
@@ -150,7 +150,7 @@ Parcel будет следить за файлами в каталоге `bundle
Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812):
```sh
-pip install psycopg2
+apt-get install libpq-dev
```
Запустить Docker-контейнер с БД (замените все `...` на свои значения):
@@ -161,7 +161,7 @@ docker run -d \
-e POSTGRES_USER=... \
-e POSTGRES_PASSWORD=... \
-e POSTGRES_DB=... \
--v starburger-postgres:/var/lib/postgresql/data \
+-v starburger_postgres:/var/lib/postgresql/data \
postgres:14
```
From 8e1c710ad9068462e13e5a36adbef040311bd9c6 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 11 May 2023 19:15:11 +0300
Subject: [PATCH 38/67] add a deploy script
---
README.md | 29 ++++++++---------------------
scripts/deploy_postgres.sh | 24 ++++++++++++++++++++++++
2 files changed, 32 insertions(+), 21 deletions(-)
create mode 100755 scripts/deploy_postgres.sh
diff --git a/README.md b/README.md
index 68c4c3d56..2a8a541c8 100644
--- a/README.md
+++ b/README.md
@@ -148,34 +148,21 @@ Parcel будет следить за файлами в каталоге `bundle
### Настроить бэкенд:
-Установить зависимости для [psycopg2](https://stackoverflow.com/questions/5420789/how-to-install-psycopg2-with-pip-on-python/30995812#30995812):
-```sh
-apt-get install libpq-dev
-```
-
-Запустить Docker-контейнер с БД (замените все `...` на свои значения):
-```sh
-docker run -d \
---name starburger-postgres \
--p 5432:5432 \
--e POSTGRES_USER=... \
--e POSTGRES_PASSWORD=... \
--e POSTGRES_DB=... \
--v starburger_postgres:/var/lib/postgresql/data \
-postgres:14
-```
-
Создать файл `.env` в каталоге `star_burger/` со следующими настройками:
- `DEBUG` — дебаг-режим. Поставьте `False`.
- `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте.
- `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts)
-- `DB_URL=postgresql://юзер:пароль@localhost:5432/имя-бд`
+- `POSTGRES_USER=...`
+- `POSTGRES_PASSWORD=...`
+- `POSTGRES_DB=...`
+- `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно
-Создать таблицы и импортировать данные в БД:
+Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД.
+Затем запустить из каталога `star-burger/` скрипт, который установит зависимости для Postgres и подготовит контейнер с БД:
```sh
-./manage.py migrate
-./manage.py loaddata data/db_dump.json
+chmod u+x scripts/deploy_postgres.sh
+scripts/deploy_postgres.sh
```
## Цели проекта
diff --git a/scripts/deploy_postgres.sh b/scripts/deploy_postgres.sh
new file mode 100755
index 000000000..77481e978
--- /dev/null
+++ b/scripts/deploy_postgres.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+if [ "$EUID" -ne 0 ]
+ then
+ echo "Please run as root."
+ exit
+fi
+
+if [ $( docker ps -a | grep starburger-postgres | wc -l ) -gt 0 ]
+ then
+ echo "Postgres container already exists."
+ exit
+fi
+
+apt-get install libpq-dev
+
+docker run -d --name starburger-postgres \
+--env-file .env -p 5432:5432 \
+-v starburger_postgres:/var/lib/postgresql/data \
+postgres:14
+sleep 1
+venv/bin/python ./manage.py migrate
+venv/bin/python ./manage.py loaddata data/db_dump.json
+docker stop -t 10 starburger-postgres
From fb752fa58ed25ade2a5e827e0c1cfc76bbb22b08 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sat, 13 May 2023 02:01:04 +0300
Subject: [PATCH 39/67] update deploy script
---
.gitignore | 1 +
README.md | 8 +-
package-lock.json | 3224 ++++++++++++++++++++++++++++++++-
scripts/deploy_star_burger.sh | 21 +
4 files changed, 3209 insertions(+), 45 deletions(-)
create mode 100755 scripts/deploy_star_burger.sh
diff --git a/.gitignore b/.gitignore
index 9447fd3c4..d6c5988cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -143,3 +143,4 @@ cython_debug/
.parcel-cache/
/data/
+staticfiles/
diff --git a/README.md b/README.md
index 2a8a541c8..0e29d84fa 100644
--- a/README.md
+++ b/README.md
@@ -159,12 +159,18 @@ Parcel будет следить за файлами в каталоге `bundle
- `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно
Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД.
-Затем запустить из каталога `star-burger/` скрипт, который установит зависимости для Postgres и подготовит контейнер с БД:
+Убедиться, что на сервере установлен Docker.
+Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД:
```sh
chmod u+x scripts/deploy_postgres.sh
scripts/deploy_postgres.sh
```
+### Подтянуть изменения в репозитории и перезапустить сервисы:
+```shell
+scripts/deploy_star_burger.sh
+```
+
## Цели проекта
Код написан в учебных целях — это урок в курсе по Python и веб-разработке на сайте [Devman](https://dvmn.org). За основу был взят код проекта [FoodCart](https://github.com/Saibharath79/FoodCart).
diff --git a/package-lock.json b/package-lock.json
index b94c7285d..82ac26c33 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,3138 @@
{
+ "name": "star-burger",
+ "lockfileVersion": 2,
"requires": true,
- "lockfileVersion": 1,
+ "packages": {
+ "": {
+ "name": "star-burger",
+ "dependencies": {
+ "16": "0.0.2",
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
+ "@babel/preset-react": "^7.16.7",
+ "core-js": "^3.21.1",
+ "lodash": "^4.17.21",
+ "parcel": "^2.4.0",
+ "react": "^17.0.2",
+ "react-bootstrap": "^0.33.1",
+ "react-dom": "^17.0.2",
+ "react-transition-group": "^4.4.2"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.17.8"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz",
+ "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
+ "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
+ "dependencies": {
+ "@babel/highlight": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
+ "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
+ "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
+ "dependencies": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.17.7",
+ "@babel/helper-compilation-targets": "^7.17.7",
+ "@babel/helper-module-transforms": "^7.17.7",
+ "@babel/helpers": "^7.17.8",
+ "@babel/parser": "^7.17.8",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.1.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
+ "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
+ "dependencies": {
+ "@babel/types": "^7.17.0",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+ "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
+ "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
+ "dependencies": {
+ "@babel/compat-data": "^7.17.7",
+ "@babel/helper-validator-option": "^7.16.7",
+ "browserslist": "^4.17.5",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz",
+ "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-member-expression-to-functions": "^7.16.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
+ "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
+ "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+ "dependencies": {
+ "@babel/helper-get-function-arity": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-get-function-arity": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
+ "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
+ "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz",
+ "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==",
+ "dependencies": {
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+ "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
+ "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-simple-access": "^7.17.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz",
+ "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
+ "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz",
+ "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==",
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-member-expression-to-functions": "^7.16.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/traverse": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.17.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
+ "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
+ "dependencies": {
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
+ "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+ "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz",
+ "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
+ "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
+ "dependencies": {
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.16.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
+ "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
+ "node_modules/@babel/highlight/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/highlight/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz",
+ "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==",
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-class-properties": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz",
+ "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
+ "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-display-name": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz",
+ "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx": {
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz",
+ "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-jsx": "^7.16.7",
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-development": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz",
+ "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==",
+ "dependencies": {
+ "@babel/plugin-transform-react-jsx": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-pure-annotations": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz",
+ "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-react": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz",
+ "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-validator-option": "^7.16.7",
+ "@babel/plugin-transform-react-display-name": "^7.16.7",
+ "@babel/plugin-transform-react-jsx": "^7.16.7",
+ "@babel/plugin-transform-react-jsx-development": "^7.16.7",
+ "@babel/plugin-transform-react-pure-annotations": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
+ "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
+ "dependencies": {
+ "regenerator-runtime": "^0.13.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs2": {
+ "version": "7.17.8",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.17.8.tgz",
+ "integrity": "sha512-KWN7KTjojEVk+hhT7EtvWtSBTueqnPiCT2xPoDFF+ept2Sx9UKnLY7hGsnrNsdx7jvMUQnHoDS6AHCys7i15LA==",
+ "dependencies": {
+ "core-js": "^2.6.5",
+ "regenerator-runtime": "^0.13.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs2/node_modules/core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+ "hasInstallScript": true
+ },
+ "node_modules/@babel/template": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+ "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/parser": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
+ "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.17.3",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/parser": "^7.17.3",
+ "@babel/types": "^7.17.0",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
+ "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
+ "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "node_modules/@parcel/bundler-default": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.4.0.tgz",
+ "integrity": "sha512-RaXlxo0M51739Ko3bsOJpDBZlJ+cqkDoBTozNeSc65jS2TMBIBWLMapm8095qmty39OrgYNhzjgPiIlKDS/LWA==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/cache": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.4.0.tgz",
+ "integrity": "sha512-oOudoAafrCAHQY0zkU7gVHG1pAGBUz9rht7Tx4WupTmAH0O0F5UnZs6XbjoBJaPHg+CYUXK7v9wQcrNA72E3GA==",
+ "dependencies": {
+ "@parcel/fs": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "lmdb": "2.2.4"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "peerDependencies": {
+ "@parcel/core": "^2.4.0"
+ }
+ },
+ "node_modules/@parcel/codeframe": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.4.0.tgz",
+ "integrity": "sha512-PJ3W9Z0sjoS2CANyo50c+LEr9IRZrtu0WsVPSYZ5ZYRuSXrSa/6PcAlnkyDk2+hi7Od8ncT2bmDexl0Oar3Jyg==",
+ "dependencies": {
+ "chalk": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/compressor-raw": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.4.0.tgz",
+ "integrity": "sha512-ZErX14fTc0gKIgtnuqW7Clfln4dpXWfUaJQQIf5C3x/LkpUeEhdXeKntkvSxOddDk2JpIKDwqzAxEMZUnDo4Nw==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/config-default": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.4.0.tgz",
+ "integrity": "sha512-pFOPBXPO6HGqNWTLkcK5i8haMOrRgUouUhcWPGWDpN9IPUYFK2E/O1E/uyMjIA1mSL3FnazI+jJwZ45NhKPpIA==",
+ "dependencies": {
+ "@parcel/bundler-default": "2.4.0",
+ "@parcel/compressor-raw": "2.4.0",
+ "@parcel/namer-default": "2.4.0",
+ "@parcel/optimizer-css": "2.4.0",
+ "@parcel/optimizer-htmlnano": "2.4.0",
+ "@parcel/optimizer-image": "2.4.0",
+ "@parcel/optimizer-svgo": "2.4.0",
+ "@parcel/optimizer-terser": "2.4.0",
+ "@parcel/packager-css": "2.4.0",
+ "@parcel/packager-html": "2.4.0",
+ "@parcel/packager-js": "2.4.0",
+ "@parcel/packager-raw": "2.4.0",
+ "@parcel/packager-svg": "2.4.0",
+ "@parcel/reporter-dev-server": "2.4.0",
+ "@parcel/resolver-default": "2.4.0",
+ "@parcel/runtime-browser-hmr": "2.4.0",
+ "@parcel/runtime-js": "2.4.0",
+ "@parcel/runtime-react-refresh": "2.4.0",
+ "@parcel/runtime-service-worker": "2.4.0",
+ "@parcel/transformer-babel": "2.4.0",
+ "@parcel/transformer-css": "2.4.0",
+ "@parcel/transformer-html": "2.4.0",
+ "@parcel/transformer-image": "2.4.0",
+ "@parcel/transformer-js": "2.4.0",
+ "@parcel/transformer-json": "2.4.0",
+ "@parcel/transformer-postcss": "2.4.0",
+ "@parcel/transformer-posthtml": "2.4.0",
+ "@parcel/transformer-raw": "2.4.0",
+ "@parcel/transformer-react-refresh-wrap": "2.4.0",
+ "@parcel/transformer-svg": "2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "peerDependencies": {
+ "@parcel/core": "^2.4.0"
+ }
+ },
+ "node_modules/@parcel/core": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.4.0.tgz",
+ "integrity": "sha512-EWZ2UWtIuwDc3fgsKyyTLpNNPoG8Yk2L117ICWF/+cqY8z/wJHm2KwLbeplDeq524shav0GJ9O4CemP3JPx0Nw==",
+ "dependencies": {
+ "@parcel/cache": "2.4.0",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/events": "2.4.0",
+ "@parcel/fs": "2.4.0",
+ "@parcel/graph": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/package-manager": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "@parcel/workers": "2.4.0",
+ "abortcontroller-polyfill": "^1.1.9",
+ "base-x": "^3.0.8",
+ "browserslist": "^4.6.6",
+ "clone": "^2.1.1",
+ "dotenv": "^7.0.0",
+ "dotenv-expand": "^5.1.0",
+ "json-source-map": "^0.6.1",
+ "json5": "^2.2.0",
+ "msgpackr": "^1.5.4",
+ "nullthrows": "^1.1.1",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css/-/css-1.7.3.tgz",
+ "integrity": "sha512-rgdRX4Uk31EvzH/mUScL0wdXtkci3U5N1W2pgam+9S10vQy4uONhWBepZ1tUCjONHLacGXr1jp3LbG/HI7LiTw==",
+ "dependencies": {
+ "detect-libc": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/css-darwin-arm64": "1.7.3",
+ "@parcel/css-darwin-x64": "1.7.3",
+ "@parcel/css-linux-arm-gnueabihf": "1.7.3",
+ "@parcel/css-linux-arm64-gnu": "1.7.3",
+ "@parcel/css-linux-arm64-musl": "1.7.3",
+ "@parcel/css-linux-x64-gnu": "1.7.3",
+ "@parcel/css-linux-x64-musl": "1.7.3",
+ "@parcel/css-win32-x64-msvc": "1.7.3"
+ }
+ },
+ "node_modules/@parcel/css-darwin-arm64": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.7.3.tgz",
+ "integrity": "sha512-m3HDY+Rh8HJxmLELKAvCpF59vLS7FWtgBODHxl8G9Jl2CnGtXpXvdpyeMxNsTE+2QuPC+a5QT7IeZAKb2Gjmxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-darwin-x64": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-darwin-x64/-/css-darwin-x64-1.7.3.tgz",
+ "integrity": "sha512-LuhweXKxVwrz/hjAOm9XNRMSL+p23px20nhSCASkyUP7Higaxza948W3TSQdoL3YyR+wQxQH8Yj+R/T8Tz3E3g==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-linux-arm-gnueabihf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.7.3.tgz",
+ "integrity": "sha512-/pd9Em18zMvt7eDZAMpNBEwF7c4VPVhAtBOZ59ClFrsXCTDNYP7mSy0cwNgtLelCRZCGAQmZNBDNQPH7vO3rew==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-linux-arm64-gnu": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.7.3.tgz",
+ "integrity": "sha512-5aKiEhQK40riO4iVKzRqISzgYK+7Z7i3e6JTSz+/BHuQyHEUaBe/RuJ8Z0BDQtFz0HmWQlrQCd+7hd0Xgd8vYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-linux-arm64-musl": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.7.3.tgz",
+ "integrity": "sha512-Wf7/aIueDED2JqBMfZvzbBAFSaPmd3TR28bD2pmP7CI/jZnm9vHVKMdOLgt9NKSSSjdGrp+VM410CsrUM7xcOw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-linux-x64-gnu": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.7.3.tgz",
+ "integrity": "sha512-0ZADbuFklUrHC1p2uPY4BPcN07jUTMqJzr/SSdnGN2XiXgiVZGcDCMHUj0DvC9Vwy11DDM6Rnw4QBbKHG+QGjQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-linux-x64-musl": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.7.3.tgz",
+ "integrity": "sha512-mFWWM8lX2OIID81YQuDDt9zTqof0B7UcEcs0huE7Zbs60uLEEQupdf8iH0yh5EOhxPt3sRcQnGXf2QTrXdjIMA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/css-win32-x64-msvc": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.7.3.tgz",
+ "integrity": "sha512-KUFEMQcoP7DG3QbsN21OxhjHkfQ1BARn7D9puX75bV5N1F1kv557aaLkQZiMsgiYOL4tmJvsdQXutG7x++3j4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/diagnostic": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.4.0.tgz",
+ "integrity": "sha512-TjWO/b2zMFhub5ouwGjazMm7iAUvdmXBfWmjrg4TBhUbhoQwBnyWfvMDtAYo7PcvXfxVPgPZv86Nv6Ym5H6cHQ==",
+ "dependencies": {
+ "json-source-map": "^0.6.1",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/events": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.4.0.tgz",
+ "integrity": "sha512-DEaEtFbhOhNAEmiXJ3MyF8Scq+sNDKiTyLax4lAC5/dpE5GvwfNnoD17C2+0gDuuDpdQkdHfXfvr50aYFt7jcw==",
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/fs": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.4.0.tgz",
+ "integrity": "sha512-CnUlWGUJ52SJVQi8QnaAPPQZOADmHMV9D9aX9GLcDm5XLT3Em7vmesG4bNLdMLwzYuzAtenhcWmuRCACuYztHw==",
+ "dependencies": {
+ "@parcel/fs-search": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "@parcel/watcher": "^2.0.0",
+ "@parcel/workers": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "peerDependencies": {
+ "@parcel/core": "^2.4.0"
+ }
+ },
+ "node_modules/@parcel/fs-search": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.4.0.tgz",
+ "integrity": "sha512-W/Vu6wbZk4wuB6AVdMkyymwh/S8Peed/PgJgSsApYD6lSTD315I6OuEdxZh3lWY+dqQdog/NJ7dvi/hdpH/Iqw==",
+ "dependencies": {
+ "detect-libc": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/graph": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.4.0.tgz",
+ "integrity": "sha512-5TZIAfDITkJCzgH4j4OQhnIvjV9IFwWqNBJanRl5QQTmKvdcODS3WbnK1SOJ+ZltcLVXMB+HNXmL0bX0tVolcw==",
+ "dependencies": {
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/hash": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.4.0.tgz",
+ "integrity": "sha512-nB+wYNUhe6+G8M7vQhdeFXtpYJYwJgBHOPZ7Hd9O2jdlamWjDbw0t/u1dJbYvGJ8ZDtLDwiItawQVpuVdskQ9g==",
+ "dependencies": {
+ "detect-libc": "^1.0.3",
+ "xxhash-wasm": "^0.4.2"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/logger": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.4.0.tgz",
+ "integrity": "sha512-DqfU0Zcs/0a7VBk+MsjJ80C66w4kM9EbkO3G12NIyEjNeG50ayW2CE9rUuJ91JaM9j0NFM1P82eyLpQPFFaVPw==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/events": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/markdown-ansi": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.4.0.tgz",
+ "integrity": "sha512-gPUP1xikxHiu2kFyPy35pfuVkFgAmcywO8YDQj7iYcB+k7l4QPpIYFYGXn2QADV4faf66ncMeTD4uYV8c0GqjQ==",
+ "dependencies": {
+ "chalk": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/namer-default": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.4.0.tgz",
+ "integrity": "sha512-DfL+Gx0Tyoa0vsgRpNybXjuKbWNw8MTVpy7Dk7r0btfVsn1jy3SSwlxH4USf76gb00/pK6XBsMp9zn7Z8ePREQ==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/node-resolver-core": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.4.0.tgz",
+ "integrity": "sha512-qiN97XcfW2fYNoYuVEhNKuVPEJKj5ONQl0fqr/NEMmYvWz3bVKjgiXNJwW558elZvCI08gEbdxgyThpuFFQeKQ==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/optimizer-css": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.4.0.tgz",
+ "integrity": "sha512-LQmjjOGsHEHKTJqfHR2eJyhWhLXvHP0uOAU+qopBttYYlB2J/vMK9RYAye5cyAb8bQmV8wAdi2mq9rnt7FMSPw==",
+ "dependencies": {
+ "@parcel/css": "^1.7.2",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "browserslist": "^4.6.6",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/optimizer-htmlnano": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.4.0.tgz",
+ "integrity": "sha512-02EbeElLgNOAYhGU7fFBahpoKrX5G/yzahpaoKB/ypScM4roSsAMBkGcluboR5L10YRsvfvJEpxvfGyDA3tPmw==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "htmlnano": "^2.0.0",
+ "nullthrows": "^1.1.1",
+ "posthtml": "^0.16.5",
+ "svgo": "^2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/optimizer-image": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.4.0.tgz",
+ "integrity": "sha512-Q4onaBMPkDyYxPzrb8ytBUftaQZFepj9dSUgq+ETuHDzkgia0tomDPfCqrw6ld0qvYyANzXTP5+LC4g0i5yh+A==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "@parcel/workers": "2.4.0",
+ "detect-libc": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/optimizer-svgo": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.4.0.tgz",
+ "integrity": "sha512-mwvGuCqVuNCAuMlp2maFE/Uz9ud1T1AuX0f6cCRczjFYiwZuIr/0iDdfFzSziOkVo1MRAGAZNa0dRR/UzCZtVg==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "svgo": "^2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/optimizer-terser": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.4.0.tgz",
+ "integrity": "sha512-PdCgRgXNSY6R1HTV9VG2MHp1CgUbP5pslCyxvlbUmQAS6bvEpMOpn3qSd+U28o7mGE/qXIhvpDyi808sb+MEcg==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1",
+ "terser": "^5.2.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/package-manager": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.4.0.tgz",
+ "integrity": "sha512-21AEfAQnZbHRVViTn7QsPGe/CiGaFaDUH5f0m8qVC7fDjjhC8LM8blkqU72goaO9FbaLMadtEf2txhzly7h/bg==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/fs": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "@parcel/workers": "2.4.0",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "peerDependencies": {
+ "@parcel/core": "^2.4.0"
+ }
+ },
+ "node_modules/@parcel/packager-css": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.4.0.tgz",
+ "integrity": "sha512-LmPDWzkXi60Oy3WrPF0jPKQxeTwW5hmNBgrcXJMHSu+VcXdaQZNzNxVzhnZkJUbDd2z9vAUrUGzdLh8TquC8iQ==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/packager-html": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.4.0.tgz",
+ "integrity": "sha512-OPMIQ1uHYQFpRPrsmm5BqONbAyzjlhVsPRAzHlcBrglG4BTUeOR2ow4MUKblHmVVqc3QHnfZG4nHHtFkeuNQ3A==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1",
+ "posthtml": "^0.16.5"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/packager-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.4.0.tgz",
+ "integrity": "sha512-cfslIH43CJFgBS9PmdFaSnbInMCoejsFCnxtJa2GeUpjCXSfelPRp0OPx7m8n+fap4czftPhoxBALeDUElOZGQ==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "globals": "^13.2.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/packager-raw": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.4.0.tgz",
+ "integrity": "sha512-SFfw7chMFITj3J26ZVDJxbO6xwtPFcFBm1js8cwWMgzwuwS6CEc43k5+Abj+2/EqHU9kNJU9eWV5vT6lQwf3HA==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/packager-svg": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.4.0.tgz",
+ "integrity": "sha512-DwkgrdLEQop+tu9Ocr1ZaadmpsbSgVruJPr80xq1LaB0Jiwrl9HjHStMNH1laNFueK1yydxhnj9C2JQfW28qag==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "posthtml": "^0.16.4"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/plugin": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.4.0.tgz",
+ "integrity": "sha512-ehFUAL2+h27Lv+cYbbXA74UGy8C+eglUjcpvASOOjVRFuD6poMAMliKkKAXBhQaFx/Rvhz27A2PIPv9lL2i4UQ==",
+ "dependencies": {
+ "@parcel/types": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/reporter-cli": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.4.0.tgz",
+ "integrity": "sha512-Q9bIFMaGvQgypCDxdMEKOwrJzIHAXScKkuFsqTHnUL6mmH3Mo2CoEGAq/wpMXuPhXRn1dPJcHgTNDwZ2fSzz0A==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "chalk": "^4.1.0",
+ "term-size": "^2.2.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/reporter-dev-server": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.4.0.tgz",
+ "integrity": "sha512-24h++wevs7XYuX4dKa4PUfLSstvn3g7udajFv6CeQoME+dR25RL/wH/2LUbhV5ilgXXab76rWIndSqp78xHxPA==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/resolver-default": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.4.0.tgz",
+ "integrity": "sha512-K7pIIFmGm1hjg/7Mzkg99i8tfCClKfBUTuc2R5j8cdr2n0mCAi4/f2mFf5svLrb5XZrnDgoQ05tHKklLEfUDUw==",
+ "dependencies": {
+ "@parcel/node-resolver-core": "2.4.0",
+ "@parcel/plugin": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/runtime-browser-hmr": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.4.0.tgz",
+ "integrity": "sha512-swPFtvxGoCA9LEjU/pHPNjxG1l0fte8447zXwRN/AaYrtjNu9Ww117OSKCyvCnE143E79jZOFStodTQGFuH+9A==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/runtime-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.4.0.tgz",
+ "integrity": "sha512-67OOvmkDdtmgzZVP/EyAzoXhJ/Ug3LUVUt7idg9arun5rdJptqEb3Um3wmH0zjcNa9jMbJt7Kl5x1wA8dJgPYg==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/runtime-react-refresh": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.4.0.tgz",
+ "integrity": "sha512-flnr+bf06lMZPbXZZLLaFNrPHvYpfuXTVovEghyUW46qLVpaHj33dpsU/LqZplIuHgBp2ibgrKhr/hY9ell68w==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "react-refresh": "^0.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/runtime-service-worker": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.4.0.tgz",
+ "integrity": "sha512-RgM5QUqW22WzstW03CtV+Oih8VGVuwsf94Cc4hLouU2EAD0NUJgATWbFocZVTZIBTKELAWh2gjpSQDdnL4Ur+A==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/source-map": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.0.2.tgz",
+ "integrity": "sha512-NnUrPYLpYB6qyx2v6bcRPn/gVigmGG6M6xL8wIg/i0dP1GLkuY1nf+Hqdf63FzPTqqT7K3k6eE5yHPQVMO5jcA==",
+ "dependencies": {
+ "detect-libc": "^1.0.3"
+ },
+ "engines": {
+ "node": "^12.18.3 || >=14"
+ }
+ },
+ "node_modules/@parcel/transformer-babel": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.4.0.tgz",
+ "integrity": "sha512-iWDa7KzJTMP3HNmrYxiYq/S6redk2qminx/9MwmKIN9jzm8mgts2Lj9lOg/t66YaDGky6JAvw4DhB2qW4ni6yQ==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "browserslist": "^4.6.6",
+ "json5": "^2.2.0",
+ "nullthrows": "^1.1.1",
+ "semver": "^5.7.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-css": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.4.0.tgz",
+ "integrity": "sha512-D2u48LuiQsQvbknABE0wVKFp9r6yCgWrHKEP1J6EJ31c49nXGXDHrpHJJwqq9BvAs/124eBI5mSsehTJyFEMwg==",
+ "dependencies": {
+ "@parcel/css": "^1.7.2",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "browserslist": "^4.6.6",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-html": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.4.0.tgz",
+ "integrity": "sha512-2/8X/o5QaCNVPr4wkxLCUub7v/YVvVN2L5yCEcTatNeFhNg/2iz7P2ekfqOaoDCHWZEOBT1VTwPbdBt+TMM71Q==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "nullthrows": "^1.1.1",
+ "posthtml": "^0.16.5",
+ "posthtml-parser": "^0.10.1",
+ "posthtml-render": "^3.0.0",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-html/node_modules/posthtml-parser": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz",
+ "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==",
+ "dependencies": {
+ "htmlparser2": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@parcel/transformer-image": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.4.0.tgz",
+ "integrity": "sha512-JZkQvGGoGiD0AVKLIbAYYUWxepMmUaWZ4XXx71MmS/kA7cUDwTZ0CXq63YnSY1m+DX+ClTuTN8mBlwe2dkcGbA==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/workers": "2.4.0",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ }
+ },
+ "node_modules/@parcel/transformer-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.4.0.tgz",
+ "integrity": "sha512-eeLHFwv3jT3GmIxpLC7B8EXExGK0MFaK91HXljOMh6l8a+GlQYw27MSFQVtoXr0Olx9Uq2uvjXP1+zSsq3LQUQ==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/utils": "2.4.0",
+ "@parcel/workers": "2.4.0",
+ "@swc/helpers": "^0.3.6",
+ "browserslist": "^4.6.6",
+ "detect-libc": "^1.0.3",
+ "nullthrows": "^1.1.1",
+ "regenerator-runtime": "^0.13.7",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-json": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.4.0.tgz",
+ "integrity": "sha512-3nR+d39mbURoXIypDfVCaxpwL65qMV+h8SLD78up2uhaRGklHQfN7GuemR7L+mcVAgNrmwVvZHhyNjdgYwWqqg==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "json5": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-postcss": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.4.0.tgz",
+ "integrity": "sha512-ijIa2x+dbKnJhr7zO5WlXkvuj832fDoGksMBk2DX3u2WMrbh2rqVWPpGFsDhESx7EAy38nUoV/5KUdrNqUmCEA==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "clone": "^2.1.1",
+ "nullthrows": "^1.1.1",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-posthtml": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.4.0.tgz",
+ "integrity": "sha512-xoL3AzgtVeRRAo6bh0AHAYm9bt1jZ+HiH86/7oARj/uJs6Wd8kXK/DZf6fH+F87hj4e7bnjmDDc0GPVK0lPz1w==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "nullthrows": "^1.1.1",
+ "posthtml": "^0.16.5",
+ "posthtml-parser": "^0.10.1",
+ "posthtml-render": "^3.0.0",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-posthtml/node_modules/posthtml-parser": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz",
+ "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==",
+ "dependencies": {
+ "htmlparser2": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@parcel/transformer-raw": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.4.0.tgz",
+ "integrity": "sha512-fciFbNrzj0kLlDgr6OsI0PUv414rVygDWAsgbCCq4BexDkuemMs9f9FjMctx9B2VZlctE8dTT4RGkuQumTIpUg==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-react-refresh-wrap": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.4.0.tgz",
+ "integrity": "sha512-9+f6sGOWkf0jyUQ1CuFWk+04Mq3KTOCU9kRiwCHX1YdUCv5uki6r9XUSpqiYodrV+L6w9CCwLvGMLCDHxtCxMg==",
+ "dependencies": {
+ "@parcel/plugin": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "react-refresh": "^0.9.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-svg": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.4.0.tgz",
+ "integrity": "sha512-D+yzVtSxtQML3d26fd/g4E/xYW68+OMbMUVLXORtoYMU42fnXQkJP6jGOdqy8Td+WORNY7EwVtQnESLwhBmolw==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/plugin": "2.4.0",
+ "nullthrows": "^1.1.1",
+ "posthtml": "^0.16.5",
+ "posthtml-parser": "^0.10.1",
+ "posthtml-render": "^3.0.0",
+ "semver": "^5.7.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0",
+ "parcel": "^2.4.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/transformer-svg/node_modules/posthtml-parser": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz",
+ "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==",
+ "dependencies": {
+ "htmlparser2": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@parcel/types": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.4.0.tgz",
+ "integrity": "sha512-nysGIbBEnp+7R+tKTysdcTFOZDTCodsiXFeAhYQa5bhiOnG1l9gzhxQnE2OsdsgvMm40IOsgKprqvM/DbdLfnQ==",
+ "dependencies": {
+ "@parcel/cache": "2.4.0",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/fs": "2.4.0",
+ "@parcel/package-manager": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "@parcel/workers": "2.4.0",
+ "utility-types": "^3.10.0"
+ }
+ },
+ "node_modules/@parcel/utils": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.4.0.tgz",
+ "integrity": "sha512-sdNo+mZqDZT8LJYB6WWRKa4wFVZcK6Zb5Jh6Du76QvXXwHbPIQNZgJBb6gd/Rbk4GLOp2tW7MnBfq6zP9E9E2g==",
+ "dependencies": {
+ "@parcel/codeframe": "2.4.0",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/hash": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/markdown-ansi": "2.4.0",
+ "@parcel/source-map": "^2.0.0",
+ "chalk": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.0.5.tgz",
+ "integrity": "sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-addon-api": "^3.2.1",
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/workers": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.4.0.tgz",
+ "integrity": "sha512-eSFyvEoXXPgFzQfKIlpkUjpHfIbezUCRFTPKyJAKCxvU5DSXOpb1kz5vDESWQ4qTZXKnrKvxS1PPWN6bam9z0g==",
+ "dependencies": {
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/types": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "chrome-trace-event": "^1.0.2",
+ "nullthrows": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "peerDependencies": {
+ "@parcel/core": "^2.4.0"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.8.tgz",
+ "integrity": "sha512-aWItSZvJj4+GI6FWkjZR13xPNPctq2RRakzo+O6vN7bC2yjwdg5EFpgaSAUn95b7BGSgcflvzVDPoKmJv24IOg=="
+ },
+ "node_modules/@trysound/sax": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.4",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+ "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
+ },
+ "node_modules/@types/react": {
+ "version": "17.0.43",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz",
+ "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "@types/scheduler": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/scheduler": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+ "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
+ },
+ "node_modules/16": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/16/-/16-0.0.2.tgz",
+ "integrity": "sha512-AhG4lpdn+/it+U5Xl1bm5SbaHYTH5NfU/vXZkP7E7CHjtVtITuFVZKa3AZP3gN38RDJHYYtEqWmqzCutlXaR7w==",
+ "dependencies": {
+ "numeric": "^1.2.6"
+ }
+ },
+ "node_modules/abortcontroller-polyfill": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz",
+ "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q=="
+ },
+ "node_modules/acorn": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+ "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/base-x": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+ "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+ },
+ "node_modules/browserslist": {
+ "version": "4.20.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
+ "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001317",
+ "electron-to-chromium": "^1.4.84",
+ "escalade": "^3.1.1",
+ "node-releases": "^2.0.2",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001320",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz",
+ "integrity": "sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/classnames": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+ "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+ },
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+ "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+ "dependencies": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "node_modules/convert-source-map/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/core-js": {
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
+ "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+ "hasInstallScript": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
+ "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
+ "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^5.1.0",
+ "domhandler": "^4.3.0",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+ "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+ "dependencies": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+ "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/csso": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+ "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+ "dependencies": {
+ "css-tree": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+ "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz",
+ "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/dotenv-expand": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA=="
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.92",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.92.tgz",
+ "integrity": "sha512-YAVbvQIcDE/IJ/vzDMjD484/hsRbFPW2qXJPaYTfOhtligmfYEYOep+5QojpaEU9kq6bMvNeC2aG7arYvTHYsA=="
+ },
+ "node_modules/entities": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-port": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz",
+ "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.13.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz",
+ "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/htmlnano": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.0.tgz",
+ "integrity": "sha512-thKQfhcp2xgtsWNE27A2bliEeqVL5xjAgGn0wajyttvFFsvFWWah1ntV9aEX61gz0T6MBQ5xK/1lXuEumhJTcg==",
+ "dependencies": {
+ "cosmiconfig": "^7.0.1",
+ "posthtml": "^0.16.5",
+ "timsort": "^0.3.0"
+ },
+ "peerDependencies": {
+ "cssnano": "^5.0.11",
+ "postcss": "^8.3.11",
+ "purgecss": "^4.0.3",
+ "relateurl": "^0.2.7",
+ "srcset": "^5.0.0",
+ "svgo": "^2.8.0",
+ "terser": "^5.10.0",
+ "uncss": "^0.17.3"
+ },
+ "peerDependenciesMeta": {
+ "cssnano": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "purgecss": {
+ "optional": true
+ },
+ "relateurl": {
+ "optional": true
+ },
+ "srcset": {
+ "optional": true
+ },
+ "svgo": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "uncss": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
+ "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.2",
+ "domutils": "^2.8.0",
+ "entities": "^3.0.1"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
+ "node_modules/is-json": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
+ "integrity": "sha1-a+Fm0USCihMdaGiRuYPfYsOUkf8="
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+ },
+ "node_modules/json-source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz",
+ "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg=="
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keycode": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz",
+ "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg=="
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+ },
+ "node_modules/lmdb": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.2.4.tgz",
+ "integrity": "sha512-gto+BB2uEob8qRiTlOq+R3uX0YNHsX9mjxj9Sbdue/LIKqu6IlZjrsjKeGyOMquc/474GEqFyX2pdytpydp0rQ==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "msgpackr": "^1.5.4",
+ "nan": "^2.14.2",
+ "node-gyp-build": "^4.2.3",
+ "ordered-binary": "^1.2.4",
+ "weak-lru-cache": "^1.2.2"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/msgpackr": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.5.5.tgz",
+ "integrity": "sha512-JG0V47xRIQ9pyUnx6Hb4+3TrQoia2nA3UIdmyTldhxaxtKFkekkKpUW/N6fwHwod9o4BGuJGtouxOk+yCP5PEA==",
+ "optionalDependencies": {
+ "msgpackr-extract": "^1.0.14"
+ }
+ },
+ "node_modules/msgpackr-extract": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz",
+ "integrity": "sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA==",
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "nan": "^2.14.2",
+ "node-gyp-build": "^4.2.3"
+ }
+ },
+ "node_modules/nan": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+ "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
+ },
+ "node_modules/node-addon-api": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+ "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz",
+ "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==",
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+ "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
+ },
+ "node_modules/nth-check": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+ "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/nullthrows": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
+ "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw=="
+ },
+ "node_modules/numeric": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz",
+ "integrity": "sha512-avBiDAP8siMa7AfJgYyuxw1oyII4z2sswS23+O+ZfV28KrtNzy0wxUFwi4f3RyM4eeeXNs1CThxR7pb5QQcMiw=="
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ordered-binary": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.2.4.tgz",
+ "integrity": "sha512-A/csN0d3n+igxBPfUrjbV5GC69LWj2pjZzAAeeHXLukQ4+fytfP4T1Lg0ju7MSPSwq7KtHkGaiwO8URZN5IpLg=="
+ },
+ "node_modules/parcel": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.4.0.tgz",
+ "integrity": "sha512-dPWpu4RnxG9HqiLvaF8COEWEnT/KrigrC6PyPaQ0zEgpBfp7/jzXZFBVaZk2N+lpvrbNEYMjN9bv5UQGJJszIw==",
+ "dependencies": {
+ "@parcel/config-default": "2.4.0",
+ "@parcel/core": "2.4.0",
+ "@parcel/diagnostic": "2.4.0",
+ "@parcel/events": "2.4.0",
+ "@parcel/fs": "2.4.0",
+ "@parcel/logger": "2.4.0",
+ "@parcel/package-manager": "2.4.0",
+ "@parcel/reporter-cli": "2.4.0",
+ "@parcel/reporter-dev-server": "2.4.0",
+ "@parcel/utils": "2.4.0",
+ "chalk": "^4.1.0",
+ "commander": "^7.0.0",
+ "get-port": "^4.2.0",
+ "v8-compile-cache": "^2.0.0"
+ },
+ "bin": {
+ "parcel": "lib/bin.js"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
+ },
+ "node_modules/posthtml": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
+ "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==",
+ "dependencies": {
+ "posthtml-parser": "^0.11.0",
+ "posthtml-render": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/posthtml-parser": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
+ "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
+ "dependencies": {
+ "htmlparser2": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/posthtml-render": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
+ "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
+ "dependencies": {
+ "is-json": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/prop-types-extra": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
+ "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
+ "dependencies": {
+ "react-is": "^16.3.2",
+ "warning": "^4.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=0.14.0"
+ }
+ },
+ "node_modules/react": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
+ "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-bootstrap": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.33.1.tgz",
+ "integrity": "sha512-qWTRravSds87P8WC82tETy2yIso8qDqlIm0czsrduCaYAFtHuyLu0XDbUlfLXeRzqgwm5sRk2wRaTNoiVkk/YQ==",
+ "dependencies": {
+ "@babel/runtime-corejs2": "^7.0.0",
+ "classnames": "^2.2.5",
+ "dom-helpers": "^3.2.0",
+ "invariant": "^2.2.4",
+ "keycode": "^2.2.0",
+ "prop-types": "^15.6.1",
+ "prop-types-extra": "^1.0.1",
+ "react-overlays": "^0.9.0",
+ "react-prop-types": "^0.4.0",
+ "react-transition-group": "^2.0.0",
+ "uncontrollable": "^7.0.2",
+ "warning": "^3.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0",
+ "react-dom": ">=16.3.0"
+ }
+ },
+ "node_modules/react-bootstrap/node_modules/dom-helpers": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
+ "node_modules/react-bootstrap/node_modules/react-transition-group": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+ "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+ "dependencies": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0",
+ "react-dom": ">=15.0.0"
+ }
+ },
+ "node_modules/react-bootstrap/node_modules/warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+ "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
+ "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "scheduler": "^0.20.2"
+ },
+ "peerDependencies": {
+ "react": "17.0.2"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-overlays": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.9.3.tgz",
+ "integrity": "sha512-u2T7nOLnK+Hrntho4p0Nxh+BsJl0bl4Xuwj/Y0a56xywLMetgAfyjnDVrudLXsNcKGaspoC+t3C1V80W9QQTdQ==",
+ "dependencies": {
+ "classnames": "^2.2.5",
+ "dom-helpers": "^3.2.1",
+ "prop-types": "^15.5.10",
+ "prop-types-extra": "^1.0.1",
+ "react-transition-group": "^2.2.1",
+ "warning": "^3.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.3.0",
+ "react-dom": ">=16.3.0"
+ }
+ },
+ "node_modules/react-overlays/node_modules/dom-helpers": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "dependencies": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
+ "node_modules/react-overlays/node_modules/react-transition-group": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+ "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+ "dependencies": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0",
+ "react-dom": ">=15.0.0"
+ }
+ },
+ "node_modules/react-overlays/node_modules/warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+ "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/react-prop-types": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz",
+ "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=",
+ "dependencies": {
+ "warning": "^3.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=0.14.0"
+ }
+ },
+ "node_modules/react-prop-types/node_modules/warning": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+ "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
+ "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-transition-group": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+ "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.9",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/scheduler": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+ "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/stable": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility"
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/svgo": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+ "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+ "dependencies": {
+ "@trysound/sax": "0.2.0",
+ "commander": "^7.2.0",
+ "css-select": "^4.1.3",
+ "css-tree": "^1.1.3",
+ "csso": "^4.2.0",
+ "picocolors": "^1.0.0",
+ "stable": "^0.1.8"
+ },
+ "bin": {
+ "svgo": "bin/svgo"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/term-size": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
+ "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.17.3",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz",
+ "integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.2",
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "node_modules/timsort": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/uncontrollable": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
+ "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.6.3",
+ "@types/react": ">=16.9.11",
+ "invariant": "^2.2.4",
+ "react-lifecycles-compat": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react": ">=15.0.0"
+ }
+ },
+ "node_modules/utility-types": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
+ "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/weak-lru-cache": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+ "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw=="
+ },
+ "node_modules/xxhash-wasm": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
+ "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA=="
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "engines": {
+ "node": ">= 6"
+ }
+ }
+ },
"dependencies": {
"16": {
"version": "0.0.2",
@@ -14,7 +3146,6 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz",
"integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==",
- "dev": true,
"requires": {
"@jridgewell/trace-mapping": "^0.3.0"
}
@@ -30,14 +3161,12 @@
"@babel/compat-data": {
"version": "7.17.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz",
- "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==",
- "dev": true
+ "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ=="
},
"@babel/core": {
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz",
"integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==",
- "dev": true,
"requires": {
"@ampproject/remapping": "^2.1.0",
"@babel/code-frame": "^7.16.7",
@@ -59,8 +3188,7 @@
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
@@ -93,7 +3221,6 @@
"version": "7.17.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz",
"integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==",
- "dev": true,
"requires": {
"@babel/compat-data": "^7.17.7",
"@babel/helper-validator-option": "^7.16.7",
@@ -104,8 +3231,7 @@
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
@@ -177,7 +3303,6 @@
"version": "7.17.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz",
"integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==",
- "dev": true,
"requires": {
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-module-imports": "^7.16.7",
@@ -218,7 +3343,6 @@
"version": "7.17.7",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz",
"integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==",
- "dev": true,
"requires": {
"@babel/types": "^7.17.0"
}
@@ -245,7 +3369,6 @@
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz",
"integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==",
- "dev": true,
"requires": {
"@babel/template": "^7.16.7",
"@babel/traverse": "^7.17.3",
@@ -447,26 +3570,47 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
"@jridgewell/resolve-uri": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
- "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
- "dev": true
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
+ },
+ "@jridgewell/source-map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz",
+ "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==",
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
},
"@jridgewell/sourcemap-codec": {
- "version": "1.4.11",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
- "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==",
- "dev": true
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"@jridgewell/trace-mapping": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz",
- "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==",
- "dev": true,
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
"requires": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@parcel/bundler-default": {
@@ -1308,7 +4452,6 @@
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
- "dev": true,
"requires": {
"safe-buffer": "~5.1.1"
},
@@ -1316,8 +4459,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
@@ -1480,8 +4622,7 @@
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
},
"get-port": {
"version": "4.2.0",
@@ -1570,9 +4711,9 @@
"integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg=="
},
"json5": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
- "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
},
"keycode": {
"version": "2.2.1",
@@ -2013,13 +5154,13 @@
"integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="
},
"terser": {
- "version": "5.12.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
- "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
+ "version": "5.17.3",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.3.tgz",
+ "integrity": "sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg==",
"requires": {
+ "@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
"commander": "^2.20.0",
- "source-map": "~0.7.2",
"source-map-support": "~0.5.20"
},
"dependencies": {
@@ -2027,11 +5168,6 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
}
}
},
diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh
new file mode 100755
index 000000000..0565621cf
--- /dev/null
+++ b/scripts/deploy_star_burger.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+
+if [ "$EUID" -ne 0 ]
+ then
+ echo "Please run as root."
+ exit
+fi
+
+git pull
+npm ci --dev
+npm audit fix
+venv/bin/pip install -U -r requirements.txt
+node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
+venv/bin/python manage.py collectstatic --noinput
+venv/bin/python manage.py migrate
+systemctl restart starburger-django.service
+systemctl reload nginx
+
+echo "Project updated."
From 4518911a20d525054283e059c7d69bfadf212dd6 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 14 May 2023 11:36:35 +0300
Subject: [PATCH 40/67] notify rollbar of deploys
---
README.md | 4 +--
scripts/deploy_star_burger.sh | 2 ++
.../{deploy_postgres.sh => init_postgres.sh} | 0
scripts/report_deploy_rollbar.py | 26 +++++++++++++++++++
4 files changed, 30 insertions(+), 2 deletions(-)
rename scripts/{deploy_postgres.sh => init_postgres.sh} (100%)
create mode 100644 scripts/report_deploy_rollbar.py
diff --git a/README.md b/README.md
index 0e29d84fa..5507b5f4d 100644
--- a/README.md
+++ b/README.md
@@ -162,8 +162,8 @@ Parcel будет следить за файлами в каталоге `bundle
Убедиться, что на сервере установлен Docker.
Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД:
```sh
-chmod u+x scripts/deploy_postgres.sh
-scripts/deploy_postgres.sh
+chmod u+x scripts/init_postgres.sh
+scripts/init_postgres.sh
```
### Подтянуть изменения в репозитории и перезапустить сервисы:
diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh
index 0565621cf..dd2ae9108 100755
--- a/scripts/deploy_star_burger.sh
+++ b/scripts/deploy_star_burger.sh
@@ -18,4 +18,6 @@ venv/bin/python manage.py migrate
systemctl restart starburger-django.service
systemctl reload nginx
+venv/bin/python scripts/report_deploy_rollbar.py
+
echo "Project updated."
diff --git a/scripts/deploy_postgres.sh b/scripts/init_postgres.sh
similarity index 100%
rename from scripts/deploy_postgres.sh
rename to scripts/init_postgres.sh
diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py
new file mode 100644
index 000000000..d021cff23
--- /dev/null
+++ b/scripts/report_deploy_rollbar.py
@@ -0,0 +1,26 @@
+import subprocess
+
+import requests
+from environs import Env
+
+
+env = Env()
+env.read_env()
+
+commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], encoding='UTF-8').strip()
+
+url = 'https://api.rollbar.com/api/1/deploy'
+headers = {
+ 'accept': 'application/json',
+ 'content-type': 'application/json',
+ 'X-Rollbar-Access-Token': env.str('ROLLBAR_TOKEN'),
+}
+data = {
+ 'environment': env.str('ROLLBAR_ENV'),
+ 'revision': commit_hash,
+ # 'status': 'succeeded',
+ 'rollbar_username': env.str('ROLLBAR_USERNAME')
+}
+
+response = requests.post(url, headers=headers, json=data)
+response.raise_for_status()
From d72fb6e480509d89e842d8379bbfb8d58407394f Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 14 May 2023 11:43:47 +0300
Subject: [PATCH 41/67] use a full commit hash
---
scripts/report_deploy_rollbar.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py
index d021cff23..7e622cca7 100644
--- a/scripts/report_deploy_rollbar.py
+++ b/scripts/report_deploy_rollbar.py
@@ -1,3 +1,5 @@
+"""Helper script to be used at the end of deploy_star_burger.sh"""
+
import subprocess
import requests
@@ -7,7 +9,7 @@
env = Env()
env.read_env()
-commit_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], encoding='UTF-8').strip()
+commit_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'], encoding='UTF-8').strip()
url = 'https://api.rollbar.com/api/1/deploy'
headers = {
@@ -15,12 +17,11 @@
'content-type': 'application/json',
'X-Rollbar-Access-Token': env.str('ROLLBAR_TOKEN'),
}
-data = {
+payload = {
'environment': env.str('ROLLBAR_ENV'),
'revision': commit_hash,
- # 'status': 'succeeded',
'rollbar_username': env.str('ROLLBAR_USERNAME')
}
-response = requests.post(url, headers=headers, json=data)
+response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
From cdf536c62728af4ac2f42b99dccb01dfc9274735 Mon Sep 17 00:00:00 2001
From: mavel-x <96616212+mavel-x@users.noreply.github.com>
Date: Sun, 14 May 2023 12:17:16 +0300
Subject: [PATCH 42/67] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5507b5f4d..0c70d3e58 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Сайт доставки еды Star Burger
+# Сайт доставки еды [Star Burger](https://starburger.mavel.cc/)
Это сайт сети ресторанов Star Burger. Здесь можно заказать превосходные бургеры с доставкой на дом.
From 99f8c559a11f1c63911907b1746db9f0be7dc8be Mon Sep 17 00:00:00 2001
From: mavel
Date: Tue, 16 May 2023 01:19:32 +0300
Subject: [PATCH 43/67] unrequire Rollbar, update README
---
README.md | 6 ++++++
star_burger/settings.py | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 0c70d3e58..77f727166 100644
--- a/README.md
+++ b/README.md
@@ -166,6 +166,12 @@ chmod u+x scripts/init_postgres.sh
scripts/init_postgres.sh
```
+Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup)
+и добавьте следующие переменные в `.env`:
+- `ROLLBAR_TOKEN=...`
+- `ROLLBAR_ENV=...` - development/production/...
+- `ROLLBAR_USERNAME=...`
+
### Подтянуть изменения в репозитории и перезапустить сервисы:
```shell
scripts/deploy_star_burger.sh
diff --git a/star_burger/settings.py b/star_burger/settings.py
index 3f8a7e827..f50f2a1fb 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -134,7 +134,7 @@
YANDEX_GEO_KEY = env.str('YANDEX_GEO_KEY')
ROLLBAR = {
- 'access_token': env.str('ROLLBAR_TOKEN'),
+ 'access_token': env.str('ROLLBAR_TOKEN', None),
'environment': env.str('ROLLBAR_ENV', 'development'),
'root': BASE_DIR,
}
From 64f0fbf58c5a7e222e3ec5ae0d63bc4ea9dc3577 Mon Sep 17 00:00:00 2001
From: mavel
Date: Wed, 17 May 2023 14:20:22 +0300
Subject: [PATCH 44/67] add no-input to script
---
scripts/deploy_star_burger.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh
index dd2ae9108..bab2c0b81 100755
--- a/scripts/deploy_star_burger.sh
+++ b/scripts/deploy_star_burger.sh
@@ -14,7 +14,7 @@ npm audit fix
venv/bin/pip install -U -r requirements.txt
node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
venv/bin/python manage.py collectstatic --noinput
-venv/bin/python manage.py migrate
+venv/bin/python manage.py migrate --noinput
systemctl restart starburger-django.service
systemctl reload nginx
From 76eee59435f2a8cffc56eb42ad4606e5c979819a Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 2 Jul 2023 16:21:30 +0200
Subject: [PATCH 45/67] prepare for docker compose deploy
---
.dockerignore | 3 +++
.gitignore | 1 -
Dockerfile | 20 +++++++++++++++
README.md | 29 ++++++++++------------
bundles/.gitkeep | 0
docker-compose.yml | 42 ++++++++++++++++++++++++++++++++
nginx.conf | 21 ++++++++++++++++
requirements.txt | 1 +
scripts/deploy_star_burger.sh | 23 -----------------
scripts/first_deploy.sh | 11 +++++++++
scripts/init_postgres.sh | 24 ------------------
scripts/report_deploy_rollbar.py | 2 --
star_burger/settings.py | 22 ++++++++++++-----
starburger.service.template | 15 ++++++++++++
14 files changed, 142 insertions(+), 72 deletions(-)
create mode 100644 .dockerignore
create mode 100644 Dockerfile
delete mode 100644 bundles/.gitkeep
create mode 100644 docker-compose.yml
create mode 100644 nginx.conf
delete mode 100755 scripts/deploy_star_burger.sh
create mode 100755 scripts/first_deploy.sh
delete mode 100755 scripts/init_postgres.sh
create mode 100644 starburger.service.template
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..45acdd4a4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+node_modules
+venv
+staticfiles
diff --git a/.gitignore b/.gitignore
index d6c5988cc..28abf0a39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-bundles/**
node_modules/
media/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..38df5dbfa
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM node:16.20 AS parcel
+WORKDIR /app
+COPY package.json package-lock.json ./
+RUN npm ci
+COPY bundles-src/ ./bundles-src/
+RUN ./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
+
+FROM python:3.10
+WORKDIR /app
+COPY --from=parcel /app/bundles/ ./bundles/
+RUN apt update \
+ && apt install -y libpq-dev \
+ && rm -rf /var/lib/apt/lists/*
+COPY requirements.txt ./
+RUN pip install -r requirements.txt
+COPY . .
+RUN python manage.py collectstatic --noinput
+RUN mkdir -p frontend/ \
+ && cp -R bundles/* frontend/ \
+ && cp -R staticfiles/* frontend/
diff --git a/README.md b/README.md
index 77f727166..e3bb4e45b 100644
--- a/README.md
+++ b/README.md
@@ -140,15 +140,9 @@ Parcel будет следить за файлами в каталоге `bundle
## Как запустить prod-версию сайта
-### Собрать фронтенд:
-
-```sh
-./node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
-```
-
### Настроить бэкенд:
-Создать файл `.env` в каталоге `star_burger/` со следующими настройками:
+Создать файл `.env` в корневом каталоге проекта со следующими настройками:
- `DEBUG` — дебаг-режим. Поставьте `False`.
- `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте.
@@ -156,15 +150,9 @@ Parcel будет следить за файлами в каталоге `bundle
- `POSTGRES_USER=...`
- `POSTGRES_PASSWORD=...`
- `POSTGRES_DB=...`
-- `DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` — в этой строке ничего менять не нужно
Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД.
Убедиться, что на сервере установлен Docker.
-Затем запустить из каталога `star-burger/` скрипт, который установит зависимости и подготовит контейнер с БД:
-```sh
-chmod u+x scripts/init_postgres.sh
-scripts/init_postgres.sh
-```
Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup)
и добавьте следующие переменные в `.env`:
@@ -172,11 +160,20 @@ scripts/init_postgres.sh
- `ROLLBAR_ENV=...` - development/production/...
- `ROLLBAR_USERNAME=...`
-### Подтянуть изменения в репозитории и перезапустить сервисы:
-```shell
-scripts/deploy_star_burger.sh
+### Поднять контейнеры и запустить приложение:
+```sh
+scripts/first_deploy.sh
```
+Приложение контролируется systemd и запускается автоматически при перезагрузке сервера.
+Команды, которые могут пригодиться:
+```sh
+systemctl stop starburger # остановить
+systemctl start starburger # запустить
+systemctl status starburger # посмотреть на статус и stdout
+```
+
+
## Цели проекта
Код написан в учебных целях — это урок в курсе по Python и веб-разработке на сайте [Devman](https://dvmn.org). За основу был взят код проекта [FoodCart](https://github.com/Saibharath79/FoodCart).
diff --git a/bundles/.gitkeep b/bundles/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 000000000..f4300fcc7
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,42 @@
+version: '3'
+
+services:
+ db:
+ image: postgres:14
+ environment:
+ - POSTGRES_DB=${POSTGRES_DB}
+ - POSTGRES_USER=${POSTGRES_USER}
+ - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
+ volumes:
+ - postgres:/var/lib/postgresql/data
+
+ django:
+ build: .
+ command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application
+ environment:
+ - POSTGRES_HOST=db
+ - ALLOWED_HOSTS=127.0.0.1,starburger.mavel.cc
+ volumes:
+ - .:/app
+ - frontend:/app/frontend
+ - media:/app/media
+ ports:
+ - "8081:8081"
+ depends_on:
+ - db
+
+ nginx:
+ image: nginx:1.25
+ ports:
+ - "80:80"
+ volumes:
+ - ./nginx.conf:/etc/nginx/conf.d/default.conf
+ - frontend:/frontend
+ - media:/media
+ depends_on:
+ - django
+
+volumes:
+ postgres:
+ frontend:
+ media:
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 000000000..45ea4ae4f
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,21 @@
+server {
+ server_name starburger.mavel.cc www.starburger.mavel.cc;
+ listen 0:80;
+
+ location / {
+ 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_pass http://django:8081/;
+ }
+
+ location /static/ {
+ alias '/frontend/';
+ }
+
+ location /media/ {
+ alias '/media/';
+ }
+
+}
+
diff --git a/requirements.txt b/requirements.txt
index a72d46a29..49c37a21f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,4 @@ djangorestframework==3.14.0
geopy~=2.3.0
rollbar~=0.16.3
psycopg2~=2.9.6
+gunicorn~=20.1.0
diff --git a/scripts/deploy_star_burger.sh b/scripts/deploy_star_burger.sh
deleted file mode 100755
index bab2c0b81..000000000
--- a/scripts/deploy_star_burger.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [ "$EUID" -ne 0 ]
- then
- echo "Please run as root."
- exit
-fi
-
-git pull
-npm ci --dev
-npm audit fix
-venv/bin/pip install -U -r requirements.txt
-node_modules/.bin/parcel build bundles-src/index.js --dist-dir bundles --public-url="./"
-venv/bin/python manage.py collectstatic --noinput
-venv/bin/python manage.py migrate --noinput
-systemctl restart starburger-django.service
-systemctl reload nginx
-
-venv/bin/python scripts/report_deploy_rollbar.py
-
-echo "Project updated."
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
new file mode 100755
index 000000000..c7aeea7bf
--- /dev/null
+++ b/scripts/first_deploy.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+cp starburger.service.template /etc/systemd/system/starburger.service
+systemctl daemon-reload
+systemctl enable starburger
+systemctl start starburger
+
+docker exec star-burger-django-1 python manage.py migrate
+docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json
+
+venv/bin/python scripts/report_deploy_rollbar.py
diff --git a/scripts/init_postgres.sh b/scripts/init_postgres.sh
deleted file mode 100755
index 77481e978..000000000
--- a/scripts/init_postgres.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-if [ "$EUID" -ne 0 ]
- then
- echo "Please run as root."
- exit
-fi
-
-if [ $( docker ps -a | grep starburger-postgres | wc -l ) -gt 0 ]
- then
- echo "Postgres container already exists."
- exit
-fi
-
-apt-get install libpq-dev
-
-docker run -d --name starburger-postgres \
---env-file .env -p 5432:5432 \
--v starburger_postgres:/var/lib/postgresql/data \
-postgres:14
-sleep 1
-venv/bin/python ./manage.py migrate
-venv/bin/python ./manage.py loaddata data/db_dump.json
-docker stop -t 10 starburger-postgres
diff --git a/scripts/report_deploy_rollbar.py b/scripts/report_deploy_rollbar.py
index 7e622cca7..a955c5e46 100644
--- a/scripts/report_deploy_rollbar.py
+++ b/scripts/report_deploy_rollbar.py
@@ -1,5 +1,3 @@
-"""Helper script to be used at the end of deploy_star_burger.sh"""
-
import subprocess
import requests
diff --git a/star_burger/settings.py b/star_burger/settings.py
index f50f2a1fb..d40bb1d75 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -85,12 +85,22 @@
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
-DATABASES = {
- 'default': env.dj_db_url(
- 'DB_URL',
- default=f'sqlite:////{os.path.join(BASE_DIR, "db.sqlite3")}'
- )
-}
+if not env.str('POSTGRES_USER', None):
+ DEFAULT_DATABASE = {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': BASE_DIR / 'db.sqlite3',
+ }
+else:
+ DEFAULT_DATABASE = {
+ 'ENGINE': env.str('DB_ENGINE', 'django.db.backends.postgresql'),
+ 'NAME': env.str('POSTGRES_DB'),
+ 'USER': env.str('POSTGRES_USER'),
+ 'PASSWORD': env.str('POSTGRES_PASSWORD'),
+ 'HOST': env.str('POSTGRES_HOST', '127.0.0.1'),
+ 'PORT': env.str('POSTGRES_PORT', '5432'),
+ }
+
+DATABASES = {'default': DEFAULT_DATABASE}
AUTH_PASSWORD_VALIDATORS = [
{
diff --git a/starburger.service.template b/starburger.service.template
new file mode 100644
index 000000000..13f2d0319
--- /dev/null
+++ b/starburger.service.template
@@ -0,0 +1,15 @@
+[Unit]
+Description=Starburger Docker Compose App Service
+Requires=docker.service
+After=docker.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+WorkingDirectory=/opt/star-burger/project
+ExecStart=/usr/local/bin/docker-compose up -d
+ExecStop=/usr/local/bin/docker-compose down
+TimeoutStartSec=0
+
+[Install]
+WantedBy=multi-user.target
From 707c22c5ad450c195f2455771d5e7df57d1370a4 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 2 Jul 2023 19:35:21 +0200
Subject: [PATCH 46/67] fix broken commands
---
scripts/first_deploy.sh | 4 +++-
starburger.service.template | 4 ++--
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
index c7aeea7bf..943782bac 100755
--- a/scripts/first_deploy.sh
+++ b/scripts/first_deploy.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+set -e
+
cp starburger.service.template /etc/systemd/system/starburger.service
systemctl daemon-reload
systemctl enable starburger
@@ -8,4 +10,4 @@ systemctl start starburger
docker exec star-burger-django-1 python manage.py migrate
docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json
-venv/bin/python scripts/report_deploy_rollbar.py
+python scripts/report_deploy_rollbar.py
diff --git a/starburger.service.template b/starburger.service.template
index 13f2d0319..09a83b284 100644
--- a/starburger.service.template
+++ b/starburger.service.template
@@ -7,8 +7,8 @@ After=docker.service
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/star-burger/project
-ExecStart=/usr/local/bin/docker-compose up -d
-ExecStop=/usr/local/bin/docker-compose down
+ExecStart=docker compose up -d
+ExecStop=docker compose down
TimeoutStartSec=0
[Install]
From 89407b355779414d073d04665fd688cf3768e3a7 Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 2 Jul 2023 19:40:38 +0200
Subject: [PATCH 47/67] fix broken path
---
starburger.service.template | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/starburger.service.template b/starburger.service.template
index 09a83b284..68f0dbe41 100644
--- a/starburger.service.template
+++ b/starburger.service.template
@@ -6,7 +6,7 @@ After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
-WorkingDirectory=/opt/star-burger/project
+WorkingDirectory=/opt/star-burger/
ExecStart=docker compose up -d
ExecStop=docker compose down
TimeoutStartSec=0
From 581cbced7d2e1c2c9ca6510f2fca80d64ea7ee1b Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 2 Jul 2023 23:14:05 +0200
Subject: [PATCH 48/67] add guard checks for missing components, move script
call to container
---
scripts/first_deploy.sh | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
index 943782bac..e82d9819a 100755
--- a/scripts/first_deploy.sh
+++ b/scripts/first_deploy.sh
@@ -2,6 +2,16 @@
set -e
+if [ ! -f data/db_dump.json ]; then
+ echo "Error: data/db_dump.json does not exist"
+ exit 1
+fi
+
+if [ ! -d media ] || [ ! "$(ls -A media)" ]; then
+ echo "Error: media directory does not exist or is empty"
+ exit 1
+fi
+
cp starburger.service.template /etc/systemd/system/starburger.service
systemctl daemon-reload
systemctl enable starburger
@@ -10,4 +20,4 @@ systemctl start starburger
docker exec star-burger-django-1 python manage.py migrate
docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json
-python scripts/report_deploy_rollbar.py
+docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py
From 4cbeaed97f53f735fa5bfe84c852d37233bd8dad Mon Sep 17 00:00:00 2001
From: mavel
Date: Sun, 2 Jul 2023 23:36:31 +0200
Subject: [PATCH 49/67] add a deploy script
---
scripts/deploy.sh | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 scripts/deploy.sh
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
new file mode 100644
index 000000000..89af88abc
--- /dev/null
+++ b/scripts/deploy.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+git pull
+docker compose build
+systemctl restart starburger
+docker exec star-burger-django-1 python manage.py migrate --noinput
+
+docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py
From 3b2899a4e0ca8be301b6b0dd803243a461f8be29 Mon Sep 17 00:00:00 2001
From: mavel
Date: Mon, 3 Jul 2023 01:18:42 +0200
Subject: [PATCH 50/67] update README
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index e3bb4e45b..efa248467 100644
--- a/README.md
+++ b/README.md
@@ -173,6 +173,10 @@ systemctl start starburger # запустить
systemctl status starburger # посмотреть на статус и stdout
```
+### Подтянуть изменения из репозитория и перезапустить сервисы:
+```sh
+scripts/deploy.sh
+```
## Цели проекта
From 1b240a0d4b51a47460f076d04359473aa569b202 Mon Sep 17 00:00:00 2001
From: mavel
Date: Mon, 3 Jul 2023 01:19:14 +0200
Subject: [PATCH 51/67] update link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index efa248467..1c0dbd559 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Сайт доставки еды [Star Burger](https://starburger.mavel.cc/)
+# Сайт доставки еды [Star Burger](http://starburger.mavel.cc/)
Это сайт сети ресторанов Star Burger. Здесь можно заказать превосходные бургеры с доставкой на дом.
From 7e2467950bec98c0f0f47d844f1db186a6ae73e5 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:26:38 +0200
Subject: [PATCH 52/67] certbot config
---
docker-compose.yml | 15 +++++++
nginx.conf | 56 +++++++++++++++++--------
scripts/init_letsencrypt.sh | 83 +++++++++++++++++++++++++++++++++++++
3 files changed, 137 insertions(+), 17 deletions(-)
create mode 100644 scripts/init_letsencrypt.sh
diff --git a/docker-compose.yml b/docker-compose.yml
index f4300fcc7..72f329aa6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,6 +2,7 @@ version: '3'
services:
db:
+ container_name: starburger_db
image: postgres:14
environment:
- POSTGRES_DB=${POSTGRES_DB}
@@ -11,6 +12,7 @@ services:
- postgres:/var/lib/postgresql/data
django:
+ container_name: starburger_django
build: .
command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application
environment:
@@ -26,16 +28,29 @@ services:
- db
nginx:
+ container_name: starburger_nginx
image: nginx:1.25
ports:
- "80:80"
+ - "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- frontend:/frontend
- media:/media
+ - ./data/certbot/conf:/etc/letsencrypt
+ - ./data/certbot/www:/var/www/certbot
depends_on:
- django
+ certbot:
+ container_name: starburger_certbot
+ image: certbot/certbot:v2.6.0
+ volumes:
+ - ./data/certbot/conf:/etc/letsencrypt
+ - ./data/certbot/www:/var/www/certbot
+ depends_on:
+ - nginx
+
volumes:
postgres:
frontend:
diff --git a/nginx.conf b/nginx.conf
index 45ea4ae4f..7e069ba5d 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -1,21 +1,43 @@
server {
- server_name starburger.mavel.cc www.starburger.mavel.cc;
- listen 0:80;
-
- location / {
- 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_pass http://django:8081/;
- }
-
- location /static/ {
- alias '/frontend/';
- }
-
- location /media/ {
- alias '/media/';
- }
+ listen 80;
+ listen [::]:80;
+
+ server_name starburger.mavel.cc www.starburger.mavel.cc;
+
+ location /.well-known/acme-challenge/ {
+ root /var/www/certbot;
+ }
+
+ location / {
+ return 301 https://$host$request_uri;
+ }
+}
+
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl;
+ http2 on;
+ ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
+ include /etc/letsencrypt/options-ssl-nginx.conf;
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+
+ server_name starburger.mavel.cc www.starburger.mavel.cc;
+
+ location / {
+ 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_pass http://django:8081/;
+ }
+
+ location /static/ {
+ alias '/frontend/';
+ }
+
+ location /media/ {
+ alias '/media/';
+ }
}
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
new file mode 100644
index 000000000..febab402b
--- /dev/null
+++ b/scripts/init_letsencrypt.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+set -e
+
+if ! [ -x "$(command -v docker-compose)" ]; then
+ echo 'Error: docker-compose is not installed.' >&2
+ exit 1
+fi
+
+domains=(starburger.mavel.cc www.starburger.mavel.cc)
+rsa_key_size=4096
+data_path="./data/certbot"
+email="starburger@mavel.cc"
+staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits
+nginx_container_name="starburger_nginx"
+
+if [ -d "$data_path" ]; then
+ read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
+ if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
+ exit
+ fi
+fi
+
+
+if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
+ echo "### Downloading recommended TLS parameters ..."
+ mkdir -p "$data_path/conf"
+ curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
+ curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
+ echo
+ fi
+
+echo "### Creating dummy certificate for $domains ..."
+path="/etc/letsencrypt/live/$domains"
+mkdir -p "$data_path/conf/live/$domains"
+docker-compose run --rm --entrypoint "\
+ openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
+ -keyout '$path/privkey.pem' \
+ -out '$path/fullchain.pem' \
+ -subj '/CN=localhost'" certbot
+echo
+
+
+echo "### Starting nginx ..."
+docker-compose up --force-recreate -d nginx
+echo
+
+echo "### Deleting dummy certificate for $domains ..."
+docker-compose run --rm --entrypoint "\
+ rm -Rf /etc/letsencrypt/live/$domains && \
+ rm -Rf /etc/letsencrypt/archive/$domains && \
+ rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
+echo
+
+
+echo "### Requesting Let's Encrypt certificate for $domains ..."
+#Join $domains to -d args
+domain_args=""
+for domain in "${domains[@]}"; do
+ domain_args="$domain_args -d $domain"
+done
+
+# Select appropriate email arg
+case "$email" in
+ "") email_arg="--register-unsafely-without-email" ;;
+ *) email_arg="--email $email" ;;
+esac
+
+# Enable staging mode if needed
+if [ $staging != "0" ]; then staging_arg="--staging"; fi
+
+docker-compose run --rm --entrypoint "\
+ certbot certonly --webroot -w /var/www/certbot \
+ $staging_arg \
+ $email_arg \
+ $domain_args \
+ --rsa-key-size $rsa_key_size \
+ --agree-tos \
+ --force-renewal" certbot
+echo
+
+echo "### Reloading nginx ..."
+docker-compose exec $container_name nginx -s reload
From eb079e2a31709a990dadb0e1dadd02b7cb0daff7 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:27:47 +0200
Subject: [PATCH 53/67] allow running script
---
scripts/init_letsencrypt.sh | 0
1 file changed, 0 insertions(+), 0 deletions(-)
mode change 100644 => 100755 scripts/init_letsencrypt.sh
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
old mode 100644
new mode 100755
From 6522ee0f91e9364512564ec869991bca9fa2663e Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:29:19 +0200
Subject: [PATCH 54/67] remove hyphen in docker compose
---
scripts/init_letsencrypt.sh | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index febab402b..12aa0daac 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -2,8 +2,8 @@
set -e
-if ! [ -x "$(command -v docker-compose)" ]; then
- echo 'Error: docker-compose is not installed.' >&2
+if ! [ -x "$(command -v docker compose)" ]; then
+ echo 'Error: docker compose is not installed.' >&2
exit 1
fi
@@ -33,7 +33,7 @@ if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/
echo "### Creating dummy certificate for $domains ..."
path="/etc/letsencrypt/live/$domains"
mkdir -p "$data_path/conf/live/$domains"
-docker-compose run --rm --entrypoint "\
+docker compose run --rm --entrypoint "\
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
-keyout '$path/privkey.pem' \
-out '$path/fullchain.pem' \
@@ -42,11 +42,11 @@ echo
echo "### Starting nginx ..."
-docker-compose up --force-recreate -d nginx
+docker compose up --force-recreate -d nginx
echo
echo "### Deleting dummy certificate for $domains ..."
-docker-compose run --rm --entrypoint "\
+docker compose run --rm --entrypoint "\
rm -Rf /etc/letsencrypt/live/$domains && \
rm -Rf /etc/letsencrypt/archive/$domains && \
rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
@@ -69,7 +69,7 @@ esac
# Enable staging mode if needed
if [ $staging != "0" ]; then staging_arg="--staging"; fi
-docker-compose run --rm --entrypoint "\
+docker compose run --rm --entrypoint "\
certbot certonly --webroot -w /var/www/certbot \
$staging_arg \
$email_arg \
@@ -80,4 +80,4 @@ docker-compose run --rm --entrypoint "\
echo
echo "### Reloading nginx ..."
-docker-compose exec $container_name nginx -s reload
+docker compose exec $container_name nginx -s reload
From eb51ba028c60cd0f19366d0ec041ab36fd6ac0a8 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:42:13 +0200
Subject: [PATCH 55/67] remove user input check
---
scripts/init_letsencrypt.sh | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index 12aa0daac..c01abafa6 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -76,7 +76,8 @@ docker compose run --rm --entrypoint "\
$domain_args \
--rsa-key-size $rsa_key_size \
--agree-tos \
- --force-renewal" certbot
+ --force-renewal \
+ --no-eff-email" certbot
echo
echo "### Reloading nginx ..."
From 5d5655358ebf4ffadf9967c71447c306358ac9a7 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:46:16 +0200
Subject: [PATCH 56/67] add allow all for certbot challenge
---
nginx.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/nginx.conf b/nginx.conf
index 7e069ba5d..eec1a4f30 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -6,6 +6,7 @@ server {
location /.well-known/acme-challenge/ {
root /var/www/certbot;
+ allow all;
}
location / {
From 3ea9651699683a10aa21b62225826e98557e7944 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 00:54:58 +0200
Subject: [PATCH 57/67] replace domain from example
---
nginx.conf | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nginx.conf b/nginx.conf
index eec1a4f30..5b56f0d99 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -18,8 +18,8 @@ server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
- ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem;
+ ssl_certificate /etc/letsencrypt/live/starburger.mavel.cc/fullchain.pem; # replace domain here
+ ssl_certificate_key /etc/letsencrypt/live/starburger.mavel.cc/privkey.pem; # and here
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
From b015c329547d75ab49982bbcfcfc268c42cc4e31 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 01:31:40 +0200
Subject: [PATCH 58/67] certbot config and renewal services
---
scripts/deploy.sh | 8 ++++----
scripts/first_deploy.sh | 12 ++++++------
scripts/init_letsencrypt.sh | 4 ++--
systemd_units/starburger.target | 7 +++++++
systemd_units/starburger_cert_renewal.service | 7 +++++++
systemd_units/starburger_cert_renewal.timer | 10 ++++++++++
systemd_units/starburger_clearsessions.service | 6 ++++++
systemd_units/starburger_clearsessions.timer | 10 ++++++++++
.../starburger_containers.service | 1 +
9 files changed, 53 insertions(+), 12 deletions(-)
create mode 100644 systemd_units/starburger.target
create mode 100644 systemd_units/starburger_cert_renewal.service
create mode 100644 systemd_units/starburger_cert_renewal.timer
create mode 100644 systemd_units/starburger_clearsessions.service
create mode 100644 systemd_units/starburger_clearsessions.timer
rename starburger.service.template => systemd_units/starburger_containers.service (92%)
diff --git a/scripts/deploy.sh b/scripts/deploy.sh
index 89af88abc..3adc64642 100644
--- a/scripts/deploy.sh
+++ b/scripts/deploy.sh
@@ -3,8 +3,8 @@
set -e
git pull
-docker compose build
-systemctl restart starburger
-docker exec star-burger-django-1 python manage.py migrate --noinput
+docker compose build django
+docker compose restart django
+docker exec starburger_django python manage.py migrate --noinput
-docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py
+docker exec starburger_django python scripts/report_deploy_rollbar.py
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
index e82d9819a..270718978 100755
--- a/scripts/first_deploy.sh
+++ b/scripts/first_deploy.sh
@@ -12,12 +12,12 @@ if [ ! -d media ] || [ ! "$(ls -A media)" ]; then
exit 1
fi
-cp starburger.service.template /etc/systemd/system/starburger.service
+cp systemd_units/* /etc/systemd/system/
systemctl daemon-reload
-systemctl enable starburger
-systemctl start starburger
+systemctl enable starburger.target
+systemctl start starburger.target
-docker exec star-burger-django-1 python manage.py migrate
-docker exec star-burger-django-1 python manage.py loaddata data/db_dump.json
+docker exec starburger_django python manage.py migrate
+docker exec starburger_django python manage.py loaddata data/db_dump.json
-docker exec star-burger-django-1 python scripts/report_deploy_rollbar.py
+docker exec starburger_django python scripts/report_deploy_rollbar.py
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index c01abafa6..6f79ed4ae 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -11,7 +11,7 @@ domains=(starburger.mavel.cc www.starburger.mavel.cc)
rsa_key_size=4096
data_path="./data/certbot"
email="starburger@mavel.cc"
-staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits
+staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
nginx_container_name="starburger_nginx"
if [ -d "$data_path" ]; then
@@ -81,4 +81,4 @@ docker compose run --rm --entrypoint "\
echo
echo "### Reloading nginx ..."
-docker compose exec $container_name nginx -s reload
+docker compose exec "$container_name" nginx -s reload
diff --git a/systemd_units/starburger.target b/systemd_units/starburger.target
new file mode 100644
index 000000000..e5e3a4df3
--- /dev/null
+++ b/systemd_units/starburger.target
@@ -0,0 +1,7 @@
+[Unit]
+Description=Starburger Target
+Requires=starburger_containers.service starburger_cert_renewal.timer starburger_clearsessions.timer
+After=starburger_containers.service starburger_cert_renewal.timer starburger_clearsessions.timer
+
+[Install]
+WantedBy=multi-user.target
diff --git a/systemd_units/starburger_cert_renewal.service b/systemd_units/starburger_cert_renewal.service
new file mode 100644
index 000000000..aa2a3b29a
--- /dev/null
+++ b/systemd_units/starburger_cert_renewal.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Starburger Certbot Renewal
+
+[Service]
+WorkingDirectory=/opt/star-burger
+ExecStart=/usr/bin/docker exec starburger_certbot certbot renew --force-renewal
+ExecStartPost=/usr/bin/docker exec starburger_nginx nginx -s reload
diff --git a/systemd_units/starburger_cert_renewal.timer b/systemd_units/starburger_cert_renewal.timer
new file mode 100644
index 000000000..7ab97b48c
--- /dev/null
+++ b/systemd_units/starburger_cert_renewal.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=Weekly Starburger Certbot Renewal
+PartOf=starburger.target
+
+[Timer]
+OnCalendar=weekly
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/systemd_units/starburger_clearsessions.service b/systemd_units/starburger_clearsessions.service
new file mode 100644
index 000000000..e96c63aa0
--- /dev/null
+++ b/systemd_units/starburger_clearsessions.service
@@ -0,0 +1,6 @@
+[Unit]
+Description=Starburger Django Clear Sessions
+
+[Service]
+WorkingDirectory=/opt/star-burger
+ExecStart=/usr/bin/docker exec starburger_django python manage.py clearsessions
diff --git a/systemd_units/starburger_clearsessions.timer b/systemd_units/starburger_clearsessions.timer
new file mode 100644
index 000000000..18d469256
--- /dev/null
+++ b/systemd_units/starburger_clearsessions.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=Weekly Django Clear Sessions for Starburger
+PartOf=starburger.target
+
+[Timer]
+OnCalendar=weekly
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/starburger.service.template b/systemd_units/starburger_containers.service
similarity index 92%
rename from starburger.service.template
rename to systemd_units/starburger_containers.service
index 68f0dbe41..9f5ead134 100644
--- a/starburger.service.template
+++ b/systemd_units/starburger_containers.service
@@ -2,6 +2,7 @@
Description=Starburger Docker Compose App Service
Requires=docker.service
After=docker.service
+PartOf=starburger.target
[Service]
Type=oneshot
From 0c3460ddd55ee6d010a3de4e8d7a21f4ac2f9664 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 01:36:42 +0200
Subject: [PATCH 59/67] add certbot script call to first_deploy script
---
scripts/first_deploy.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
index 270718978..60aba13f4 100755
--- a/scripts/first_deploy.sh
+++ b/scripts/first_deploy.sh
@@ -12,6 +12,8 @@ if [ ! -d media ] || [ ! "$(ls -A media)" ]; then
exit 1
fi
+scripts/init_letsencrypt.sh
+
cp systemd_units/* /etc/systemd/system/
systemctl daemon-reload
systemctl enable starburger.target
From 23f9c33ced531c15c58c3b90b81933d9d8af820c Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 01:45:42 +0200
Subject: [PATCH 60/67] fix error in variable name
---
scripts/init_letsencrypt.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index 6f79ed4ae..59d049c8a 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -81,4 +81,4 @@ docker compose run --rm --entrypoint "\
echo
echo "### Reloading nginx ..."
-docker compose exec "$container_name" nginx -s reload
+docker compose exec "$nginx_container_name" nginx -s reload
From 17a8e033e72ae60a66a7c1ce6b76a7b61ae24e21 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 11:36:23 +0200
Subject: [PATCH 61/67] use service name instead of container name
---
scripts/init_letsencrypt.sh | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index 59d049c8a..37c788016 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -12,7 +12,6 @@ rsa_key_size=4096
data_path="./data/certbot"
email="starburger@mavel.cc"
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
-nginx_container_name="starburger_nginx"
if [ -d "$data_path" ]; then
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
@@ -81,4 +80,4 @@ docker compose run --rm --entrypoint "\
echo
echo "### Reloading nginx ..."
-docker compose exec "$nginx_container_name" nginx -s reload
+docker compose exec nginx nginx -s reload
From 80fb803d453a5069beee4065381adda462726152 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 11:50:15 +0200
Subject: [PATCH 62/67] remove existing certbot data_dir to avoid filepath
problems with nginx
---
scripts/init_letsencrypt.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index 37c788016..bcaaa5621 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -20,6 +20,8 @@ if [ -d "$data_path" ]; then
fi
fi
+rm -r "$data_path"
+
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
echo "### Downloading recommended TLS parameters ..."
From 43b8685a891579e0413c42b2f3791aa184198d19 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 11:50:30 +0200
Subject: [PATCH 63/67] echo message at the end
---
scripts/first_deploy.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/scripts/first_deploy.sh b/scripts/first_deploy.sh
index 60aba13f4..4200d2bf0 100755
--- a/scripts/first_deploy.sh
+++ b/scripts/first_deploy.sh
@@ -23,3 +23,5 @@ docker exec starburger_django python manage.py migrate
docker exec starburger_django python manage.py loaddata data/db_dump.json
docker exec starburger_django python scripts/report_deploy_rollbar.py
+
+echo "Deploy successful."
From 0e2b4453d89f0c23f66ce673d8c31a7214be91f5 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 12:17:22 +0200
Subject: [PATCH 64/67] take domains and email from .env
---
docker-compose.yml | 1 -
scripts/init_letsencrypt.sh | 6 ++++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index 72f329aa6..70d924739 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,7 +17,6 @@ services:
command: gunicorn -b 0.0.0.0:8081 --workers 3 star_burger.wsgi:application
environment:
- POSTGRES_HOST=db
- - ALLOWED_HOSTS=127.0.0.1,starburger.mavel.cc
volumes:
- .:/app
- frontend:/app/frontend
diff --git a/scripts/init_letsencrypt.sh b/scripts/init_letsencrypt.sh
index bcaaa5621..f71a9d3ca 100755
--- a/scripts/init_letsencrypt.sh
+++ b/scripts/init_letsencrypt.sh
@@ -7,10 +7,12 @@ if ! [ -x "$(command -v docker compose)" ]; then
exit 1
fi
-domains=(starburger.mavel.cc www.starburger.mavel.cc)
+source .env
+
+IFS=',' read -ra domains <<< "$CERT_DOMAINS" # stores `CERT_DOMAINS` from .env in the array `domains`
rsa_key_size=4096
data_path="./data/certbot"
-email="starburger@mavel.cc"
+email="$EMAIL"
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
if [ -d "$data_path" ]; then
From 483c3c24193954d3e43c1a603d37a12e4658d548 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 12:17:34 +0200
Subject: [PATCH 65/67] update README
---
README.md | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 1c0dbd559..638648b84 100644
--- a/README.md
+++ b/README.md
@@ -147,32 +147,45 @@ Parcel будет следить за файлами в каталоге `bundle
- `DEBUG` — дебаг-режим. Поставьте `False`.
- `SECRET_KEY` — секретный ключ проекта. Он отвечает за шифрование на сайте. Например, им зашифрованы все пароли на вашем сайте.
- `ALLOWED_HOSTS` — [см. документацию Django](https://docs.djangoproject.com/en/3.1/ref/settings/#allowed-hosts)
-- `POSTGRES_USER=...`
-- `POSTGRES_PASSWORD=...`
-- `POSTGRES_DB=...`
+- `POSTGRES_USER`
+- `POSTGRES_PASSWORD`
+- `POSTGRES_DB`
+
+Для SSL-сертификата:
+- `EMAIL`
+- `CERT_DOMAINS` - список доменов в формате `example.org,www.example.org`
Убедиться, что в каталоге `star-burger/data` лежат данные, которые нужно загрузить в БД.
Убедиться, что на сервере установлен Docker.
+Заменить домены в `nginx.conf` на ваши собственные.
+
Чтобы получать мгновенные уведомления об ошибках, подключите свой аккаунт [Rollbar](https://docs.rollbar.com/docs/setup)
и добавьте следующие переменные в `.env`:
-- `ROLLBAR_TOKEN=...`
-- `ROLLBAR_ENV=...` - development/production/...
-- `ROLLBAR_USERNAME=...`
+- `ROLLBAR_TOKEN`
+- `ROLLBAR_ENV` - `development`/`production`/...
+- `ROLLBAR_USERNAME`
+
+### Поднять контейнеры, получить сертификат SSL и запустить приложение:
-### Поднять контейнеры и запустить приложение:
```sh
scripts/first_deploy.sh
```
-Приложение контролируется systemd и запускается автоматически при перезагрузке сервера.
+Приложение контролируется таргетом systemd и запускается автоматически при перезагрузке сервера.
+В таргет включены следующие юниты:
+- starburger_containers.service - запускает и останавливает контейнеры через docker compose
+- starburger_cert_renewal.timer - обновляет сертификат SSL и перезагружает nginx
+- starburger_clearsessions.timer - удаляет устаревшие сессии в Django
+
Команды, которые могут пригодиться:
```sh
-systemctl stop starburger # остановить
-systemctl start starburger # запустить
-systemctl status starburger # посмотреть на статус и stdout
+systemctl stop starburger.target # остановить
+systemctl start starburger.target # запустить
+docker compose logs # посмотреть на stdout контейнеров
```
+
### Подтянуть изменения из репозитория и перезапустить сервисы:
```sh
scripts/deploy.sh
From a5e07e06378e2a20b1e1982cbf381ecf875ebdcc Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 12:17:55 +0200
Subject: [PATCH 66/67] add comments for domains
---
nginx.conf | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nginx.conf b/nginx.conf
index 5b56f0d99..bfc8cac2f 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -2,7 +2,7 @@ server {
listen 80;
listen [::]:80;
- server_name starburger.mavel.cc www.starburger.mavel.cc;
+ server_name starburger.mavel.cc www.starburger.mavel.cc; # replace domain here
location /.well-known/acme-challenge/ {
root /var/www/certbot;
@@ -23,7 +23,7 @@ server {
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
- server_name starburger.mavel.cc www.starburger.mavel.cc;
+ server_name starburger.mavel.cc www.starburger.mavel.cc; # replace domain here
location / {
proxy_set_header Host $host;
From cd87f75c8a553cf66c39c5d60d5edc5581795dd6 Mon Sep 17 00:00:00 2001
From: mavel
Date: Thu, 6 Jul 2023 12:21:55 +0200
Subject: [PATCH 67/67] fix an error in path joining
---
star_burger/settings.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/star_burger/settings.py b/star_burger/settings.py
index d40bb1d75..626c66e23 100644
--- a/star_burger/settings.py
+++ b/star_burger/settings.py
@@ -88,7 +88,7 @@
if not env.str('POSTGRES_USER', None):
DEFAULT_DATABASE = {
'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': BASE_DIR / 'db.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
else:
DEFAULT_DATABASE = {
|