|
| 1 | +import binascii |
| 2 | + |
1 | 3 | import graphene |
2 | 4 | import base64 |
3 | 5 | import os |
|
32 | 34 | import logging |
33 | 35 | from zoneinfo import ZoneInfo |
34 | 36 | from sqlalchemy import func, cast, Date |
| 37 | +import boto3 |
| 38 | +from botocore.exceptions import ClientError |
35 | 39 |
|
36 | 40 | local_tz = ZoneInfo("America/New_York") |
37 | 41 |
|
@@ -833,81 +837,103 @@ class Arguments: |
833 | 837 | def mutate(self, info, name, net_id, email, encoded_image=None): |
834 | 838 | # Check if a user with the given NetID already exists |
835 | 839 | existing_user = db_session.query(UserModel).filter(UserModel.net_id == net_id).first() |
836 | | - final_photo_url = None |
837 | 840 | if existing_user: |
838 | 841 | raise GraphQLError("NetID already exists.") |
839 | 842 |
|
| 843 | + final_photo_url = None |
| 844 | + |
840 | 845 | if encoded_image: |
841 | | - upload_url = os.getenv("DIGITAL_OCEAN_URL") |
842 | | - if not upload_url: |
843 | | - raise GraphQLError("Upload URL not configured.") |
844 | 846 |
|
845 | | - headers = {"Content-Type": "application/json"} |
| 847 | + s3 = boto3.client( |
| 848 | + "s3", |
| 849 | + endpoint_url=os.getenv("DIGITAL_OCEAN_URL"), |
| 850 | + aws_access_key_id=os.getenv("DIGITAL_OCEAN_ACCESS"), |
| 851 | + aws_secret_access_key=os.getenv("DIGITAL_OCEAN_SECRET_ACCESS") |
| 852 | + ) |
846 | 853 |
|
847 | | - image_bytes = base64.b64decode(encoded_image) |
848 | | - files = {"image": ("profile.png", image_bytes, "image/png")} |
849 | | - data = {"bucket": os.getenv("BUCKET_NAME")} |
850 | 854 | try: |
851 | | - response = requests.post(upload_url, files=files, data=data) |
852 | | - response.raise_for_status() |
853 | | - json_response = response.json() |
854 | | - final_photo_url = json_response.get("data") |
855 | | - if not final_photo_url: |
856 | | - raise GraphQLError("No URL returned from upload service.") |
857 | | - except requests.exceptions.RequestException as e: |
858 | | - print(f"Request failed: {e}") |
859 | | - raise GraphQLError(f"Failed to upload photo: {e}") |
| 855 | + image_data = base64.b64decode(encoded_image, validate=True) |
| 856 | + except (binascii.Error, ValueError) as err: |
| 857 | + raise GraphQLError("Invalid profile image encoding.") |
860 | 858 |
|
| 859 | + try: |
| 860 | + bucket = "appdev-upload" |
| 861 | + path = f"uplift-dev/user-profile/{net_id}-profile.png" |
| 862 | + region = "nyc3" |
| 863 | + |
| 864 | + s3.put_object( |
| 865 | + Bucket=bucket, |
| 866 | + Key=path, |
| 867 | + Body=image_data, |
| 868 | + ContentType="image/png", |
| 869 | + ACL="public-read" |
| 870 | + ) |
| 871 | + |
| 872 | + final_photo_url = f"https://{bucket}.{region}.digitaloceanspaces.com/{path}" |
| 873 | + except ClientError as e: |
| 874 | + print("Upload error:", e) |
| 875 | + raise GraphQLError("Error uploading user profile picture.") |
| 876 | + |
861 | 877 | new_user = UserModel(name=name, net_id=net_id, email=email, encoded_image=final_photo_url) |
862 | 878 | db_session.add(new_user) |
863 | 879 | db_session.commit() |
864 | 880 |
|
865 | 881 | return new_user |
866 | 882 |
|
867 | 883 |
|
868 | | -class EditUser(graphene.Mutation): |
| 884 | +class EditUserById(graphene.Mutation): |
869 | 885 | class Arguments: |
| 886 | + user_id = graphene.Int(required=True) |
870 | 887 | name = graphene.String(required=False) |
871 | | - net_id = graphene.String(required=True) |
872 | 888 | email = graphene.String(required=False) |
873 | 889 | encoded_image = graphene.String(required=False) |
874 | 890 |
|
875 | 891 | Output = User |
876 | 892 |
|
877 | | - def mutate(self, info, net_id, name=None, email=None, encoded_image=None): |
878 | | - existing_user = db_session.query(UserModel).filter(UserModel.net_id == net_id).first() |
| 893 | + @jwt_required() |
| 894 | + def mutate(self, info, user_id, name=None, email=None, encoded_image=None): |
| 895 | + existing_user = db_session.query(UserModel).filter(UserModel.id == user_id).first() |
| 896 | + |
879 | 897 | if not existing_user: |
880 | | - raise GraphQLError("User with given net id does not exist.") |
881 | | - |
| 898 | + raise GraphQLError("User with given id does not exist.") |
| 899 | + if get_jwt_identity() != user_id: |
| 900 | + raise GraphQLError("Unauthorized operation") |
882 | 901 | if name is not None: |
883 | 902 | existing_user.name = name |
884 | 903 | if email is not None: |
885 | 904 | existing_user.email = email |
886 | 905 | if encoded_image is not None: |
887 | | - upload_url = os.getenv("DIGITAL_OCEAN_URL") # Base URL for upload endpoint |
888 | | - if not upload_url: |
889 | | - raise GraphQLError("Upload URL not configured.") |
890 | | - |
891 | | - payload = { |
892 | | - "bucket": os.getenv("BUCKET_NAME", "DEV_BUCKET"), |
893 | | - "image": encoded_image, # Base64-encoded image string |
894 | | - } |
895 | | - headers = {"Content-Type": "application/json"} |
896 | | - |
897 | | - print(f"Uploading image with payload: {payload}") |
| 906 | + final_photo_url = None |
| 907 | + s3 = boto3.client( |
| 908 | + "s3", |
| 909 | + endpoint_url=os.getenv("DIGITAL_OCEAN_URL"), |
| 910 | + aws_access_key_id=os.getenv("DIGITAL_OCEAN_ACCESS"), |
| 911 | + aws_secret_access_key=os.getenv("DIGITAL_OCEAN_SECRET_ACCESS") |
| 912 | + ) |
| 913 | + |
| 914 | + try: |
| 915 | + image_data = base64.b64decode(encoded_image, validate=True) |
| 916 | + except (binascii.Error, ValueError) as err: |
| 917 | + raise GraphQLError("Invalid profile image encoding.") |
898 | 918 |
|
899 | 919 | try: |
900 | | - response = requests.post(upload_url, json=payload, headers=headers) |
901 | | - response.raise_for_status() |
902 | | - json_response = response.json() |
903 | | - print(f"Upload API response: {json_response}") |
904 | | - final_photo_url = json_response.get("data") |
905 | | - if not final_photo_url: |
906 | | - raise GraphQLError("No URL returned from upload service.") |
| 920 | + bucket = "appdev-upload" |
| 921 | + path = f"uplift-dev/user-profile/{existing_user.net_id}-profile.png" |
| 922 | + region = "nyc3" |
| 923 | + |
| 924 | + s3.put_object( |
| 925 | + Bucket=bucket, |
| 926 | + Key=path, |
| 927 | + Body=image_data, |
| 928 | + ContentType="image/png", |
| 929 | + ACL="public-read" |
| 930 | + ) |
| 931 | + |
| 932 | + final_photo_url = f"https://{bucket}.{region}.digitaloceanspaces.com/{path}" |
907 | 933 | existing_user.encoded_image = final_photo_url |
908 | | - except requests.exceptions.RequestException as e: |
909 | | - print(f"Request failed: {e}") |
910 | | - raise GraphQLError("Failed to upload photo.") |
| 934 | + except ClientError as e: |
| 935 | + print("Upload error:", e) |
| 936 | + raise GraphQLError("Error adding new user profile picture.") |
911 | 937 |
|
912 | 938 | db_session.commit() |
913 | 939 | return existing_user |
@@ -1063,6 +1089,7 @@ def mutate(self, info, user_id, workout_goal): |
1063 | 1089 |
|
1064 | 1090 | db_session.commit() |
1065 | 1091 | return user |
| 1092 | + |
1066 | 1093 | class logWorkout(graphene.Mutation): |
1067 | 1094 | class Arguments: |
1068 | 1095 | workout_time = graphene.DateTime(required=True) |
@@ -1157,11 +1184,34 @@ class Arguments: |
1157 | 1184 |
|
1158 | 1185 | Output = User |
1159 | 1186 |
|
| 1187 | + @jwt_required() |
1160 | 1188 | def mutate(self, info, user_id): |
1161 | 1189 | # Check if user exists |
1162 | 1190 | user = User.get_query(info).filter(UserModel.id == user_id).first() |
| 1191 | + |
1163 | 1192 | if not user: |
1164 | 1193 | raise GraphQLError("User with given ID does not exist.") |
| 1194 | + |
| 1195 | + if get_jwt_identity() != user_id: |
| 1196 | + raise GraphQLError("Unauthorized operation") |
| 1197 | + |
| 1198 | + s3 = boto3.client( |
| 1199 | + "s3", |
| 1200 | + endpoint_url=os.getenv("DIGITAL_OCEAN_URL"), |
| 1201 | + aws_access_key_id=os.getenv("DIGITAL_OCEAN_ACCESS"), |
| 1202 | + aws_secret_access_key=os.getenv("DIGITAL_OCEAN_SECRET_ACCESS") |
| 1203 | + ) |
| 1204 | + |
| 1205 | + if user.encoded_image: |
| 1206 | + try: |
| 1207 | + s3.delete_object( |
| 1208 | + Bucket="appdev-upload", |
| 1209 | + Key=f"uplift-dev/user-profile/{user.net_id}-profile.png", |
| 1210 | + ) |
| 1211 | + except ClientError as e: |
| 1212 | + print("Delete error:", e) |
| 1213 | + raise GraphQLError("Error deleting user profile picture") |
| 1214 | + |
1165 | 1215 | db_session.delete(user) |
1166 | 1216 | db_session.commit() |
1167 | 1217 | return user |
@@ -1440,7 +1490,7 @@ def mutate(self, info, user_id): |
1440 | 1490 | class Mutation(graphene.ObjectType): |
1441 | 1491 | create_giveaway = CreateGiveaway.Field(description="Creates a new giveaway.") |
1442 | 1492 | create_user = CreateUser.Field(description="Creates a new user.") |
1443 | | - edit_user = EditUser.Field(description="Edit a new user.") |
| 1493 | + edit_user = EditUserById.Field(description="Edit a new user by id.") |
1444 | 1494 | enter_giveaway = EnterGiveaway.Field(description="Enters a user into a giveaway.") |
1445 | 1495 | set_workout_goals = SetWorkoutGoals.Field(description="Set a user's workout goals.") |
1446 | 1496 | log_workout = logWorkout.Field(description="Log a user's workout.") |
|
0 commit comments