@zodic/shared 0.0.345 → 0.0.347

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/app/api/index.ts +128 -39
  2. package/package.json +1 -1
package/app/api/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import OpenAI from 'openai';
2
+ import { v4 } from 'uuid';
2
3
  import {
3
4
  BackendBindings,
4
5
  BatchInputItem,
@@ -785,46 +786,115 @@ export const Api = (env: BackendBindings) => ({
785
786
  'x-api-key': `${env.PIAPI_API_KEY}`,
786
787
  'Content-Type': 'application/json',
787
788
  };
788
-
789
+
789
790
  // Validate image URLs before making the request
790
- const validateImageUrl = async (url: string, label: string): Promise<void> => {
791
+ const validateImageUrl = async (
792
+ url: string,
793
+ label: string
794
+ ): Promise<void> => {
791
795
  try {
792
796
  const response = await fetch(url, { method: 'HEAD' });
793
797
  if (!response.ok) {
794
- throw new Error(`${label} is inaccessible: ${response.status} ${response.statusText}`);
798
+ throw new Error(
799
+ `${label} is inaccessible: ${response.status} ${response.statusText}`
800
+ );
795
801
  }
796
802
  const contentType = response.headers.get('Content-Type');
797
803
  if (!contentType || !contentType.startsWith('image/')) {
798
- throw new Error(`${label} is not a valid image: Content-Type is ${contentType}`);
804
+ throw new Error(
805
+ `${label} is not a valid image: Content-Type is ${contentType}`
806
+ );
799
807
  }
800
808
  } catch (err: any) {
801
809
  console.error(`Error validating ${label}:`, err.message);
802
810
  throw new Error(`Invalid ${label}: ${err.message}`);
803
811
  }
804
812
  };
805
-
806
- // Helper function to generate a resized image URL using Cloudflare Image Transformations
807
- const getResizedImageUrl = (originalUrl: string, label: string): string => {
808
- // Use your Cloudflare Worker domain
809
- const zone = 'https://zodic-backend.lucdelbel.workers.dev';
810
-
811
- // Construct the Cloudflare Image Transformation URL
812
- // Resize to fit within 2048x2048 while maintaining proportions
813
- const resizedUrl = `${zone}/cdn-cgi/image/width=2048,height=2048,fit=scale-down/${encodeURIComponent(originalUrl)}`;
814
-
815
- console.log(`${label} resized URL:`, resizedUrl);
816
- return resizedUrl;
813
+
814
+ // Helper function to resize an image by calling the Express server
815
+ const resizeImage = async (
816
+ url: string,
817
+ label: string
818
+ ): Promise<ArrayBuffer> => {
819
+ const resizeEndpoint = `https://zodiako-image-mounter.onrender.com/resize-image?url=${encodeURIComponent(
820
+ url
821
+ )}`; // Replace with your Express server URL
822
+ try {
823
+ const response = await fetch(resizeEndpoint);
824
+ if (!response.ok) {
825
+ throw new Error(
826
+ `${label} resize failed: ${response.status} ${response.statusText}`
827
+ );
828
+ }
829
+ const contentType = response.headers.get('Content-Type');
830
+ if (!contentType || !contentType.startsWith('image/')) {
831
+ throw new Error(
832
+ `${label} resize response is not an image: Content-Type is ${contentType}`
833
+ );
834
+ }
835
+ return await response.arrayBuffer();
836
+ } catch (err: any) {
837
+ console.error(`Error resizing ${label}:`, err.message);
838
+ throw new Error(`Failed to resize ${label}: ${err.message}`);
839
+ }
817
840
  };
818
-
841
+
842
+ const getKey = (filePath: string) => {
843
+ const fileNameParts = filePath.split('.');
844
+ const extension = fileNameParts.pop();
845
+ const fileNameWithoutExtension = fileNameParts.join('.');
846
+
847
+ const newFileName = `${fileNameWithoutExtension}-${v4()}.${extension}`;
848
+ const key = `${newFileName.replace(/ /g, '_')}`;
849
+
850
+ return key;
851
+ };
852
+
853
+ // Helper function to upload an image to storage and get a public URL
854
+ const uploadImageToStorage = async (
855
+ imageData: ArrayBuffer,
856
+ label: string
857
+ ): Promise<string> => {
858
+ const key = getKey(label);
859
+ await env.PHOTOS_BUCKET.put(key, imageData);
860
+ const imageUrl = `https://pub-c4bb4b3e4ccd46b59eed7fa434c59b60.r2.dev/${key}`;
861
+ return imageUrl;
862
+ // Replace this with your actual storage service implementation (e.g., Cloudflare R2, AWS S3)
863
+ // Example for Cloudflare R2:
864
+ // const r2Response = await env.R2_BUCKET.put(fileName, imageData, {
865
+ // httpMetadata: { contentType: 'image/jpeg' },
866
+ // });
867
+ // return `https://your-r2-bucket-url/${fileName}`;
868
+
869
+ // For now, we'll throw an error if this isn't implemented
870
+ throw new Error(`Storage service not implemented for ${label}`);
871
+ };
872
+
819
873
  try {
820
874
  // Validate image URLs
821
875
  await validateImageUrl(sourceImageUrl, 'Source image URL');
822
876
  await validateImageUrl(targetImageUrl, 'Target image URL');
823
-
824
- // Generate resized URLs using Cloudflare Image Transformations
825
- const resizedSourceUrl = getResizedImageUrl(sourceImageUrl, 'Source image');
826
- const resizedTargetUrl = getResizedImageUrl(targetImageUrl, 'Target image');
827
-
877
+
878
+ // Resize images by calling the Express server
879
+ const resizedSourceData = await resizeImage(
880
+ sourceImageUrl,
881
+ 'Source image'
882
+ );
883
+ const resizedTargetData = await resizeImage(
884
+ targetImageUrl,
885
+ 'Target image'
886
+ );
887
+
888
+ // Upload resized images to storage to get public URLs
889
+ const resizedSourceUrl = await uploadImageToStorage(
890
+ resizedSourceData,
891
+ 'Source image'
892
+ );
893
+ const resizedTargetUrl = await uploadImageToStorage(
894
+ resizedTargetData,
895
+ 'Target image'
896
+ );
897
+
828
898
  const body = JSON.stringify({
829
899
  model: 'Qubico/image-toolkit',
830
900
  type: 'face-swap',
@@ -834,28 +904,34 @@ export const Api = (env: BackendBindings) => ({
834
904
  },
835
905
  config: {
836
906
  webhook_config: {
837
- endpoint: 'https://zodic-backend.lucdelbel.workers.dev/api/webhook/faceswap',
907
+ endpoint:
908
+ 'https://zodic-backend.lucdelbel.workers.dev/api/webhook/faceswap',
838
909
  // secret: '',
839
910
  },
840
911
  },
841
912
  });
842
-
843
- console.log('Sending FaceSwap request:', { resizedSourceUrl, resizedTargetUrl });
844
-
913
+
914
+ console.log('Sending FaceSwap request:', {
915
+ resizedSourceUrl,
916
+ resizedTargetUrl,
917
+ });
918
+
845
919
  const response = await fetch(endpoint, {
846
920
  method: 'POST',
847
921
  headers,
848
922
  body,
849
923
  });
850
-
924
+
851
925
  // Log the HTTP status and headers for debugging
852
926
  console.log('PiAPI FaceSwap Response Status:', response.status);
853
- console.log('PiAPI FaceSwap Response Headers:', [...response.headers.entries()]);
854
-
927
+ console.log('PiAPI FaceSwap Response Headers:', [
928
+ ...response.headers.entries(),
929
+ ]);
930
+
855
931
  // Read the response body as text first to inspect it
856
932
  const responseText = await response.text();
857
933
  console.log('PiAPI FaceSwap Raw Response Body:', responseText);
858
-
934
+
859
935
  // Check for the expected 201 Created status
860
936
  if (response.status !== 201) {
861
937
  let errorData;
@@ -865,32 +941,45 @@ export const Api = (env: BackendBindings) => ({
865
941
  errorData = { message: responseText || 'Unknown error' };
866
942
  }
867
943
  console.error('Error from PiAPI FaceSwap:', errorData);
868
- throw new Error(`PiAPI FaceSwap Error: ${response.status} - ${errorData.message || 'Unknown error'}`);
944
+ throw new Error(
945
+ `PiAPI FaceSwap Error: ${response.status} - ${
946
+ errorData.message || 'Unknown error'
947
+ }`
948
+ );
869
949
  }
870
-
950
+
871
951
  // Check if the response body is empty
872
952
  if (!responseText) {
873
953
  console.error('PiAPI FaceSwap response body is empty');
874
954
  throw new Error('PiAPI FaceSwap Error: Empty response body');
875
955
  }
876
-
956
+
877
957
  // Try to parse the response as JSON
878
958
  let data;
879
959
  try {
880
- data = JSON.parse(responseText) as { task_id: string; message: string };
960
+ data = JSON.parse(responseText) as {
961
+ task_id: string;
962
+ message: string;
963
+ };
881
964
  } catch (err) {
882
- console.error('Failed to parse PiAPI FaceSwap response as JSON:', responseText);
965
+ console.error(
966
+ 'Failed to parse PiAPI FaceSwap response as JSON:',
967
+ responseText
968
+ );
883
969
  throw new Error('PiAPI FaceSwap Error: Invalid JSON response');
884
970
  }
885
-
971
+
886
972
  // Validate the response structure
887
973
  if (!data.task_id || !data.message) {
888
- console.error('PiAPI FaceSwap response missing required fields:', data);
974
+ console.error(
975
+ 'PiAPI FaceSwap response missing required fields:',
976
+ data
977
+ );
889
978
  throw new Error('PiAPI FaceSwap Error: Invalid response structure');
890
979
  }
891
-
980
+
892
981
  console.log('FaceSwap task created successfully:', data);
893
-
982
+
894
983
  return {
895
984
  taskId: data.task_id,
896
985
  message: data.message,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zodic/shared",
3
- "version": "0.0.345",
3
+ "version": "0.0.347",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "publishConfig": {