diff --git a/backend/Aries/settings/common.py b/backend/Aries/settings/common.py index 68e2a00..8308b0a 100644 --- a/backend/Aries/settings/common.py +++ b/backend/Aries/settings/common.py @@ -30,6 +30,12 @@ class Common(Configuration): 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + # packages + 'oauth2_provider', + 'rest_framework', + # apps + 'user', + ] MIDDLEWARE = [ @@ -115,3 +121,7 @@ class Common(Configuration): DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + AUTH_USER_MODEL = 'user.User' + + + diff --git a/backend/Aries/urls.py b/backend/Aries/urls.py index a490658..4cdc722 100644 --- a/backend/Aries/urls.py +++ b/backend/Aries/urls.py @@ -14,9 +14,12 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin -from django.urls import path - +from django.urls import path , include +from user import urls as user_urls urlpatterns = [ path('admin/', admin.site.urls), -] + path('user/', include(user_urls)), +]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/Aries/views.py b/backend/Aries/views.py new file mode 100644 index 0000000..9eb9399 --- /dev/null +++ b/backend/Aries/views.py @@ -0,0 +1,18 @@ +from oauth2_provider.models import Application +from rest_framework.exceptions import ParseError +import utils.errors as em +from rest_framework.views import APIView + +class ApplicationBaseView(APIView): + def initial(self, request, *args, **kwargs): + super().initial(request, *args, **kwargs) + data = request.data + client_id = data.get('client_id', None) + client_secret = data.get('client_secret', None) + if not client_secret or not client_secret: + raise ParseError(em.EMPTY_FIELD('Client ID or Client secret')) + try: + app = Application.objects.get(client_id=client_id, client_secret=client_secret) + except: + raise ParseError(em.INVALID_FIELD_PARAMETER('Client ID or Client secret')) + request.app = app \ No newline at end of file diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/user/__init__.py b/backend/user/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/user/admin.py b/backend/user/admin.py new file mode 100644 index 0000000..0307712 --- /dev/null +++ b/backend/user/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Team_Member + +admin.site.register(Team_Member) +# Register your models here. diff --git a/backend/user/apps.py b/backend/user/apps.py new file mode 100644 index 0000000..578292c --- /dev/null +++ b/backend/user/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UserConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "user" diff --git a/backend/user/managers.py b/backend/user/managers.py new file mode 100644 index 0000000..c2098ef --- /dev/null +++ b/backend/user/managers.py @@ -0,0 +1,34 @@ +from django.contrib.auth.base_user import BaseUserManager +from django.utils.translation import gettext_lazy as _ + + +class UserManager(BaseUserManager): + """ + Custom user model manager where email is the unique identifiers + for authentication instead of usernames. + """ + def create_user(self, email, password, **extra_fields): + """ + Create and save a user with the given email and password. + """ + if not email: + raise ValueError(_("The Email must be set")) + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save() + return user + + def create_superuser(self, email, password, **extra_fields): + """ + Create and save a SuperUser with the given email and password. + """ + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + extra_fields.setdefault("is_active", True) + + if extra_fields.get("is_staff") is not True: + raise ValueError(_("Superuser must have is_staff=True.")) + if extra_fields.get("is_superuser") is not True: + raise ValueError(_("Superuser must have is_superuser=True.")) + return self.create_user(email, password, **extra_fields) \ No newline at end of file diff --git a/backend/user/migrations/0001_initial.py b/backend/user/migrations/0001_initial.py new file mode 100644 index 0000000..9e846c5 --- /dev/null +++ b/backend/user/migrations/0001_initial.py @@ -0,0 +1,185 @@ +# Generated by Django 4.2.4 on 2023-08-08 11:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ] + + operations = [ + migrations.CreateModel( + name="User", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ( + "email", + models.EmailField( + max_length=254, unique=True, verbose_name="email address" + ), + ), + ("is_staff", models.BooleanField(default=False)), + ("blocked", models.BooleanField(default=False)), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.permission", + verbose_name="user permissions", + ), + ), + ], + options={ + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, + }, + ), + migrations.CreateModel( + name="Team_Member", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("json_field", models.JSONField(blank=True, default=dict, null=True)), + ("first_name", models.CharField(max_length=50)), + ("last_name", models.CharField(max_length=50)), + ("phone_number", models.CharField(max_length=20)), + ( + "profile_pic", + models.ImageField(blank=True, upload_to="profile_pics/"), + ), + ("github", models.URLField(blank=True, null=True)), + ("linkdin", models.URLField(blank=True, null=True)), + ("additional_url", models.URLField(blank=True, null=True)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="team", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Team Member", + "verbose_name_plural": "Team Members", + }, + ), + migrations.CreateModel( + name="Alum", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("json_field", models.JSONField(blank=True, default=dict, null=True)), + ("first_name", models.CharField(max_length=50)), + ("last_name", models.CharField(max_length=50)), + ("phone_number", models.CharField(max_length=20)), + ( + "profile_pic", + models.ImageField(blank=True, upload_to="profile_pics/"), + ), + ("linkdin", models.URLField(blank=True, null=True)), + ("facebook_url", models.URLField(blank=True, null=True)), + ("additional_url", models.URLField(blank=True, null=True)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="alum", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Alum", + "verbose_name_plural": "Alums", + }, + ), + ] diff --git a/backend/user/migrations/__init__.py b/backend/user/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/user/models.py b/backend/user/models.py new file mode 100644 index 0000000..f2ff565 --- /dev/null +++ b/backend/user/models.py @@ -0,0 +1,61 @@ +from django.contrib.auth.models import AbstractUser +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from utils.mixins import JsonModel, TimestampedModel +from utils.path import get_profile_pics_path + +from .managers import UserManager + + +class User(AbstractUser): + username = None + email = models.EmailField(_("email address"), unique=True) + is_staff = models.BooleanField(default=False) + blocked = models.BooleanField(default=False) + USERNAME_FIELD = "email" + REQUIRED_FIELDS = [] + objects = UserManager() + + def __str__(self): + return self.email + + +class Team_Member(JsonModel, TimestampedModel): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="team") + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + phone_number = models.CharField(max_length=20) + profile_pic = models.ImageField(upload_to=get_profile_pics_path(), blank=True) + github = models.URLField(null=True,blank=True) + linkdin = models.URLField(null=True,blank=True) + additional_url = models.URLField(null=True,blank=True) + + class Meta: + verbose_name = "Team Member" + verbose_name_plural = "Team Members" + + + + def __str__(self): + return f"{self.first_name} {self.last_name} {self.user.email}" + + + + + +class Alum(JsonModel, TimestampedModel): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="alum") + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + phone_number = models.CharField(max_length=20) + profile_pic = models.ImageField(upload_to=get_profile_pics_path(), blank=True) + linkdin = models.URLField(null=True,blank=True) + facebook_url = models.URLField(null=True,blank=True) + additional_url = models.URLField(null=True,blank=True) + def __str__(self): + return f"{self.first_name} {self.last_name} {self.user.email}" + class Meta: + verbose_name = "Alum" + verbose_name_plural = "Alums" + \ No newline at end of file diff --git a/backend/user/serilizers.py b/backend/user/serilizers.py new file mode 100644 index 0000000..9d11344 --- /dev/null +++ b/backend/user/serilizers.py @@ -0,0 +1,15 @@ +from rest_framework import serializers + +from .models import Alum, Team_Member + +class TeamMemberSerializer(serializers.ModelSerializer): + class Meta: + model = Team_Member + fields = "__all__" + +class AlumSerializer(serializers.ModelSerializer): + class Meta: + model = Alum + fields = "__all__" + + diff --git a/backend/user/tests.py b/backend/user/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/user/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/user/urls.py b/backend/user/urls.py new file mode 100644 index 0000000..63e18e7 --- /dev/null +++ b/backend/user/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from .views import TeamMemberView + +urlpatterns = [ + path('team/', TeamMemberView.as_view()), +] \ No newline at end of file diff --git a/backend/user/views.py b/backend/user/views.py new file mode 100644 index 0000000..ce56b00 --- /dev/null +++ b/backend/user/views.py @@ -0,0 +1,20 @@ +from django.shortcuts import render + +from Aries.views import ApplicationBaseView +from rest_framework.exceptions import ParseError +from rest_framework.generics import ListCreateAPIView +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT +from rest_framework.views import APIView +from .models import Team_Member +from .serilizers import TeamMemberSerializer + +class TeamMemberView(ApplicationBaseView,ListCreateAPIView): + allowed_methods = ('GET',) + + queryset = Team_Member.objects.all() + serializer_class = TeamMemberSerializer + + + \ No newline at end of file diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/utils/errors.py b/backend/utils/errors.py new file mode 100644 index 0000000..130565a --- /dev/null +++ b/backend/utils/errors.py @@ -0,0 +1,19 @@ +def INVALID_FIELD_PARAMETER(field): + return {'detail': f'{field} is invalid'} + + +def EMPTY_FIELD(field): + return {'detail': f'{field} cannot be empty'} + + +UNEXPECTED_ERROR = {'detail': 'Something went wrong!'} + +WRONG_CREDENTIALS = {'detail': 'Wrong credentials!'} + + +def ALREADY_EXISTS(field): + return {'detail': f'{field} already exists'} + + +def NOT_FOUND(field): + return {'detail': f'{field} not found!'} \ No newline at end of file diff --git a/backend/utils/mixins.py b/backend/utils/mixins.py new file mode 100644 index 0000000..4362af4 --- /dev/null +++ b/backend/utils/mixins.py @@ -0,0 +1,40 @@ +from django.conf import settings +from django.db import models +from django.urls import reverse + + +class TimestampedModel(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + class Meta: + abstract = True + ordering = ["-created_at", "-updated_at"] + + def get_admin_url(self, absolute=False): + url = reverse( + "admin:%s_%s_change" % (self._meta.app_label, self._meta.model_name), + args=[self.id], + ) + if absolute: + url = settings.ADMIN_URL + url + return url + + +class JsonModel(models.Model): + json_field = models.JSONField(blank=True, null=True, default=dict) + + class Meta: + abstract = True + + def __str__(self): + return str(self.json_field) + + def get_admin_url(self, absolute=False): + url = reverse( + "admin:%s_%s_change" % (self._meta.app_label, self._meta.model_name), + args=[self.id], + ) + if absolute: + url = settings.ADMIN_URL + url + return url \ No newline at end of file diff --git a/backend/utils/path.py b/backend/utils/path.py new file mode 100644 index 0000000..6dac407 --- /dev/null +++ b/backend/utils/path.py @@ -0,0 +1,3 @@ + +def get_profile_pics_path(): + return f"profile_pics/" \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index fa7affb..6f53710 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@types/react": "18.2.18", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.14", + "axios": "^1.4.0", "eslint": "8.46.0", "eslint-config-next": "13.4.12", "husky": "^8.0.3", diff --git a/frontend/utils/network.js b/frontend/utils/network.js new file mode 100644 index 0000000..9b893e6 --- /dev/null +++ b/frontend/utils/network.js @@ -0,0 +1,113 @@ +import axios from "axios"; + + +export function addQueryParams(urlString, queryParams) { + const query = Object.keys(queryParams) + .map((k) => { + if (Array.isArray(queryParams[k])) { + return queryParams[k].map((val) => `${k}[]=${val}`).join("&"); + } + return `${k}=${queryParams[k]}`; + }) + .join("&"); + return `${urlString}?${query}`; +} + +const addClientIdToBody = body => { + + return { + ...body, + client_id: process.env.REACT_APP_CLIENT_ID, + client_secret: process.env.REACT_APP_CLIENT_SECRET, + }; + } + + +export function request( + method, + url, + data, + authorized = true, + contentType = "application/json" +) { + + return new Promise(async (resolve, reject) => { + let headers = { "content-type": contentType }; + + if (authorized) { + const token = await getKey(TOKEN_TYPE); + const body = addClientIdToBody(data); + if (token) { + headers.Authorization = `Bearer ${token}`; + axios({ + method, + url, + body, + headers, + responseType: "json", + }) + .then((res) => { + resolve(res); + }) + .catch((err) => { + + reject(err); + }); + } else { + reject("Unauthorized"); + } + } else { + axios({ + method, + url, + body, + headers, + responseType: "json", + }) + .then((res) => { + resolve(res); + }) + .catch((err) => { + + reject(err); + }); + } + }); +} + +export const getErrorBody = (error) => { + let response = {}; + try { + response = error.response; + } catch (err) { + response = {}; + } + response = response || {}; + const outputErrorBody = { + ...response.data, + status: response.status ? response.status : 408, + }; + return outputErrorBody; +}; + +const get_base_api = () => { + let env = process.env.NODE_ENV; + if (env == 'production') return process.env.PRODUCTION_SERVER; + else if (env == 'staging') + return process.env.STAGING_SERVER; + + return process.env.DEVELOPMENT_SERVER; +}; + +export const BASE_API = get_base_api(); +export const getUrl = (relUrl) => `${BASE_API}${relUrl}`; + +function getBaseImgUrl() { + let env = process.env.NODE_ENV; + if (env === "production" || env === "staging" || env === "dev") return ""; + else { + return process.env.DEVELOPMENT_SERVER; + } +} + +export const BASE_IMG_URL = getBaseImgUrl(); \ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 5f4345c..35d9f69 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -434,6 +434,11 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@10.4.14: version "10.4.14" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" @@ -456,6 +461,15 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -587,6 +601,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -671,6 +692,11 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -1109,6 +1135,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -1116,6 +1147,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -1675,6 +1715,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2049,6 +2101,11 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"