/ app / src / pages / api / facecheck.ts
facecheck.ts
  1  // /pages/api/facecheck.ts
  2  import type { NextApiRequest, NextApiResponse } from 'next';
  3  import FormData from 'form-data';
  4  import { addSocialsToPersonDocument, createPersonDocument } from '@/utils/firebase';
  5  import { getStorage, ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
  6  import axios from 'axios';
  7  
  8  
  9  
 10  const TESTING_MODE = false;
 11  const APITOKEN = process.env.NEXT_PUBLIC_FACECHECK_APITOKEN;
 12  
 13  
 14  
 15  
 16  export const uploadImage = async (imageData: string): Promise<Blob> => {
 17      const storage = getStorage();
 18      const imageRef = ref(storage, `images/${Date.now()}.jpg`);
 19    
 20      // Convert base64 to Blob
 21      const byteCharacters = atob(imageData.split(',')[1]);
 22      const byteNumbers = new Array(byteCharacters.length);
 23      for (let i = 0; i < byteCharacters.length; i++) {
 24        byteNumbers[i] = byteCharacters.charCodeAt(i);
 25      }
 26      const byteArray = new Uint8Array(byteNumbers);
 27      const blob = new Blob([byteArray], {type: 'image/jpeg'});
 28    
 29      const uploadTask = uploadBytesResumable(imageRef, blob);
 30    
 31      return new Promise((resolve, reject) => {
 32        uploadTask.on('state_changed',
 33          (snapshot) => {
 34            // Progress function
 35            const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
 36            console.log('Upload is ' + progress + '% done');
 37          },
 38          (error) => {
 39            // Error function
 40            console.log('Error uploading image: ', error);
 41            reject(error);
 42          },
 43          () => {
 44            // Complete function
 45            getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
 46              console.log('File available at', downloadURL);
 47              resolve(downloadURL as unknown as Blob);
 48              return downloadURL
 49            });
 50          }
 51        );
 52      });
 53    };
 54  
 55  
 56  const search_by_face = async (imageUrl: string): Promise<[string | null, any[] | null]> => {
 57    if (TESTING_MODE) {
 58      console.log('****** TESTING MODE search, results are inaccurate, and queue wait is long, but credits are NOT deducted ******');
 59    }
 60  
 61    const site = 'https://facecheck.id';
 62    const headers = {
 63      accept: 'application/json',
 64      Authorization: APITOKEN,
 65    };
 66    
 67    const url  = await uploadImage(imageUrl)
 68    // @ts-ignore
 69      const responseImage = await axios.get(url, { responseType: 'arraybuffer' });
 70      const imageData = Buffer.from(responseImage.data, 'binary');
 71  
 72  
 73  
 74    let form = new FormData();
 75    form.append('images', imageData, { filename: 'image.jpg' });
 76    form.append('id_search', '');
 77  
 78  let response = await axios.post(site+'/api/upload_pic', form, { headers: {
 79      ...form.getHeaders(),
 80      'accept': 'application/json',
 81      'Authorization': APITOKEN
 82  } });
 83  response = response.data;
 84  // @ts-ignore
 85    if (response.error) {
 86      // @ts-ignore
 87      return [`${response.error} (${response.code})`, null];
 88    }
 89  // @ts-ignore
 90  
 91    const id_search = response.id_search;
 92    // @ts-ignore
 93  
 94    console.log(`${response.message} id_search=${id_search}`);
 95    const json_data = {
 96      id_search: id_search,
 97      with_progress: true,
 98      status_only: false,
 99      demo: TESTING_MODE,
100    };
101  // @ts-ignore
102    while (true) {
103      console.log('waiting for search results')
104      response = await axios.post(site+'/api/search', json_data, { headers: headers });
105      response = response.data;
106      // @ts-ignore
107  
108      if (response?.error) {
109          // @ts-ignore
110  
111        return [`${response.error} (${response?.code})`, null];
112      }// @ts-ignore
113  
114      if (response?.output) {
115          // @ts-ignore
116  
117        return [null, response?.output?.items];
118      }
119      // @ts-ignore
120  
121      console.log(`${response?.message} progress: ${response?.progress}%`);
122      await new Promise(r => setTimeout(r, 1000));
123    }
124  };
125  
126  export default async function handler(
127    req: NextApiRequest,
128    res: NextApiResponse
129  ) {
130    if (req.method === 'POST') {
131      const imageData = req.body.imageData;
132      const personDocID = req.body.personDocID;
133  
134  
135      const [error, urls_images] = await search_by_face(imageData);
136  
137      if (urls_images) {
138        console.log("got some urls");
139        urls_images.sort((a, b) => b.score - a.score);
140        const formatted_urls = urls_images.map(im => {
141  
142          const score = im.score; // 0 to 100 score how well the face is matching found image
143          const url = im.url; // url to webpage where the person was found
144          // const image_base64 = im.base64; // thumbnail image encoded as base64 string
145          return { score, url };
146        });
147        await addSocialsToPersonDocument(formatted_urls, personDocID);
148        res.status(200).json(formatted_urls);
149      } else {
150        res.status(500).json({ error });
151      }
152    } else {
153      res.setHeader('Allow', ['POST']);
154      res.status(405).end(`Method ${req.method} Not Allowed`);
155    }
156  }