diff --git a/backend/letters/serializers.py b/backend/letters/serializers.py index 4cf0afc..4ed67f7 100644 --- a/backend/letters/serializers.py +++ b/backend/letters/serializers.py @@ -30,7 +30,7 @@ class LetterSerializer(serializers.ModelSerializer): "updated_at", "images", ] # user to be fetched from request - read_only_fields = ["public_id", "created_at", "updated_at"] + read_only_fields = ["created_at", "updated_at"] def validate(self, data): if (data.get("encrypted_content") or data.get("encrypted_metadata")) and not data.get("encrypted_dek"): diff --git a/backend/letters/views.py b/backend/letters/views.py index 6d8e771..d4f3e17 100644 --- a/backend/letters/views.py +++ b/backend/letters/views.py @@ -16,7 +16,11 @@ class LetterView(generics.ListCreateAPIView): return Letter.objects.filter(user=self.request.user) def put(self, request, public_id): - serializer = self.get_serializer(data=request.data) + data = request.data.copy() + # remove public_id from data to avoid UniqueValidator firing + # since we use it from the URL for update_or_create anyway + data.pop("public_id", None) + serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) diff --git a/frontend/src/components/ui/ComposeCanvas.tsx b/frontend/src/components/ui/ComposeCanvas.tsx index dc38c01..c680b1f 100644 --- a/frontend/src/components/ui/ComposeCanvas.tsx +++ b/frontend/src/components/ui/ComposeCanvas.tsx @@ -5,11 +5,15 @@ const PAD = 36; export type CanvasTools = { addImage: (url: string, file: File) => void; - getData: () => { objects: any }; + getData: () => { objects: any[] }; getJsonData: () => string; getImages: () => { src: string; file: File }[]; }; +export interface FabricImageWithFile extends fabric.FabricImage { + _customRawFile: File; +} + export const ComposeCanvas = forwardRef((_props, ref) => { const wrapperRef = useRef(null); const canvasRef = useRef(null); @@ -150,7 +154,7 @@ export const ComposeCanvas = forwardRef((_props, ref) => { }); }, getData: () => { - if (!fabricRef.current) return ""; + if (!fabricRef.current) return { objects: [] }; return fabricRef.current.toJSON(); }, getJsonData: () => { @@ -159,8 +163,11 @@ export const ComposeCanvas = forwardRef((_props, ref) => { }, getImages: () => { if (!fabricRef.current) return []; - return fabricRef.current.getObjects("Image").map((img: any) => ({ - src: img._element.currentSrc, + const images = fabricRef.current.getObjects( + "Image", + ) as FabricImageWithFile[]; + return images.map((img) => ({ + src: (img.getElement() as HTMLImageElement).currentSrc, file: img._customRawFile, })); }, diff --git a/frontend/src/pages/Editor.tsx b/frontend/src/pages/Editor.tsx index de8d63b..44deb6b 100644 --- a/frontend/src/pages/Editor.tsx +++ b/frontend/src/pages/Editor.tsx @@ -19,9 +19,11 @@ import { useKeyStore } from "../store/useKeyStore"; import { CryptoUtils } from "../utils/crypto"; export default function Editor() { + const navigate = useNavigate(); + // check for existing letter const { public_id } = useParams(); const letterIdRef = useRef(public_id ?? ""); - const navigate = useNavigate(); + const [isSealing, setIsSealing] = useState(false); const [isSaveSuccess, setIsSaveSuccess] = useState(false); @@ -40,9 +42,11 @@ export default function Editor() { async function handleSeal(): Promise { if (!public_id) { + // if no uuid slug, then generate a new one and update params letterIdRef.current = crypto.randomUUID(); navigate(ROUTES.WRITE(letterIdRef.current), { replace: true }); } + if (isSealing) return; setIsSealing(true); const cryptoUtils = new CryptoUtils(); @@ -79,7 +83,11 @@ export default function Editor() { JSON.stringify(canvasData), masterKey, ); - const encrypted_metadata = ""; + + const encrypted_metadata = await cryptoUtils.encryptMetadata( + { recipient }, + masterKey, + ); // upload to server /* @@ -98,7 +106,7 @@ export default function Editor() { formData.append("status", "SEALED"); formData.append("encrypted_content", encrypted_letter.encrypted_content); formData.append("encrypted_dek", encrypted_letter.encrypted_dek); - formData.append("encrypted_metadata", encrypted_metadata); + formData.append("encrypted_metadata", encrypted_metadata.encrypted_content); encImageFilesMap.forEach((image, filename) => { formData.append("image_files", image, filename); });