Museum Central is a faithful recreation inspired by the renowned e-commerce site, Boutiques de Musées where users can explore their favorite museums and discover an array of exquisite products. Logged in users can seamlessly add captivating products to their cart, creating a wishlist of cultural gems, and become curators in their own right by allowing them to create and manage their virtual museums populated by unique products of their choice. Whether you're a history buff, art enthusiast, or science lover, Museum Central is your gateway to a curated collection of cultural wonders. Click here to view the Museum Central Live Site
This is a concise list of technologies utilized to develop this project.
-
Clone this repository (only the main branch).
-
Install dependencies.
pipenv install -r requirements.txt
to regenerate requirements.txt run
pipenv requirements > requirements.txt
-
Create a .env file based on the example with proper settings for your development environment.
-
Make sure the SQLite3 database connection URL is in the .env file.
-
Make sure the AWS S3 credentials (bucket name, S3 key, and S3 secret key) are in the .env file. If you don't have an AWS S3 bucket set up:
- Set up an account on AWS
- Create a new S3 bucket for your application (to store your files). Refer to Getting started with Amazon S3 for more details.
- Create a user to access the S3 bucket (this user has the necessary credentials) If you accidentally expose these credentials, you should delete the user's credentials and create new credentials. Refer to Creating an IAM user in your AWS account for more details.
-
Make sure the Google Cloud API key to use is in the .env file.
-
This project organizes all tables inside the
flask_schema
schema, defined by theSCHEMA
environment variable. Replace the value forSCHEMA
with a unique name, making sure you use the snake_case convention. -
Get into your pipenv, migrate your database, seed your database, and run your Flask app:
pipenv shell
flask db upgrade
flask seed all
flask run
-
To run the React frontend in development,
cd
into the react-vite directory and runnpm i
to install dependencies. Finally, runnpm run dev
to open the application on the local browser.
You will be able to test the features without sign up by clicking on the "Demo User" button in the Login Modal. You will then be directed to the landing page, where you can create a musuem, create a product for a museum that you own, or add products to your cart.
- Google Maps
- AWS
- In the backend, I set up a file with helper functions that uses environmental variables to connect to the AWS S3 Bucket and exports functions to delete from and upload images to that bucket.
# app/aws.py import boto3 import botocore import os import uuid ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "svg"} BUCKET_NAME = os.environ.get("S3_BUCKET") S3_LOCATION = f"http://{BUCKET_NAME}.s3.amazonaws.com/" s3 = boto3.client( "s3", aws_access_key_id=os.environ.get("S3_KEY"), aws_secret_access_key=os.environ.get("S3_SECRET"), region_name="us-west-1" ) def get_unique_filename(filename): ext = filename.rsplit(".", 1)[1].lower() unique_filename = uuid.uuid4().hex return f"{unique_filename}.{ext}" def upload_file_to_s3(file, acl="public-read"): try: s3.upload_fileobj( file, BUCKET_NAME, file.filename, ExtraArgs={ "ACL": acl, "ContentType": file.content_type } ) except Exception as e: # in case the our s3 upload fails return {"errors": str(e)} return {"url": f"{S3_LOCATION}{file.filename}"} def remove_file_from_s3(image_url): # AWS needs the image file name, not the URL, # so we split that out of the URL key = image_url.rsplit("/", 1)[1] try: s3.delete_object( Bucket=BUCKET_NAME, Key=key ) except Exception as e: return { "errors": str(e) } return True
- In the creation, edit, and deletion routes for museums and products, I call these helper functions when appropriate
- Note: one issue I ran into was in my create product backend route, where I seemingly wasn't able to create a preview image for the newly created product since the product id passed into the new Product Image instance always ended up being null. I resolved this issue by refactoring the create product backend route so that the the newly created product is added and committed to the database before the preview image creation was attempted within the same route.
# app/api/product_routes.py @product_routes.route('', methods=['POST']) @login_required def create_product(): form = ProductForm() form['csrf_token'].data = request.cookies['csrf_token'] if form.validate_on_submit(): data = form.data new_product = Product( museum_id = data["museum_id"], name = data["name"], description = data["description"], price = data["price"], category = data["category"], dimensions = data["dimensions"], quantity = data["quantity"] ) db.session.add(new_product) db.session.commit() new_product_image = ProductImage( product_id = new_product.id, image_url = data["image_url"], preview = True ) db.session.add(new_product_image) db.session.commit() return new_product.to_dict() return {'errors': form.errors}, 400
- Search
- Account Page
- Orders