Help
RSS
API
Feed
Maltego
Contact
Domain > asprinkleofally.com.au
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-06-04
13.249.98.49
(
ClassC
)
2025-07-29
3.170.73.102
(
ClassC
)
2026-02-15
3.163.24.95
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyServer: CloudFrontDate: Sun, 15 Feb 2026 11:18:36 GMTContent-Type: text/htmlContent-Length: 167Connection: keep-aliveLocation: https://asprinkleofally.com.au/X-Cache: Redirect from cloudfrontVia: 1.1 26c9d43b9089eee93b9e4ad4293d02c0.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: 9Coie0toPiP9po3TcORHY4-HlSySIQPyrfclK0Pn0aI3UHPm1E6S7wX-XSS-Protection: 1; modeblockX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originX-Content-Type-Options: nosniffVary: Origin html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>CloudFront/center>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 61446Connection: keep-aliveLast-Modified: Thu, 29 May 2025 05:45:06 GMTx-amz-server-side-encryption: AES256x-amz-version-id: DsqiHw2wryWI5Nt0ny0EOgpgUU2mwfZZAccept-Ranges: bytesServer: AmazonS3Date: Sun, 15 Feb 2026 11:18:36 GMTETag: 929a16a419b6a469f3024803072c255aX-Cache: Hit from cloudfrontVia: 1.1 eb6e5a827e45274130b33c12b0d48aaa.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: nkClR_O75EJhNh92L5rhv5C1bTchD-New_qfiqvEek85VK2y_swhrQAge: 8783X-XSS-Protection: 1; modeblockX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originX-Content-Type-Options: nosniffStrict-Transport-Security: max-age31536000Vary: Origin !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0> title>A Sprinkle of Ally - Custom Order/title> link relicon typeimage/vnd.microsoft.icon hrefhttps://staging.asprinkleofally.com.au/favicon.ico sizes192x192> link relstylesheet hrefhttps://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css> script srchttps://code.jquery.com/jquery-3.6.0.min.js>/script> script srchttps://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js>/script> link relstylesheet hrefhttps://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css> link hrefhttps://fonts.googleapis.com/css2?familyPoppins:wght@300;400;500;600&displayswap relstylesheet> style> body { /* background-image: url(https://staging.asprinkleofally.com.au/images/branding/Light Pink.jpg); */ background-image: url(https://staging.asprinkleofally.com.au/images/branding/Dark Pink.jpg); background-size: cover; background-position: center; background-attachment: fixed; color: #C21421; font-family: Poppins, Segoe UI, Tahoma, Geneva, Verdana, sans-serif; } .container { max-width: 800px; background: #FFD1E8; padding: 30px; border-radius: 15px; box-shadow: 0 8px 20px rgba(194, 20, 33, 0.15); margin-top: 30px; margin-bottom: 30px; } .page-header { border-bottom: 2px solid #C21421; padding-bottom: 15px; margin-bottom: 25px; } .form-group { margin-bottom: 1.5rem; } .form-control { border: 2px solid #FFD1E8; border-radius: 8px; padding: 10px 15px; transition: all 0.3s ease; min-height: 42px; height: auto; } .form-control:focus { border-color: #C21421; box-shadow: 0 0 0 0.2rem rgba(194, 20, 33, 0.25); } .form-control-file { padding: 20px; background: white; border-radius: 8px; border: 2px dashed #FFD1E8; cursor: pointer; transition: all 0.3s ease; width: 100%; text-align: center; color: #C21421; position: relative; overflow: hidden; } .form-control-file:hover { border-color: #C21421; background: #FFF0F5; } .form-control-file::before { content: \f03e; font-family: Font Awesome 5 Free; font-weight: 900; font-size: 24px; margin-bottom: 10px; display: block; } .form-control-file::after { content: Click or drag images here; display: block; font-size: 14px; margin-top: 5px; } .btn { border-radius: 8px; padding: 10px 20px; font-weight: 500; transition: all 0.3s ease; } .btn-primary { background: linear-gradient(45deg, #FA8CB5, #C21421); border: none; box-shadow: 0 4px 15px rgba(194, 20, 33, 0.3); width: 100%; padding: 15px 30px; font-size: 1.1rem; letter-spacing: 0.5px; } .btn-primary:hover { background: linear-gradient(45deg, #C21421, #FA8CB5); transform: translateY(-2px); box-shadow: 0 6px 18px rgba(194, 20, 33, 0.4); } .btn-secondary { background: #6c757d; border: none; } .btn-secondary:hover { background: #5a6268; transform: translateY(-2px); } .image-preview { max-width: 150px; max-height: 150px; width: 150px; height: 150px; margin: 5px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); object-fit: contain; background: white; } .preview-container { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 15px; } .preview-item { position: relative; display: inline-block; } .remove-image { position: absolute; top: -8px; right: -8px; background: #C21421; color: white; border-radius: 50%; width: 24px; height: 24px; line-height: 24px; text-align: center; cursor: pointer; font-size: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); transition: all 0.3s ease; } .remove-image:hover { background: #FA8CB5; transform: scale(1.1); } .loading-spinner { display: none; text-align: center; padding: 20px; } .form-section { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); margin-bottom: 20px; transition: all 0.3s ease; opacity: 0; transform: translateY(20px); } .form-section.visible { opacity: 1; transform: translateY(0); } .form-section:first-child { opacity: 1; transform: translateY(0); } .progress-indicator { display: flex; justify-content: center; margin: 20px 0; opacity: 0; transform: translateY(-20px); transition: all 0.3s ease; } .progress-indicator.visible { opacity: 1; transform: translateY(0); } .progress-step { width: 30px; height: 30px; border-radius: 50%; background: #FFD1E8; border: 2px solid #C21421; display: flex; align-items: center; justify-content: center; margin: 0 10px; position: relative; transition: all 0.2s ease; } .progress-step.active { background: #C21421; color: white; transform: scale(1.1); } .progress-step.completed { background: #C21421; color: white; } .progress-step.completed::after { content: \f00c; font-family: Font Awesome 5 Free; font-weight: 900; } .progress-line { position: absolute; top: 50%; left: 100%; width: 40px; height: 2px; background: #C21421; transform: translateY(-50%); } .progress-step:last-child .progress-line { display: none; } .form-section.hidden { display: none; } .form-section.fade-in { animation: fadeInUp 0.3s ease forwards; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .category-selection { display: flex; justify-content: center; gap: 20px; margin: 30px 0; flex-wrap: wrap; } .category-card { background: white; border-radius: 15px; padding: 30px; text-align: center; cursor: pointer; transition: all 0.2s ease; flex: 1; max-width: 250px; min-width: 200px; box-shadow: 0 4px 15px rgba(194, 20, 33, 0.1); position: relative; overflow: hidden; } .category-card::before { content: ; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(45deg, #FA8CB5, #C21421); opacity: 0; transition: opacity 0.2s ease; z-index: 1; } .category-card.selected::before { opacity: 1; } .category-card:hover { transform: translateY(-10px); box-shadow: 0 8px 25px rgba(194, 20, 33, 0.2); } .category-card.selected { color: white; } .category-card i { font-size: 3rem; margin-bottom: 15px; color: #C21421; transition: color 0.2s ease; position: relative; z-index: 2; } .category-card.selected i { color: white; } .category-card h4 { margin: 0; font-weight: 600; position: relative; z-index: 2; } .category-options { margin-top: 30px; transition: all 0.3s ease; } .category-options.hidden { display: none; } .category-options.visible { display: block; animation: fadeInUp 0.3s ease forwards; } .form-container { opacity: 0; transform: translateY(20px); transition: all 0.3s ease; } .form-container.visible { opacity: 1; transform: translateY(0); } .back-button { display: none; margin-bottom: 20px; } .back-button.visible { display: block; animation: slideIn 0.3s ease; } @keyframes slideIn { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } } .required-field::after { content: *; color: #C21421; margin-left: 4px; } select.form-control { padding-right: 30px; appearance: none; background-image: url(data:image/svg+xml,%3Csvg xmlnshttp://www.w3.org/2000/svg width12 height12 fill%23C21421 viewBox0 0 16 16%3E%3Cpath dM7.247 11.14L2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z/%3E%3C/svg%3E); background-repeat: no-repeat; background-position: right 10px center; background-size: 12px; min-height: 42px; height: auto; } inputtypedate { padding-right: 30px; appearance: none; background-image: url(data:image/svg+xml,%3Csvg xmlnshttp://www.w3.org/2000/svg width16 height16 fill%23C21421 viewBox0 0 16 16%3E%3Cpath dM6.445 11.688V6.354h-.633A12.6 12.6 0 0 0 4.5 7.16v.695c.375-.257.969-.62 1.258-.777h.06v4.61h1.266zm-7.5 3.013V3.5h1.5v11.201h-1.5z/%3E%3Cpath dM2.974 8.756c.1.164.277.417.487.68l.569-.667c.047-.056.1-.108.136-.16H3.3a.5.5 0 0 0-.5.5v.243zm3.427 3.426v-.243a.5.5 0 0 0-.5-.5H2.136c.035.052.09.104.136.16l.569.668c.21-.263.387-.516.487-.68z/%3E%3Cpath dM9.025 8.756c-.1.164-.277.417-.487.68l-.569-.667c-.047-.056-.1-.108-.136-.16h.669a.5.5 0 0 1 .5.5v.243zm3.427 3.426v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68z/%3E%3Cpath dM14.5 3.5h-1.5v11.201h1.5V3.5zm-3.5 8.513v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68zm-9.025 3.426v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68z/%3E%3Cpath dM8.974 8.756c.1.164.277.417.487.68l.569-.667c.047-.056.1-.108.136-.16h-.669a.5.5 0 0 0-.5.5v.243z/%3E%3Cpath dM5.5 7.16a12.6 12.6 0 0 1 1.312-1.806h.633v4.61h-1.266V6.354h-.633A12.6 12.6 0 0 0 4.5 7.16v.695c.375-.257.969-.62 1.258-.777h.06v4.61h1.266zm-7.5 3.013V3.5h1.5v11.201h-1.5z/%3E%3Cpath dM2.974 8.756c.1.164.277.417.487.68l.569-.667c.047-.056.1-.108.136-.16H3.3a.5.5 0 0 0-.5.5v.243zm3.427 3.426v-.243a.5.5 0 0 0-.5-.5H2.136c.035.052.09.104.136.16l.569.668c.21-.263.387-.516.487-.68z/%3E%3Cpath dM9.025 8.756c-.1.164-.277.417-.487.68l-.569-.667c-.047-.056-.1-.108-.136-.16h.669a.5.5 0 0 1 .5.5v.243zm3.427 3.426v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68z/%3E%3Cpath dM14.5 3.5h-1.5v11.201h1.5V3.5zm-3.5 8.513v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68zm-9.025 3.426v-.243a.5.5 0 0 1 .5-.5h.669c.035.052.09.104.136.16l-.569.668c-.21-.263-.387-.516-.487-.68z/%3E%3Cpath dM8.974 8.756c.1.164.277.417.487.68l.569-.667c.047-.056.1-.108.136-.16h-.669a.5.5 0 0 0-.5.5v.243z/%3E%3C/svg%3E); background-repeat: no-repeat; background-position: right 10px center; background-size: 16px; min-height: 42px; height: auto; position: relative; z-index: 1; } inputtypedate::-webkit-calendar-picker-indicator { opacity: 0; cursor: pointer; height: 100%; width: 100%; position: absolute; right: 0; top: 0; z-index: 2; } /* Add styles for the date picker popup */ inputtypedate::-webkit-calendar-picker { position: absolute; z-index: 1; } /* Ensure the date picker popup doesnt interfere with other elements */ inputtypedate::-webkit-calendar-picker-popup { position: fixed; z-index: 1000; } /* Add a wrapper for the date input to control its stacking context */ .date-input-wrapper { position: relative; z-index: 1; } /* Ensure the description textarea has a higher z-index than the date input */ #description { position: relative; z-index: 2; } .success-section { background: white; padding: 30px; border-radius: 15px; box-shadow: 0 4px 15px rgba(194, 20, 33, 0.1); margin-top: 20px; } .order-summary { background: #FFF0F5; padding: 20px; border-radius: 10px; margin-top: 20px; } .summary-content { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; } .summary-item { background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); } .summary-item h5 { color: #C21421; margin-bottom: 10px; } .summary-item p { margin: 0; color: #666; } .color-selector { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-top: 10px; } .color-option { display: flex; flex-direction: column; align-items: center; padding: 10px; border: 2px solid #FFD1E8; border-radius: 8px; cursor: pointer; transition: all 0.3s ease; background: white; } .color-option:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(194, 20, 33, 0.1); } .color-option.selected { border-color: #C21421; background: #FFF0F5; } .color-preview { width: 40px; height: 40px; border-radius: 50%; margin-bottom: 8px; border: 2px solid #ddd; } .color-name { font-size: 0.9rem; text-align: center; color: #666; } .color-option.selected .color-name { color: #C21421; font-weight: 500; } /style>/head>body> div classcontainer> div classpage-header> div classtext-center> img srchttps://staging.asprinkleofally.com.au/images/branding/PrimaryLogoCropped.png altA Sprinkle of Ally classimg-fluid mb-3 stylemax-height: 300px; width: auto; object-fit: contain; margin: 0 auto;> /div> h3 classtext-center>Custom Order Request/h3> /div> div classloading-spinner idloadingSpinner> div classspinner-border text-primary rolestatus> span classsr-only>Loading.../span> /div> p classmt-2>Submitting your request.../p> /div> div classsuccess-section idsuccessSection styledisplay: none;> div classtext-center mb-4> i classfas fa-check-circle text-success stylefont-size: 4rem;>/i> h3 classmt-3>Order Submitted Successfully!/h3> p classtext-muted>Thank you for choosing A Sprinkle of Ally/p> /div> div classorder-summary> h4 classmb-4>Order Summary/h4> div classsummary-content> !-- Order details will be populated here --> /div> /div> div classtext-center mt-4> button classbtn btn-primary onclickwindow.location.href/> i classfas fa-home>/i> Return to Home /button> /div> /div> form idcustomOrderForm> div classform-section> h4 classmb-4>Your Information/h4> div classform-group> label forname classrequired-field>Your Name/label> input typetext classform-control idname required> /div> div classform-group> label foremail classrequired-field>Email Address/label> input typeemail classform-control idemail required> /div> /div> div classform-section> h4 classmb-4>Select Your Category/h4> div classcategory-selection idcategorySelection> div classcategory-card data-categoryCake> i classfas fa-birthday-cake>/i> h4>Cake/h4> /div> div classcategory-card data-categoryCupcake> i classfas fa-cookie-bite>/i> h4>Cupcakes/h4> /div> div classcategory-card data-categoryCookie> i classfas fa-cookie>/i> h4>Cookies/h4> /div> /div> /div> div classform-section> h4 classmb-4>Order Details/h4> div classform-group> label forcompletionDate classrequired-field>Event Date/label> div classdate-input-wrapper> input typedate classform-control idcompletionDate required min> /div> /div> div classform-group> label fordescription classrequired-field>Description/label> textarea classform-control iddescription rows6 required placeholderPlease provide as much detail as possible about your custom order. Include any specific colours, flavours, designs, or special requirements you have in mind.>/textarea> /div> /div> div classcategory-options hidden idcategoryOptions> !-- Category-specific options will be shown here --> /div> div classform-section> h4 classmb-4>Inspiration Images (Optional)/h4> div classform-group> label forinspirationImages>Upload Inspiration Images/Videos/label> input typefile idinspirationImages nameinspirationImages multiple acceptimage/jpeg,image/jpg,image/png,image/gif,image/svg+xml,.jpg,.jpeg,.png,.gif,.svg,video/mp4,video/quicktime,video/x-msvideo,video/webm,.mp4,.mov,.avi,.webm classform-control> small classform-text text-muted>You can upload multiple images and videos. Supported formats: JPG, PNG, GIF, SVG, MP4, MOV, AVI, WEBM/small> /div> /div> div classmt-4> button typesubmit classbtn btn-primary> i classfas fa-paper-plane>/i> Submit Request /button> /div> /form> /div> script> //const apiBaseUrl https://kioyw5hvw8.execute-api.ap-southeast-2.amazonaws.com/; //Staging Admin const apiBaseUrl https://api.asprinkleofally.com.au/; //Production API // Global array to store all selected files let selectedFiles ; // Show first section and form container immediately $(document).ready(function() { $(.form-section:first-child).addClass(visible); $(#formContainer).addClass(visible); // Initialize the DataTransfer object for file management window.fileTransfer new DataTransfer(); // Set minimum date for event date (3 days from today) setMinimumDate(); }); // Function to set the minimum date for the date picker (3 days from today) function setMinimumDate() { const today new Date(); const minDate new Date(today); minDate.setDate(today.getDate() + 3); // Add 3 days // Format date as YYYY-MM-DD const year minDate.getFullYear(); const month String(minDate.getMonth() + 1).padStart(2, 0); const day String(minDate.getDate()).padStart(2, 0); const formattedDate `${year}-${month}-${day}`; // Set the min attribute of the date input document.getElementById(completionDate).min formattedDate; } // Function to update file input with all selected files function updateFileInput() { const input document.getElementById(inspirationImages); // Create a new DataTransfer object const dataTransfer new DataTransfer(); // Add all files to the DataTransfer object selectedFiles.forEach(file > { dataTransfer.items.add(file); }); // Update the file input input.files dataTransfer.files; } function handleImagePreview() { const input $(#inspirationImages)0; const newFiles input.files; const preview $(#imagePreview); // Clear existing previews preview.empty(); // Add newly selected files to our collection if (newFiles.length > 0) { for (let i 0; i newFiles.length; i++) { const file newFilesi; // Check if file is already in the selectedFiles array (by name and size) const isDuplicate selectedFiles.some(existingFile > existingFile.name file.name && existingFile.size file.size ); if (!isDuplicate) { selectedFiles.push(file); } } } // Update the file input with all selected files updateFileInput(); // Show previews for all selected files selectedFiles.forEach((file, index) > { if (file.type.startsWith(image/) || file.type image/svg+xml) { const reader new FileReader(); reader.onload function(e) { const previewItem $(` div classpreview-item> img src${e.target.result} classimage-preview altPreview ${index + 1}> div classremove-image data-index${index}> i classfas fa-times>/i> /div> /div> `); preview.append(previewItem); }; reader.readAsDataURL(file); } }); } // Handle remove image clicks $(document).on(click, .remove-image, function() { const index $(this).data(index); // Remove the file from our collection selectedFiles.splice(index, 1); // Update the file input updateFileInput(); // Regenerate all previews handleImagePreview(); }); $(#inspirationImages).change(handleImagePreview); $(#customOrderForm).submit(function(e) { e.preventDefault(); // Show loading spinner $(#loadingSpinner).show(); // Get the selected category const category $(.category-card.selected).data(category); if (!category) { alert(Please select a category); $(#loadingSpinner).hide(); return; } // Base form data const formData { name: $(#name).val(), email: $(#email).val(), category: category, description: $(#description).val(), completion_date: $(#completionDate).val() }; // Add category-specific data based on selected category switch(category) { case Cupcake: const cupcakeQuantity $(#cupcakeQuantity).val(); formData.cupcake_quantity cupcakeQuantity custom ? $(#customQuantity).val() : cupcakeQuantity; formData.cupcake_size $(#cupcakeSize).val(); formData.cupcake_flavour $(#cupcakeFlavour).val(); break; case Cake: formData.cake_size $(#cakeSize).val(); formData.cake_flavour $(#cakeFlavour).val(); // formData.cake_color $(#cakeColor).val(); break; case Cookie: const cookieQuantity $(#cookieQuantity).val(); formData.cookie_quantity cookieQuantity; formData.cookie_color $(#cookieColor).val(); break; } // Handle image uploads - use our global array instead of the input.files if (selectedFiles.length > 0) { // Update loading message $(#loadingSpinner p).text(`Preparing to upload ${selectedFiles.length} image(s)...`); // Get file extensions for each image const fileTypes selectedFiles.map(file > { return getFileExtension(file); }); // Initialize upload tracking const totalUploads selectedFiles.length; // Get presigned URLs for image uploads getPresignedUrls(selectedFiles.length, fileTypes) .then(presignedUrls > { // Update loading message $(#loadingSpinner p).text(`Starting upload of ${totalUploads} image(s)...`); // Upload all images to S3 return uploadImagesToS3(selectedFiles, presignedUrls, (completed, total) > { // Update progress in the loading message $(#loadingSpinner p).text(`Uploading images: ${completed} of ${total} complete...`); }); }) .then(imageUrls > { // Update loading message $(#loadingSpinner p).text(`All ${imageUrls.length} image(s) uploaded. Submitting form...`); // Add image URLs to form data formData.inspiration_image_urls imageUrls; // Submit form data to API submitFormData(formData); }) .catch(error > { $(#loadingSpinner).hide(); // Create more detailed error message let errorDetails error.toString(); if (error.message && error.message.includes(Failed to upload)) { errorDetails + \n\nThis may be due to a CORS configuration issue. Please check the browser console for more details.; } alert(Error uploading images: + errorDetails); }); } else { // No images to upload, just submit the form data submitFormData(formData); } }); /** * Get presigned URLs for uploading images to S3 * @param {number} count - Number of URLs to generate * @param {Array} fileTypes - Array of file extensions * @returns {PromiseArray>} - Array of presigned URL objects */ function getPresignedUrls(count, fileTypes) { const fileTypesParam fileTypes.join(,); return fetch(`${apiBaseUrl}get-upload-urls?count${count}&fileTypes${fileTypesParam}`) .then(response > { if (!response.ok) { throw new Error(Failed to get upload URLs); } return response.json(); }); } /** * Upload images to S3 using presigned URLs * @param {FileList} files - The image files to upload * @param {Array} presignedUrls - Array of presigned URL objects * @param {Function} progressCallback - Callback for upload progress updates * @returns {PromiseArray>} - Array of public URLs for the uploaded images */ function uploadImagesToS3(files, presignedUrls, progressCallback () > {}) { const uploadResults ; let completedUploads 0; return new Promise((resolve, reject) > { if (files.length 0) { resolve(); return; } console.log(Debug Starting upload of files:, Array.from(files).map(f > `${f.name} (${f.type})`)); console.log(Debug Presigned URLs:, presignedUrls.map(u > ({ ext: u.extension, contentType: u.contentType }))); // Process each file sequentially to avoid overwhelming the browser Array.from(files).forEach((file, index) > { const { uploadUrl, publicUrl, contentType } presignedUrlsindex; console.log(`Debug Uploading file ${index+1}: ${file.name} (${file.type}) to ${uploadUrl.split(?)0}`); console.log(`Debug Using content type: ${contentType}`); // Use the content type provided by the backend to ensure signature matches // Important: Were using the contentType from the presigned URL, not the files type fetch(uploadUrl, { method: PUT, body: file, // The Content-Type is already encoded in the presigned URL signature // Dont include it in the headers to avoid signature mismatch mode: cors, credentials: omit }) .then(response > { console.log(`Debug Upload response status for ${file.name}: ${response.status} ${response.statusText}`); if (!response.ok) { return response.text().then(text > { console.error(`Debug Error response for ${file.name}:`, text); throw new Error(`Failed to upload image ${index + 1}: ${response.status} ${response.statusText}`); }).catch(err > { throw new Error(`Failed to upload image ${index + 1}: ${response.status} ${response.statusText}`); }); } console.log(`Debug Successfully uploaded ${file.name} to ${publicUrl}`); return publicUrl; }) .then(url > { // Store the result and update progress uploadResultsindex url; completedUploads++; // Update progress progressCallback(completedUploads, files.length); // Check if all uploads are complete if (completedUploads files.length) { // All uploads complete, resolve with all URLs console.log(Debug All uploads completed successfully:, uploadResults); resolve(uploadResults); } }) .catch(error > { console.error(`Debug Error uploading ${file.name}:`, error); reject(error); // Reject the overall promise on any single upload failure }); }); }); } /** * Submit form data to API * @param {Object} formData - The form data to submit */ function submitFormData(formData) { // Update loading message $(#loadingSpinner p).text(Submitting your request...); $.ajax({ url: `${apiBaseUrl}custom-order`, type: POST, contentType: application/json, data: JSON.stringify(formData), success: function(response) { // Hide form elements $(#loadingSpinner).hide(); $(#customOrderForm).hide(); // Show success section $(#successSection).show(); // Populate order summary const summaryContent $(.summary-content); summaryContent.empty(); // Add customer information summaryContent.append(` div classsummary-item> h5>Customer Information/h5> p>strong>Name:/strong> ${formData.name}/p> p>strong>Email:/strong> ${formData.email}/p> p>strong>Category:/strong> ${formData.category}/p> p>strong>Event Date:/strong> ${formData.completion_date}/p> /div> `); // Add category-specific information const category formData.category; let categoryDetails ; switch(category) { case Cupcake: categoryDetails ` div classsummary-item> h5>Cupcake Details/h5> p>strong>Quantity:/strong> ${formData.cupcake_quantity}/p> p>strong>Size:/strong> ${formData.cupcake_size}/p> p>strong>Flavour:/strong> ${formData.cupcake_flavour}/p> /div>`; break; case Cake: categoryDetails ` div classsummary-item> h5>Cake Details/h5> p>strong>Size:/strong> ${formData.cake_size}/p> p>strong>Flavour:/strong> ${formData.cake_flavour}/p> /div>`; //p>strong>Colour:/strong> ${formData.cake_color}/p> break; case Cookie: categoryDetails ` div classsummary-item> h5>Cookie Details/h5> p>strong>Quantity:/strong> ${formData.cookie_quantity}/p> p>strong>Colour:/strong> ${formData.cookie_color}/p> /div>`; break; } summaryContent.append(categoryDetails); // Add description if provided if (formData.description) { summaryContent.append(` div classsummary-item> h5>Additional Details/h5> p>${formData.description}/p> /div> `); } // Add inspiration images if provided if (formData.inspiration_image_urls && formData.inspiration_image_urls.length > 0) { const imageGrid $(div classsummary-item>h5>Inspiration Images/h5>div classpreview-container>/div>/div>); formData.inspiration_image_urls.forEach(imageUrl > { imageGrid.find(.preview-container).append(` div classpreview-item> img src${imageUrl} classimage-preview altInspiration> /div> `); }); summaryContent.append(imageGrid); } }, error: function(xhr, status, error) { $(#loadingSpinner).hide(); alert(Error submitting request: + error); } }); } // Color selector functionality $(.color-option).click(function() { const selector $(this).closest(.color-selector); selector.find(.color-option).removeClass(selected); $(this).addClass(selected); // Update hidden input const hiddenInput selector.siblings(inputtypehidden); hiddenInput.val($(this).data(color)); }); // Function to get file extension from MIME type or filename function getFileExtension(file) { // First try to get extension from MIME type const mimeType file.type; let extension ; // Handle image types if (mimeType.startsWith(image/)) { extension mimeType.split(/)1; // Handle special cases if (extension jpeg) extension jpg; if (extension svg+xml) extension svg; } // Handle video types else if (mimeType.startsWith(video/)) { extension mimeType.split(/)1; // Handle special cases if (extension quicktime) extension mov; if (extension x-msvideo) extension avi; } // If no extension found from MIME type, try to get it from filename if (!extension) { const filename file.name; const lastDot filename.lastIndexOf(.); if (lastDot > 0) { extension filename.slice(lastDot + 1).toLowerCase(); } } return extension; } // Function to upload images and videos to S3 async function uploadImagesToS3(files) { if (!files || files.length 0) return ; // Get file extensions for each file const fileTypes Array.from(files).map(file > getFileExtension(file)); // Get presigned URLs for all files const presignedUrls await getPresignedUrls(files.length, fileTypes); // Upload each file const uploadPromises Array.from(files).map(async (file, index) > { const { uploadUrl, contentType } presignedUrlsindex; // Use the content type from the presigned URL to avoid signature mismatches const response await fetch(uploadUrl, { method: PUT, body: file, mode: cors, credentials: omit }); if (!response.ok) { throw new Error(`Failed to upload ${file.name}: ${response.status} ${response.statusText}`); } return presignedUrlsindex.publicUrl; }); return Promise.all(uploadPromises); } // Category selection handler $(.category-card).click(function() { const category $(this).data(category); $(.category-card).removeClass(selected); $(this).addClass(selected); // Show category-specific options const optionsContainer $(#categoryOptions); optionsContainer.removeClass(hidden).addClass(visible); // Update options based on category let optionsHtml ; switch(category) { case Cupcake: optionsHtml ` div classform-section> h4 classmb-4>Cupcake Options/h4> div classform-group> label forcupcakeSize classrequired-field>Size/label> select classform-control idcupcakeSize required> option value>Select size/option> option valueMini>Mini/option> option valueNormal>Normal/option> /select> /div> div classform-group> label forcupcakeQuantity classrequired-field>Quantity/label> select classform-control idcupcakeQuantity required> option value>Select quantity/option> option value6 data-sizeOriginal>6/option> option value12 data-sizeOriginal>12/option> option value24 data-sizeboth>24/option> option value36 data-sizeOriginal>36/option> option value48 data-sizeboth>48/option> /select> /div> div classform-group> label forcupcakeFlavour classrequired-field>Flavour/label> select classform-control idcupcakeFlavour required> option value>Select flavour/option> option valueVanilla>Vanilla/option> option valueChocolate>Chocolate/option> option valueCaramel>Caramel/option> option valueRed Velvet>Red Velvet/option> /select> /div> /div>`; break; case Cake: optionsHtml ` div classform-section> h4 classmb-4>Cake Options/h4> div classform-group> label forcakeSize classrequired-field>Size/label> select classform-control idcakeSize required> option value>Select size/option> option value6>6 (Serves 8-10 people)/option> option value8>8 (Serves 12-16 people)/option> option value10>10 (Serves 20-25 people)/option> /select> /div> div classform-group> label forcakeFlavour classrequired-field>Flavour/label> select classform-control idcakeFlavour required> option value>Select flavour/option> option valueWhite Chocolate Mud>White Chocolate Mud/option> option valueChocolate Mud>Chocolate Mud/option> option valueCaramel Almond>Caramel Almond/option> option valueRed Velvet>Red Velvet/option> /select> /div> /div>`; /* div classform-group> label forcakeColor classrequired-field>Colour/label> div classcolor-selector idcakeColorSelector> div classcolor-option data-colorPastel Rainbow> div classcolor-preview stylebackground: linear-gradient(45deg, #FFB6C1, #87CEEB, #98FB98, #DDA0DD);>/div> span classcolor-name>Pastel Rainbow/span> /div> div classcolor-option data-colorBlack> div classcolor-preview stylebackground: #000000;>/div> span classcolor-name>Black/span> /div> div classcolor-option data-colorBlue Bell> div classcolor-preview stylebackground: #A2A2D0;>/div> span classcolor-name>Blue Bell/span> /div> div classcolor-option data-colorBright Blue> div classcolor-preview stylebackground: #4169E1;>/div> span classcolor-name>Bright Blue/span> /div> div classcolor-option data-colorBurgundy> div classcolor-preview stylebackground: #800020;>/div> span classcolor-name>Burgundy/span> /div> div classcolor-option data-colorChampagne> div classcolor-preview stylebackground: #F7E7CE;>/div> span classcolor-name>Champagne/span> /div> div classcolor-option data-colorCream> div classcolor-preview stylebackground: #FFFDD0;>/div> span classcolor-name>Cream/span> /div> div classcolor-option data-colorDusk> div classcolor-preview stylebackground: #8B7355;>/div> span classcolor-name>Dusk/span> /div> div classcolor-option data-colorHot Pink> div classcolor-preview stylebackground: #FF69B4;>/div> span classcolor-name>Hot Pink/span> /div> div classcolor-option data-colorLemon> div classcolor-preview stylebackground: #FFF700;>/div> span classcolor-name>Lemon/span> /div> div classcolor-option data-colorLight Blue> div classcolor-preview stylebackground: #ADD8E6;>/div> span classcolor-name>Light Blue/span> /div> div classcolor-option data-colorLight Green> div classcolor-preview stylebackground: #90EE90;>/div> span classcolor-name>Light Green/span> /div> div classcolor-option data-colorLight Pink> div classcolor-preview stylebackground: #FFB6C1;>/div> span classcolor-name>Light Pink/span> /div> div classcolor-option data-colorLilac> div classcolor-preview stylebackground: #C8A2C8;>/div> span classcolor-name>Lilac/span> /div> div classcolor-option data-colorOrange> div classcolor-preview stylebackground: #FFA500;>/div> span classcolor-name>Orange/span> /div> div classcolor-option data-colorPurple> div classcolor-preview stylebackground: #800080;>/div> span classcolor-name>Purple/span> /div> div classcolor-option data-colorRed> div classcolor-preview stylebackground: #FF0000;>/div> span classcolor-name>Red/span> /div> div classcolor-option data-colorSage> div classcolor-preview stylebackground: #BCB88A;>/div> span classcolor-name>Sage/span> /div> div classcolor-option data-colorThin Black> div classcolor-preview stylebackground: #000000;>/div> span classcolor-name>Thin Black/span> /div> div classcolor-option data-colorWhite> div classcolor-preview stylebackground: #FFFFFF;>/div> span classcolor-name>White/span> /div> div classcolor-option data-colorBaby Pink> div classcolor-preview stylebackground: #FFC0CB;>/div> span classcolor-name>Baby Pink/span> /div> div classcolor-option data-colorBeige> div classcolor-preview stylebackground: #F5F5DC;>/div> span classcolor-name>Beige/span> /div> div classcolor-option data-colorBlush> div classcolor-preview stylebackground: #FFE4E1;>/div> span classcolor-name>Blush/span> /div> div classcolor-option data-colorCandy Pink> div classcolor-preview stylebackground: #FF69B4;>/div> span classcolor-name>Candy Pink/span> /div> div classcolor-option data-colorCoral> div classcolor-preview stylebackground: #FF7F50;>/div> span classcolor-name>Coral/span> /div> div classcolor-option data-colorCream / Off White> div classcolor-preview stylebackground: #FFFDD0;>/div> span classcolor-name>Cream / Off White/span> /div> div classcolor-option data-colorGrey> div classcolor-preview stylebackground: #808080;>/div> span classcolor-name>Grey/span> /div> div classcolor-option data-colorGreen> div classcolor-preview stylebackground: #008000;>/div> span classcolor-name>Green/span> /div> div classcolor-option data-colorMango> div classcolor-preview stylebackground: #FFA500;>/div> span classcolor-name>Mango/span> /div> div classcolor-option data-colorOlive> div classcolor-preview stylebackground: #808000;>/div> span classcolor-name>Olive/span> /div> div classcolor-option data-colorPeach> div classcolor-preview stylebackground: #FFDAB9;>/div> span classcolor-name>Peach/span> /div> /div> input typehidden idcakeColor required> /div>*/ break; case Cookie: optionsHtml ` div classform-section> h4 classmb-4>Cookie Options/h4> div classform-group> label forcookieQuantity classrequired-field>Quantity/label> input typenumber classform-control idcookieQuantity required min20 placeholderEnter quantity (minimum 20)> small classform-text text-muted>Minimum order quantity is 20 cookies/small> /div> div classform-group> label forcookieColor classrequired-field>Colour/label> select classform-control idcookieColor required> option value>Select colour/option> option valueRed>Red/option> option valueBlue>Blue/option> option valueGreen>Green/option> option valueYellow>Yellow/option> /select> /div> /div>`; break; } optionsContainer.html(optionsHtml); }); // Add cupcake size-specific quantity handling $(document).on(change, #cupcakeSize, function() { const size $(this).val(); const quantitySelect $(#cupcakeQuantity); const currentQuantity quantitySelect.val(); // Reset quantity selection quantitySelect.val(); // Show/hide options based on size quantitySelect.find(option).each(function() { const option $(this); const validSizes option.data(size); if (validSizes both || validSizes size) { option.show(); } else { option.hide(); } }); // If there was a previously selected quantity thats still valid, keep it if (currentQuantity && quantitySelect.find(`optionvalue${currentQuantity}`).is(:visible)) { quantitySelect.val(currentQuantity); } }); // Trigger the cupcake size change handler on page load if a size is selected if ($(#cupcakeSize).val()) { $(#cupcakeSize).trigger(change); } //TODO: expire old presigned url image uploads // Handle form section visibility on scroll $(window).scroll(function() { $(.form-section).each(function() { const sectionTop $(this).offset().top; const windowHeight $(window).height(); const scrollTop $(window).scrollTop(); if (sectionTop scrollTop + windowHeight * 0.9) { $(this).addClass(visible); } }); }); /script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]