diff --git a/ecommerce-backend-master/ecommerce-backend-master/Dockerfile b/ecommerce-backend-master/ecommerce-backend-master/Dockerfile new file mode 100644 index 0000000..6d7c685 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3 +WORKDIR /usr/src/app +ADD requirements.txt /usr/src/app +RUN pip install -r requirements.txt +ADD . /usr/src/app \ No newline at end of file diff --git a/ecommerce-backend-master/ecommerce-backend-master/Procfile b/ecommerce-backend-master/ecommerce-backend-master/Procfile new file mode 100644 index 0000000..b477142 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/Procfile @@ -0,0 +1,2 @@ +release: python manage.py migrate --settings=src.settings.production +web: gunicorn src.wsgi --log-file - \ No newline at end of file diff --git a/ecommerce-backend-master/ecommerce-backend-master/README.md b/ecommerce-backend-master/ecommerce-backend-master/README.md new file mode 100644 index 0000000..41875e5 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/README.md @@ -0,0 +1,77 @@ +# ecommerce-backend +Django backend for eCommerce project + +This project is built using Django REST Framework to provide the backend API for eCommerce project. The deployed API is available on [Heroku](https://ecommerce-backend-django.herokuapp.com/). The frontend is available [here](https://github.com/ncunx/ecommerce-frontend). + +Features +-------- +1. Products API endpoint available at `/api/products/`. +2. Custom user authentication using JSON Web Tokens. The API is available at `/api/accounts/`. +2. Simple newsletter functionality: superuser can view the list of all subscribers in Django admin panel; any visitor can subscribe. The relevant API endpoint is available at `/api/newsletter/`. +3. [Stripe](https://stripe.com/) payments API endpoint available at `/api/payments/`. + +Main requirements +------------ + +1. `python` 3.5, 3.6, 3.7 +2. `Django` 2.1.8 +3. `PostgreSQL` 11.1 + +This project also uses other packages (see `requirements.txt` file for details). +For instance, tag support is provided by [django-taggit](https://github.com/alex/django-taggit) and image processing is thanks to [Pillow](https://github.com/python-pillow/Pillow). + +## How to set up + +### Setup using Docker + +The easiest way to get backend up and running is via [Docker](https://www.docker.com/). See [docs](https://docs.docker.com/get-started/) to get started. Once set up run the following command: + +`docker-compose up` + +This command takes care of populating products list with sample data. + +It may take a while for the process to complete, as Docker needs to pull required dependencies. Once it is done, the application should be accessible at `0.0.0.0:8000`. + +### Manual setup + +Firstly, create a new directory and change to it: + +`mkdir ecommerce-backend && cd ecommerce-backend` + +Then, clone this repository to the current directory: + +`git clone https://github.com/ncunx/ecommerce-backend.git .` + + +For the backend to work, one needs to setup database like SQLite or PostgreSQL on a local machine. This project uses PostgreSQL by default (see [Django documentation](https://docs.djangoproject.com/en/2.1/ref/settings/#databases) for different setup). This process may vary from one OS to another, eg. on Arch Linux one can follow a straightforward guide [here](https://wiki.archlinux.org/index.php/PostgreSQL). + +The database settings are specified in `src/settings/local.py`. In particular the default database name is `eCommerceDjango`, which can be created from the PostgreSQL shell by running `createdb eCommerceDjango`. + +Next, set up a virtual environment and activate it: + +`python3 -m venv env && source env/bin/activate` + +Install required packages: + +`pip3 install -r requirements.txt` + +Next, perform migration: + +`python3 manage.py migrate --settings=src.settings.local` + +At this point, one may want to create a superuser account and create some products. One can also use sample data provided in `products/fixtures.json` by running: + +`python3 manage.py loaddata products/fixture.json --settings=src.settings.local` + +The backend is now ready. Run a local server with + +`python3 manage.py runserver --settings=src.settings.local` + +The backend should be available at `localhost:8000`. + +In order to use [Stripe payments](https://stripe.com/) one needs to create an account and obtain a pair of keys (available in the dashboard after signing in). These keys should replace `STRIPE_SECRET_KEY` and `STRIPE_PUBLISHABLE_KEY` values in `src/settings/local.py`. + +## To do + +1. Implement an API endpoint at `/api/orders/` to accept POST request with order JSON from authenticated user, and GET requests from superuser (for further processing). +2. Implement password recovery. diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/admin.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/admin.py new file mode 100644 index 0000000..4ca062d --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/admin.py @@ -0,0 +1,32 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin + +from .forms import CustomUserCreationForm, CustomUserChangeForm +from .models import CustomUser + + +class CustomUserAdmin(UserAdmin): + add_form = CustomUserCreationForm + form = CustomUserChangeForm + model = CustomUser + list_display = ("first_name", "last_name", "email", "is_staff", "is_active") + list_filter = ("first_name", "last_name", "email", "is_staff", "is_active") + fieldsets = ( + (None, {"fields": ("email", "password")}), + ("Personal information", {"fields": ("first_name", "last_name")}), + ("Permissions", {"fields": ("is_staff", "is_active")}), + ) + add_fieldsets = ( + ( + None, + { + "classes": ("wide",), + "fields": ("email", "password1", "password2", "is_staff", "is_active"), + }, + ), + ) + search_fields = ("first_name", "last_name", "email") + ordering = ("first_name", "last_name", "email") + + +admin.site.register(CustomUser, CustomUserAdmin) diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/api/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/api/permissions.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/permissions.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/api/serializers.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/serializers.py new file mode 100644 index 0000000..9ab7097 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/serializers.py @@ -0,0 +1,69 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers +from rest_auth.serializers import LoginSerializer + +try: + from allauth.utils import email_address_exists + from allauth.account.adapter import get_adapter + from allauth.account.utils import setup_user_email +except ImportError: + raise ImportError("allauth needs to be added to INSTALLED_APPS.") + +User = get_user_model() + + +class CustomUserDetailsSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ("id", "first_name", "last_name", "email") + + +class CustomLoginSerializer(LoginSerializer): + username = None + email = serializers.EmailField(required=False, allow_blank=True) + password = serializers.CharField(style={"input_type": "password"}) + + +class CustomRegisterSerializer(serializers.Serializer): + """ + Modified RegisterSerializer class from rest_auth + """ + + username = None + first_name = serializers.CharField(required=True) + last_name = serializers.CharField(required=True) + email = serializers.EmailField(required=True) + password1 = serializers.CharField(write_only=True, style={"input_type": "password"}) + password2 = serializers.CharField(write_only=True, style={"input_type": "password"}) + + def validate_email(self, email): + email = get_adapter().clean_email(email) + if email and email_address_exists(email): + raise serializers.ValidationError( + "A user is already registered with this e-mail address." + ) + return email + + def validate_password1(self, password): + return get_adapter().clean_password(password) + + def validate(self, data): + if data["password1"] != data["password2"]: + raise serializers.ValidationError("The two password fields didn't match.") + return data + + def get_cleaned_data(self): + return { + "first_name": self.validated_data.get("first_name", ""), + "last_name": self.validated_data.get("last_name", ""), + "password1": self.validated_data.get("password1", ""), + "email": self.validated_data.get("email", ""), + } + + def save(self, request): + adapter = get_adapter() + user = adapter.new_user(request) + self.cleaned_data = self.get_cleaned_data() + adapter.save_user(request, user, self) + setup_user_email(request, user, []) + return user diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/api/urls.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/urls.py new file mode 100644 index 0000000..e791809 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/urls.py @@ -0,0 +1,8 @@ +from django.urls import include, path +from .views import CustomRegisterView + + +urlpatterns = [ + path("", include("rest_auth.urls")), + path("register/", include("rest_auth.registration.urls")), +] diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/api/views.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/views.py new file mode 100644 index 0000000..1a555a1 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/api/views.py @@ -0,0 +1,9 @@ +from rest_auth.views import LoginView +from rest_auth.registration.views import RegisterView +from .serializers import CustomRegisterSerializer + +class CustomLoginView(LoginView): + pass + +class CustomRegisterView(RegisterView): + serializer_class = CustomRegisterSerializer diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/apps.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/apps.py new file mode 100644 index 0000000..9b3fc5a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AccountsConfig(AppConfig): + name = 'accounts' diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/forms.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/forms.py new file mode 100644 index 0000000..1420fa6 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/forms.py @@ -0,0 +1,14 @@ +from django.contrib.auth.forms import UserCreationForm, UserChangeForm +from .models import CustomUser + + +class CustomUserCreationForm(UserCreationForm): + class Meta(UserCreationForm): + model = CustomUser + fields = ("first_name", "last_name", "email") + + +class CustomUserChangeForm(UserChangeForm): + class Meta: + model = CustomUser + fields = ("first_name", "last_name", "email") diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/migrations/0001_initial.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..03c505f --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# Generated by Django 2.1.7 on 2019-06-13 18:16 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0009_alter_user_last_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('first_name', models.CharField(max_length=255, verbose_name='First name')), + ('last_name', models.CharField(max_length=255, verbose_name='Last name')), + ('email', models.EmailField(max_length=254, unique=True)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + ), + ] diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/migrations/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/models.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/models.py new file mode 100644 index 0000000..a70c486 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/models.py @@ -0,0 +1,55 @@ +from django.contrib.auth.base_user import BaseUserManager +from django.contrib.auth.models import AbstractUser +from django.db import models + + +class CustomUserManager(BaseUserManager): + """ + Custom user model manager with email as the unique identifier + """ + + def create_user(self, first_name, last_name, email, password, **extra_fields): + """ + Create user with the given email and password. + """ + if not email: + raise ValueError("The email must be set") + first_name = first_name.capitalize() + last_name = last_name.capitalize() + email = self.normalize_email(email) + + user = self.model( + first_name=first_name, last_name=last_name, email=email, **extra_fields + ) + user.set_password(password) + user.save() + return user + + def create_superuser(self, first_name, last_name, email, password, **extra_fields): + """ + Create 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(first_name, last_name, email, password, **extra_fields) + + +class CustomUser(AbstractUser): + username = None + first_name = models.CharField(max_length=255, verbose_name="First name") + last_name = models.CharField(max_length=255, verbose_name="Last name") + email = models.EmailField(unique=True) + + USERNAME_FIELD = "email" + REQUIRED_FIELDS = ["first_name", "last_name"] + + objects = CustomUserManager() + + def __str__(self): + return self.email diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/tests.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/accounts/views.py b/ecommerce-backend-master/ecommerce-backend-master/accounts/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/accounts/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/api/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/api/urls.py b/ecommerce-backend-master/ecommerce-backend-master/api/urls.py new file mode 100644 index 0000000..23634c3 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/api/urls.py @@ -0,0 +1,8 @@ +from django.urls import path, include + +urlpatterns = [ + path("accounts/", include("accounts.api.urls")), + path("products/", include("products.api.urls")), + path("payments/", include("payments.urls")), + path("subscribers/", include("newsletter.api.urls")), +] diff --git a/ecommerce-backend-master/ecommerce-backend-master/docker-compose.yml b/ecommerce-backend-master/ecommerce-backend-master/docker-compose.yml new file mode 100644 index 0000000..c94d3e7 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' + +services: + db: + image: postgres + migration: + build: . + command: python3 manage.py migrate --settings=src.settings.local-docker + volumes: + - .:/usr/src/app + depends_on: + - db + load_fixtures: + build: . + command: python3 manage.py loaddata products/fixture.json --settings=src.settings.local-docker + volumes: + - .:/usr/src/app + depends_on: + - migration + web: + build: . + command: python3 manage.py runserver 0.0.0.0:8000 --settings=src.settings.local-docker + volumes: + - .:/usr/src/app + ports: + - "8000:8000" + depends_on: + - db + - migration + - load_fixtures diff --git a/ecommerce-backend-master/ecommerce-backend-master/manage.py b/ecommerce-backend-master/ecommerce-backend-master/manage.py new file mode 100644 index 0000000..720ea60 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/manage.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == '__main__': + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'src.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Apricot_globemallow.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Apricot_globemallow.jpg new file mode 100644 index 0000000..8bfc7d9 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Apricot_globemallow.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Blutaparon.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Blutaparon.jpg new file mode 100644 index 0000000..93d273b Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Blutaparon.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Chamisso_Sedge.jpeg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Chamisso_Sedge.jpeg new file mode 100644 index 0000000..cd29f28 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Chamisso_Sedge.jpeg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Cone_Peak_Bedstraw.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Cone_Peak_Bedstraw.jpg new file mode 100644 index 0000000..6a8198f Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Cone_Peak_Bedstraw.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Isachne.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Isachne.jpg new file mode 100644 index 0000000..c7aaedf Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Isachne.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Kauai_Hau_Kuahiwi.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Kauai_Hau_Kuahiwi.jpg new file mode 100644 index 0000000..b5cb147 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Kauai_Hau_Kuahiwi.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Largeflower_False_Rosemary.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Largeflower_False_Rosemary.jpg new file mode 100644 index 0000000..14200c7 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Largeflower_False_Rosemary.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Marsh_Straw_Sedge.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Marsh_Straw_Sedge.jpg new file mode 100644 index 0000000..ad84c97 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Marsh_Straw_Sedge.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Oregon_Bleeding_Heart.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Oregon_Bleeding_Heart.jpg new file mode 100644 index 0000000..286f259 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Oregon_Bleeding_Heart.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pore_Lichen.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pore_Lichen.jpg new file mode 100644 index 0000000..6ba4d67 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pore_Lichen.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pygmy_Goldenweed.jpeg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pygmy_Goldenweed.jpeg new file mode 100644 index 0000000..91451f4 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Pygmy_Goldenweed.jpeg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Red-vein_Yellow_Loosestrife.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Red-vein_Yellow_Loosestrife.jpg new file mode 100644 index 0000000..bcc8721 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Red-vein_Yellow_Loosestrife.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Santo_Domingo_False_Willow.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Santo_Domingo_False_Willow.jpg new file mode 100644 index 0000000..d706b0d Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Santo_Domingo_False_Willow.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Toringo_Crab.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Toringo_Crab.jpg new file mode 100644 index 0000000..15e5101 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/Toringo_Crab.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/chinese_haw.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/chinese_haw.jpg new file mode 100644 index 0000000..8fc1254 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/chinese_haw.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe.jpeg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe.jpeg new file mode 100644 index 0000000..4a6aa37 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe.jpeg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe2.jpg b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe2.jpg new file mode 100644 index 0000000..e2a4665 Binary files /dev/null and b/ecommerce-backend-master/ecommerce-backend-master/media/products/images/shoe2.jpg differ diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/admin.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/admin.py new file mode 100644 index 0000000..677a0a9 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/admin.py @@ -0,0 +1,8 @@ +from django.contrib import admin +from .models import Subscriber + +# Register your models here. +class SubscriberAdmin(admin.ModelAdmin): + list_display = ('email', 'joined_date', ) + +admin.site.register(Subscriber, SubscriberAdmin) diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/permissions.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/permissions.py new file mode 100644 index 0000000..16e8267 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/permissions.py @@ -0,0 +1,11 @@ +from rest_framework import permissions + + +class IsPostOrIsAdmin(permissions.BasePermission): + def has_permission(self, request, view): + # allow all POST requests + if request.method == "POST": + return True + + # Otherwise, only allow authenticated requests + return request.user and request.user.is_staff diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/serializers.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/serializers.py new file mode 100644 index 0000000..448dbf2 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from newsletter.models import Subscriber + + +class SubscriberSerializer(serializers.ModelSerializer): + joined_date = serializers.DateTimeField( + format="%a, %d %b %I:%M %p", read_only=True + ) + + class Meta: + model = Subscriber + fields = "__all__" diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/urls.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/urls.py new file mode 100644 index 0000000..13e02fe --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/urls.py @@ -0,0 +1,5 @@ +from django.urls import path, include + +from newsletter.api.views import Subscribers + +urlpatterns = [path("", Subscribers.as_view(), name="list_subscribers")] diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/views.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/views.py new file mode 100644 index 0000000..6f909ed --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/api/views.py @@ -0,0 +1,20 @@ +from rest_framework import generics, status +from rest_framework.response import Response + +from newsletter.models import Subscriber +from .serializers import SubscriberSerializer +from .permissions import IsPostOrIsAdmin + + +class Subscribers(generics.ListCreateAPIView): + """ + API view for listing all existing subscribers. + + Only safe requests allowed. + + Visible only to users for which user.is_staff returns True. + """ + + queryset = Subscriber.objects.all() + serializer_class = SubscriberSerializer + permission_classes = (IsPostOrIsAdmin,) # GET for admin, POST for everyone diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/apps.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/apps.py new file mode 100644 index 0000000..b532e88 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class NewsletterConfig(AppConfig): + name = 'newsletter' diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/migrations/0001_initial.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/migrations/0001_initial.py new file mode 100644 index 0000000..852b117 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 2.1.5 on 2019-02-22 11:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Subscriber', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('email', models.EmailField(max_length=254, unique=True)), + ('joined_date', models.DateTimeField(auto_now_add=True)), + ], + options={ + 'verbose_name': 'Subscriber', + 'verbose_name_plural': 'Subscribers', + }, + ), + ] diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/migrations/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/models.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/models.py new file mode 100644 index 0000000..1cbc6fd --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/models.py @@ -0,0 +1,13 @@ +from django.db import models + +# Create your models here. +class Subscriber(models.Model): + email = models.EmailField(unique=True) + joined_date = models.DateTimeField(auto_now_add=True) + + class Meta: + verbose_name = "Subscriber" + verbose_name_plural = "Subscribers" + + def __str__(self): + return self.email diff --git a/ecommerce-backend-master/ecommerce-backend-master/newsletter/views.py b/ecommerce-backend-master/ecommerce-backend-master/newsletter/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/newsletter/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/orders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/admin.py b/ecommerce-backend-master/ecommerce-backend-master/orders/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/api/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/orders/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/api/permissions.py b/ecommerce-backend-master/ecommerce-backend-master/orders/api/permissions.py new file mode 100644 index 0000000..ab453da --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/api/permissions.py @@ -0,0 +1,11 @@ +from rest_framework import permissions + +class IsPostOrIsAdmin(permissions.BasePermission): + + def has_permission(self, request, view): + # allow all POST requests + if request.method == 'POST': + return True + + # Otherwise, only allow authenticated requests + return request.user and request.user.is_staff diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/api/serializers.py b/ecommerce-backend-master/ecommerce-backend-master/orders/api/serializers.py new file mode 100644 index 0000000..ab8b8c7 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/api/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers +from orders.models import Order + +class OrderSerializer(serializers.ModelSerializer): + order_date = serializers.DateTimeField(format='%a, %d %b %I:%M %p', + read_only=True) + class Meta: + model = Order + fields = '__all__' \ No newline at end of file diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/api/urls.py b/ecommerce-backend-master/ecommerce-backend-master/orders/api/urls.py new file mode 100644 index 0000000..744b5c4 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/api/urls.py @@ -0,0 +1,14 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter + + +from orders.api.views import OrdersViewSet + +# Create a router and register viewsets with it. +router = DefaultRouter() +router.register('', OrdersViewSet, 'orders') + +# The API URLs are now determined automatically by the router. +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/api/views.py b/ecommerce-backend-master/ecommerce-backend-master/orders/api/views.py new file mode 100644 index 0000000..6629eb3 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/api/views.py @@ -0,0 +1,18 @@ +from rest_framework import generics, status +from rest_framework import permissions +# from rest_framework.response import Response + +from orders.models import Order +from .serializers import OrderSerializer +from .permissions import IsPostOrIsAdmin + +class Orders(generics.ListCreateAPIView): + """ + API view for listing (admin) or creating (authenticated user) orders. + """ + + queryset = Order.objects.all() + serializer_class = OrderSerializer + + # GET for admin, POST for authenticated + permission_classes = (permissions.IsAuthenticated, IsPostOrIsAdmin, ) diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/apps.py b/ecommerce-backend-master/ecommerce-backend-master/orders/apps.py new file mode 100644 index 0000000..384ab43 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class OrdersConfig(AppConfig): + name = 'orders' diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/migrations/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/orders/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/models.py b/ecommerce-backend-master/ecommerce-backend-master/orders/models.py new file mode 100644 index 0000000..129e7ff --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/models.py @@ -0,0 +1,80 @@ +from django.db import models +from django.contrib.auth import get_user_model +from products.models import Product + +User = get_user_model() + +# Create your models here. + +class Cart(models.Model): + + owner = models.OneToOneField(User, + related_name="cart", + on_delete=models.CASCADE, + null=True, + blank=True) + number_of_items = models.PositiveIntegerField(default=0) + + total = models.DecimalField(defaul=0.00, + max_digits=5, + decimal_places=2) + + created_at = models.DateTimeField(auto_now_add=True) + + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"User: {self.owner}, items in cart {self.number_of_items}" + + +class CartItem(models.Model): + cart = models.ForeignKey(Cart, + on_delete=models.CASCADE) + item = models.ForeignKey(Product, + on_delete=models.CASCADE) + quantity = models.IntegerField() + + +ORDER_STATUS = ( + ('placed', 'Placed'), + ('paid', 'Paid'), + ('shipped', 'Shipped'), + ('delivered', 'Delivered') +) + +class Order(models.Model): + status = models.CharField(max_length=120, + choices=ORDER_STATUS, + default='created') + + # cart = models.ForeignKey(Cart, + # on_delete=models.PROTECT) + + user = models.ForeignKey(User, + on_delete=models.PROTECT) + + billing_address = models.CharField() + # on_delete=models.PROTECT, + # related_name='billing_address', + # null=True) + shipping_address = models.CharField() + # shipping_address = models.ForeignKey(DeliveryAddress, + # on_delete=models.PROTECT, + # related_name='shipping_address', + # null=True) + + shipping = models.DecimalField(max_digits=50, + decimal_places=2, + default=5.00) + + order_total = models.DecimalField(max_digits=50, + decimal_places=2) + + # order_id = models.CharField(max_length=20, + # null=True, + # blank=True) + order_date = models.DateTimeField(auto_now_add=True) + + @property + def is_completed(self): + return True if self.status == "paid" else False diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/tests.py b/ecommerce-backend-master/ecommerce-backend-master/orders/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/orders/views.py b/ecommerce-backend-master/ecommerce-backend-master/orders/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/orders/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/payments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/admin.py b/ecommerce-backend-master/ecommerce-backend-master/payments/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/apps.py b/ecommerce-backend-master/ecommerce-backend-master/payments/apps.py new file mode 100644 index 0000000..58d36ac --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PaymentsConfig(AppConfig): + name = 'payments' diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/migrations/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/payments/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/models.py b/ecommerce-backend-master/ecommerce-backend-master/payments/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/tests.py b/ecommerce-backend-master/ecommerce-backend-master/payments/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/urls.py b/ecommerce-backend-master/ecommerce-backend-master/payments/urls.py new file mode 100644 index 0000000..f9d2640 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/urls.py @@ -0,0 +1,6 @@ +from django.urls import path + +from .views import charge_view + +urlpatterns = [path("", charge_view, name="charge_view")] + diff --git a/ecommerce-backend-master/ecommerce-backend-master/payments/views.py b/ecommerce-backend-master/ecommerce-backend-master/payments/views.py new file mode 100644 index 0000000..3b0e2e4 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/payments/views.py @@ -0,0 +1,68 @@ +from django.conf import settings +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_http_methods + +import json +import stripe + +stripe.api_key = settings.STRIPE_SECRET_KEY + +# Create your views here. +@require_http_methods(["POST"]) +@csrf_exempt +def charge_view(request): + try: + charge = stripe.Charge.create( + amount=request.POST.get("amount", ""), + currency=request.POST.get("currency", ""), + source=request.POST.get("source", ""), + ) + if charge["status"] == "succeeded": + return HttpResponse( + json.dumps({"message": "Your transaction has been successful."}) + ) + else: + raise stripe.error.CardError + + except stripe.error.CardError as e: + # Since it's a decline, stripe.error.CardError will be caught + body = e.json_body + err = body.get("error", {}) + print("Status is: %s" % e.http_status) + print("Type is: %s" % err.get("type")) + print("Code is: %s" % err.get("code")) + print("Message is %s" % err.get("message")) + return HttpResponse( + json.dumps({"message": err.get("message")}), status=e.http_status + ) + + except stripe.error.RateLimitError as e: + # Too many requests made to the API too quickly + return HttpResponse(json.dumps({"message": "Too many requests to the API."})) + + except stripe.error.InvalidRequestError as e: + # invalid parameters were supplied to Stripe"s API + return HttpResponse(json.dumps({"message": "Invalid parameters."})) + + except stripe.error.AuthenticationError as e: + # Authentication with Stripe"s API failed + # (maybe you changed API keys recently) + return HttpResponse(json.dumps({"message": "Authentication failed."})) + + except stripe.error.APIConnectionError as e: + # Network communication with Stripe failed + return HttpResponse( + json.dumps({"message": "Network communication failed, try again."}) + ) + + except stripe.error.StripeError as e: + # Display a very generic error to the user, and maybe + # send yourself an email + return HttpResponse(json.dumps({"message": "Provider error!"})) + + # Something else happened, completely unrelated to Stripe + except Exception as e: + return HttpResponse( + json.dumps({"message": "Unable to process payment, try again."}) + ) diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/products/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/admin.py b/ecommerce-backend-master/ecommerce-backend-master/products/admin.py new file mode 100644 index 0000000..751b51f --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/admin.py @@ -0,0 +1,20 @@ +from django.contrib import admin +from .models import Product, Category + + +class ProductAdmin(admin.ModelAdmin): + list_display = ('name', 'price', 'quantity', 'featured', ) + list_filter = ('name', 'price', 'quantity', 'featured', ) + list_editable = ('price', 'quantity', ) + + # sets up slug to be generated from product name + prepopulated_fields = {'slug': ('name', )} + + +class CategoryAdmin(admin.ModelAdmin): + list_display = ('name', ) + list_display_links = ('name', ) + + +admin.site.register(Product, ProductAdmin) +admin.site.register(Category, CategoryAdmin) \ No newline at end of file diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/api/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/products/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/api/serializers.py b/ecommerce-backend-master/ecommerce-backend-master/products/api/serializers.py new file mode 100644 index 0000000..e01940d --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/api/serializers.py @@ -0,0 +1,27 @@ +from rest_framework import serializers + +from products.models import Product, Category + + +class CategorySerializer(serializers.ModelSerializer): + id = serializers.IntegerField() + + class Meta: + model = Category + fields = ("id", "name") + + +class ProductSerializer(serializers.ModelSerializer): + class Meta: + model = Product + fields = ( + "id", + "name", + "price", + "quantity", + "featured", + "description", + "picture", + "slug", + ) + lookup_field = "slug" diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/api/urls.py b/ecommerce-backend-master/ecommerce-backend-master/products/api/urls.py new file mode 100644 index 0000000..1a7a818 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/api/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter + + +from products.api.views import ProductViewSet + +router = DefaultRouter() +router.register("", ProductViewSet) + +urlpatterns = [path("", include(router.urls))] diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/api/views.py b/ecommerce-backend-master/ecommerce-backend-master/products/api/views.py new file mode 100644 index 0000000..706a653 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/api/views.py @@ -0,0 +1,20 @@ +from rest_framework import viewsets +from rest_framework.filters import SearchFilter, OrderingFilter + +from .serializers import ProductSerializer, CategorySerializer +from products.models import Product, Category + + +class CategoryViewSet(viewsets.ModelViewSet): + queryset = Category.objects.all() + serializer_class = CategorySerializer + + +class ProductViewSet(viewsets.ModelViewSet): + queryset = Product.objects.all() + # permission_classes = [permissions.IsAuthenticated, ] + serializer_class = ProductSerializer + lookup_field = "slug" + + filter_backends = [SearchFilter, OrderingFilter] + search_fields = ["category__name", "name", "description"] diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/apps.py b/ecommerce-backend-master/ecommerce-backend-master/products/apps.py new file mode 100644 index 0000000..864c43e --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ProductsConfig(AppConfig): + name = 'products' diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/fixture.json b/ecommerce-backend-master/ecommerce-backend-master/products/fixture.json new file mode 100644 index 0000000..c0cbb35 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/fixture.json @@ -0,0 +1,213 @@ +[ + { "model": "products.category", "pk": 1, "fields": { "name": "Plants" } }, + { + "model": "products.product", + "pk": 1, + "fields": { + "name": "Apricot Globemallow", + "slug": "apricot-globemallow", + "description": "Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat. Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.", + "picture": "products/images/Apricot_globemallow.jpg", + "price": "30.65", + "quantity": 38, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 2, + "fields": { + "name": "Cone Peak Bedstraw", + "slug": "cone-peak-bedstraw", + "description": "Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.", + "picture": "products/images/Cone_Peak_Bedstraw.jpg", + "price": "30.72", + "quantity": 97, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 3, + "fields": { + "name": "Blutaparon", + "slug": "blutaparon", + "description": "Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.", + "picture": "products/images/Blutaparon.jpg", + "price": "14.05", + "quantity": 10, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 4, + "fields": { + "name": "Chamisso Sedge", + "slug": "chamisso-sedge", + "description": "Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.", + "picture": "products/images/Chamisso_Sedge.jpeg", + "price": "8.80", + "quantity": 17, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 5, + "fields": { + "name": "Marsh Straw Sedge", + "slug": "marsh-straw-sedge", + "description": "Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.", + "picture": "products/images/Marsh_Straw_Sedge.jpg", + "price": "17.21", + "quantity": 91, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 6, + "fields": { + "name": "Largeflower False Rosemary", + "slug": "largeflower-false-rosemary", + "description": "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.", + "picture": "products/images/Largeflower_False_Rosemary.jpg", + "price": "8.46", + "quantity": 96, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 7, + "fields": { + "name": "Oregon Bleeding Heart", + "slug": "oregon-bleeding-heart", + "description": "Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.", + "picture": "products/images/Oregon_Bleeding_Heart.jpg", + "price": "39.31", + "quantity": 43, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 8, + "fields": { + "name": "Pore Lichen", + "slug": "pore-lichen", + "description": "Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.", + "picture": "products/images/Pore_Lichen.jpg", + "price": "6.80", + "quantity": 14, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 9, + "fields": { + "name": "Chinese Haw", + "slug": "chinese-haw", + "description": "Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo.", + "picture": "products/images/chinese_haw.jpg", + "price": "10.72", + "quantity": 67, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 10, + "fields": { + "name": "Toringo Crab", + "slug": "toringo-crab", + "description": "In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.", + "picture": "products/images/Toringo_Crab.jpg", + "price": "23.86", + "quantity": 59, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 11, + "fields": { + "name": "Kauai Hau Kuahiwi", + "slug": "kauai-hau-kuahiwi", + "description": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.", + "picture": "products/images/Kauai_Hau_Kuahiwi.jpg", + "price": "20.38", + "quantity": 87, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 12, + "fields": { + "name": "Pygmy Goldenweed", + "slug": "pygmy-goldenweed", + "description": "Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.", + "picture": "products/images/Pygmy_Goldenweed.jpeg", + "price": "32.75", + "quantity": 48, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 13, + "fields": { + "name": "Red-vein Yellow Loosestrife", + "slug": "red-vein-yellow-loosestrife", + "description": "Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.", + "picture": "products/images/Red-vein_Yellow_Loosestrife.jpg", + "price": "33.14", + "quantity": 12, + "featured": false, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 14, + "fields": { + "name": "Isachne", + "slug": "isachne", + "description": "Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl", + "picture": "products/images/Isachne.jpg", + "price": "30.33", + "quantity": 66, + "featured": true, + "category": [1] + } + }, + { + "model": "products.product", + "pk": 15, + "fields": { + "name": "Santo Domingo False Willow", + "slug": "santo-domingo-false-willow", + "description": "Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.", + "picture": "products/images/Santo_Domingo_False_Willow.jpg", + "price": "8.55", + "quantity": 10, + "featured": true, + "category": [1] + } + } +] diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/migrations/0001_initial.py b/ecommerce-backend-master/ecommerce-backend-master/products/migrations/0001_initial.py new file mode 100644 index 0000000..367a750 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 2.1.5 on 2019-02-22 11:03 + +from django.db import migrations, models +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, unique=True)), + ], + options={ + 'verbose_name_plural': 'Categories', + }, + ), + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=150)), + ('slug', models.SlugField(max_length=200)), + ('description', models.TextField(default='Empty description.', max_length=500)), + ('picture', models.ImageField(blank=True, null=True, upload_to='products/images')), + ('price', models.DecimalField(decimal_places=2, default=0, max_digits=20)), + ('quantity', models.IntegerField(default=10)), + ('featured', models.BooleanField(default=False)), + ('category', models.ManyToManyField(to='products.Category')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + options={ + 'ordering': ('name',), + }, + ), + ] diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/migrations/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/products/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/models.py b/ecommerce-backend-master/ecommerce-backend-master/products/models.py new file mode 100644 index 0000000..bf0c02a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/models.py @@ -0,0 +1,48 @@ +from django.db import models +from django.urls import reverse + +# tags +from taggit.managers import TaggableManager + +# Create your models here. +class Category(models.Model): + name = models.CharField(max_length=100, unique=True) + # parent = models.ForeignKey('self', + # on_delete=models.CASCADE, + # null=True, + # blank=True) + + class Meta: + verbose_name_plural = "Categories" + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("products:category", kwargs={"name": self.name}) + + +class Product(models.Model): + category = models.ManyToManyField(Category, blank=False) + tags = TaggableManager(blank=True) # tags mechanism + name = models.CharField(max_length=150) + slug = models.SlugField(max_length=200) + description = models.TextField(max_length=500, default="Empty description.") + picture = models.ImageField(upload_to="products/images", null=True, blank=True) + price = models.DecimalField(decimal_places=2, max_digits=20, default=0) + quantity = models.IntegerField(default=10) # available quantity of given product + featured = models.BooleanField(default=False) # is product featured? + + class Meta: + ordering = ("name",) + + def __str__(self): + return self.name + + @property + def is_featured(self): + return self.featured + + @property + def is_available(self): + return self.quantity > 0 diff --git a/ecommerce-backend-master/ecommerce-backend-master/products/views.py b/ecommerce-backend-master/ecommerce-backend-master/products/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/products/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/ecommerce-backend-master/ecommerce-backend-master/requirements.txt b/ecommerce-backend-master/ecommerce-backend-master/requirements.txt new file mode 100644 index 0000000..1fb020b --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/requirements.txt @@ -0,0 +1,53 @@ +appdirs==1.4.3 +asn1crypto==0.24.0 +astroid==2.2.5 +attrs==19.1.0 +black==19.3b0 +boto3==1.9.158 +botocore==1.12.158 +certifi==2018.11.29 +cffi==1.11.5 +chardet==3.0.4 +Click==7.0 +coverage==4.5.3 +cryptography==2.4.2 +defusedxml==0.5.0 +dj-database-url==0.5.0 +Django==2.1.10 +django-allauth==0.38.0 +django-cors-headers==2.4.0 +django-heroku==0.3.1 +django-rest-auth==0.9.3 +django-storages==1.7.1 +django-taggit==0.23.0 +django-webpack-loader==0.6.0 +djangorestframework==3.9.1 +djangorestframework-jwt==1.11.0 +docutils==0.14 +gunicorn==19.9.0 +idna==2.8 +isort==4.3.4 +jmespath==0.9.4 +lazy-object-proxy==1.3.1 +mccabe==0.6.1 +oauthlib==3.0.1 +Pillow==5.4.1 +psycopg2==2.7.6.1 +pycparser==2.19 +PyJWT==1.7.1 +pylint==2.3.1 +pyOpenSSL==19.0.0 +python-dateutil==2.8.0 +python-decouple==3.1 +python3-openid==3.1.0 +pytz==2018.9 +requests==2.22.0 +requests-oauthlib==1.2.0 +s3transfer==0.2.0 +six==1.12.0 +stripe==2.23.0 +toml==0.10.0 +typed-ast==1.3.1 +urllib3==1.25.3 +whitenoise==4.1.2 +wrapt==1.11.0 diff --git a/ecommerce-backend-master/ecommerce-backend-master/runtime.txt b/ecommerce-backend-master/ecommerce-backend-master/runtime.txt new file mode 100644 index 0000000..2af1a62 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/runtime.txt @@ -0,0 +1 @@ +python-3.7.3 \ No newline at end of file diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/settings/__init__.py b/ecommerce-backend-master/ecommerce-backend-master/src/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/settings/base.py b/ecommerce-backend-master/ecommerce-backend-master/src/settings/base.py new file mode 100644 index 0000000..6786b84 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/settings/base.py @@ -0,0 +1,140 @@ +# Base settings for the project + +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'corsheaders', + + 'products', + 'taggit', + + 'rest_framework', + 'rest_framework.authtoken', + 'rest_auth', + + 'django.contrib.sites', + 'allauth', + 'allauth.account', + 'allauth.socialaccount', + 'rest_auth.registration', + 'accounts', + + 'payments', + 'newsletter', +] + +SITE_ID = 1 + +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'src.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, "templates"), ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + + # `allauth` needs this from django + 'django.template.context_processors.request', + ], + }, + }, +] + +WSGI_APPLICATION = 'src.wsgi.application' + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +LANGUAGE_CODE = 'en-gb' + +TIME_ZONE = 'Europe/London' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# CUSTOM SETTINGS + +TAGGIT_CASE_INSENSITIVE = True + +CORS_ORIGIN_ALLOW_ALL = True + +# authentication related stuff +AUTH_USER_MODEL = 'accounts.CustomUser' + +AUTHENTICATION_BACKENDS = ( + # Needed to login by username in Django admin, regardless of `allauth` + "django.contrib.auth.backends.ModelBackend", + + # `allauth` specific authentication methods, such as login by e-mail + "allauth.account.auth_backends.AuthenticationBackend", +) + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': ( + # 'rest_framework.permissions.IsAuthenticated', + 'rest_framework.permissions.IsAuthenticatedOrReadOnly', + ), + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.BasicAuthentication', + ), +} +ACCOUNT_USER_MODEL_USERNAME_FIELD = None +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_USERNAME_REQUIRED = False +ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False +ACCOUNT_SESSION_REMEMBER = True +ACCOUNT_AUTHENTICATION_METHOD = 'email' +ACCOUNT_UNIQUE_EMAIL = True + +REST_AUTH_SERIALIZERS = { + 'USER_DETAILS_SERIALIZER': 'accounts.api.serializers.CustomUserDetailsSerializer', + 'LOGIN_SERIALIZER': 'accounts.api.serializers.CustomLoginSerializer', +} + +REST_AUTH_REGISTER_SERIALIZERS = { + "REGISTER_SERIALIZER": "accounts.api.serializers.CustomRegisterSerializer", +} + +REST_USE_JWT = True diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/settings/local-docker.py b/ecommerce-backend-master/ecommerce-backend-master/src/settings/local-docker.py new file mode 100644 index 0000000..ff1769c --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/settings/local-docker.py @@ -0,0 +1,31 @@ +# Development settings - for Docker + +from .base import * + +SECRET_KEY = 'f41z(gp#mm7ktjo1bfux-n*0!mlti$9d1@k_sws@&kl*@tfi21' + +DEBUG = True + +ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', ] + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'postgres', + 'USER': 'postgres', + 'HOST': 'db', + 'PORT': '5432', + } +} + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') + +# Media files +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + +STRIPE_SECRET_KEY = 'YOUR_STRIPE_SECRET_KEY' +STRIPE_PUBLISHABLE_KEY = 'YOUR_STRIPE_PUBLISHABLE_KEY' diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/settings/local.py b/ecommerce-backend-master/ecommerce-backend-master/src/settings/local.py new file mode 100644 index 0000000..3351c1c --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/settings/local.py @@ -0,0 +1,33 @@ +# Development settings + +from .base import * + +SECRET_KEY = 'f41z(gp#mm7ktjo1bfux-n*0!mlti$9d1@k_sws@&kl*@tfi21' + +DEBUG = True + +ALLOWED_HOSTS = ['localhost', '127.0.0.1', ] + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'eCommerceDjango', # postgres db name + 'USER': 'kamil', + 'PASSWORD': '', + 'HOST': 'localhost', + 'PORT': '', + } +} + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') +# STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'), ) + +# Media files +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') + +STRIPE_SECRET_KEY = 'YOUR_STRIPE_SECRET_KEY' +STRIPE_PUBLISHABLE_KEY = 'YOUR_STRIPE_PUBLISHABLE_KEY' diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/settings/production.py b/ecommerce-backend-master/ecommerce-backend-master/src/settings/production.py new file mode 100644 index 0000000..cd25ca8 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/settings/production.py @@ -0,0 +1,45 @@ +# Production settings + +from .base import * + +import django_heroku + +SECRET_KEY = os.environ.get("SECRET_KEY", "") + +DEBUG = False + +ALLOWED_HOSTS = ["https://ecommerce-backend-django.herokuapp.com/", ] + +INSTALLED_APPS += ["storages"] + +# STATIC_URL = "/static/" +# STATIC_ROOT = os.path.join(BASE_DIR, "static") + +# MEDIA_URL = "/media/" +# MEDIA_ROOT = os.path.join(BASE_DIR, "media") + +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # todo: use mailgun + +STRIPE_SECRET_KEY = os.environ.get("STRIPE_SECRET_KEY", "") +STRIPE_PUBLISHABLE_KEY = os.environ.get("STRIPE_PUBLISHABLE_KEY", "") + +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, "static"), +] +STATICFILES_STORAGE = "src.storage_backends.StaticStorage" +DEFAULT_FILE_STORAGE = "src.storage_backends.MediaStorage" + +AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "") +AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "") +AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME", "") +AWS_S3_CUSTOM_DOMAIN = "%s.s3.amazonaws.com" % AWS_STORAGE_BUCKET_NAME +AWS_S3_OBJECT_PARAMETERS = { + "CacheControl": "max-age=86400", +} +AWS_PRELOAD_METADATA = True +STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, "static") +ADMIN_MEDIA_PREFIX = "https://%s/%s/admin/" % (AWS_S3_CUSTOM_DOMAIN, "static") +MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, "media") + +# Activate Django-Heroku. +django_heroku.settings(locals()) diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/storage_backends.py b/ecommerce-backend-master/ecommerce-backend-master/src/storage_backends.py new file mode 100644 index 0000000..5969d52 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/storage_backends.py @@ -0,0 +1,9 @@ +from storages.backends.s3boto3 import S3Boto3Storage + +class MediaStorage(S3Boto3Storage): + location = "media" + file_overwrite = False + +class StaticStorage(S3Boto3Storage): + location = "static" + file_overwrite = False diff --git a/ecommerce-backend-master/ecommerce-backend-master/src/templates/base.html b/ecommerce-backend-master/ecommerce-backend-master/src/templates/base.html new file mode 100644 index 0000000..3f41201 --- /dev/null +++ b/ecommerce-backend-master/ecommerce-backend-master/src/templates/base.html @@ -0,0 +1,34 @@ + +
+ + +