File size: 4,470 Bytes
60a7dd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
* Lokasi: features/ghibli-remix.js
* Versi: v1
*/

const axios = require('axios');
const { createDecipheriv } = require('crypto');

async function sendCallback(url, payload) {
  try {
    await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
  } catch (error) {
    console.error('Callback failed:', error.response ? error.response.data : error.message);
  }
}

const ghibai = {
  api: {
    base: 'https://generate-api.ghibli-gpt.net',
    endpoints: {
      generate: '/v1/gpt4o-image/generate',
      task: '/v1/gpt4o-image/record-info',
    },
  },
  headers: {
    accept: '*/*',
    'content-type': 'application/json',
    origin: 'https://ghibli-gpt.net',
    referer: 'https://ghibli-gpt.net/',
    'user-agent': 'NB Android/1.0.0',
    authorization: ''
  },
  state: { token: null },
  security: {
    keyBase64: 'UBsnTxs80g8p4iW72eYyPaDvGZbpzun8K2cnoSSEz1Y',
    ivBase64: 'fG1SBDUyE2IG8kPw',
    ciphertextBase64: '2QpqZCkOD/WMHixMqt46AvhdKRYgy5aUMLXi6D0nOPGuDbH4gbNKDV0ZW/+9w9I=',
    decrypt: async () => {
      if (ghibai.state.token) return ghibai.state.token;
      const buf = k => Buffer.from(ghibai.security[k], 'base64');
      const [key, iv, ciphertext] = ['keyBase64', 'ivBase64', 'ciphertextBase64'].map(buf);
      const decipher = createDecipheriv('aes-256-gcm', key, iv);
      decipher.setAuthTag(ciphertext.slice(-16));
      const decrypted = decipher.update(ciphertext.slice(0, -16), undefined, 'utf8') + decipher.final('utf8');
      ghibai.state.token = decrypted;
      ghibai.headers.authorization = `Bearer ${decrypted}`;
      return decrypted;
    }
  },
  getTask: async (jobDetails) => {
    const { taskId, prompt, jobId, callbackUrl, callbackKey } = jobDetails;
    await ghibai.security.decrypt();

    for (let i = 0; i < 60; i++) {
      try {
        const { data } = await axios.get(`${ghibai.api.base}${ghibai.api.endpoints.task}?taskId=${taskId}`, { headers: ghibai.headers });
        const d = data?.data || {};
        const status = d.status || 'unknown';

        if (status === 'SUCCESS' && d.response?.resultUrls?.length) {
          return await sendCallback(callbackUrl, {
            jobId, callbackKey, status: 'success',
            result: {
              prompt,
              imageUrl: d.response.resultUrls[0],
              thumbnailUrl: d.response.thumbnailUrls?.[0],
            }
          });
        }
        await new Promise(r => setTimeout(r, 3000));
      } catch (err) {
        if (err.response?.status === 429) {
          await new Promise(r => setTimeout(r, 5000));
          continue;
        }
        return await sendCallback(callbackUrl, {
          jobId, callbackKey, status: 'failed',
          result: { error: 'Failed to poll task status.', details: err.message }
        });
      }
    }
    await sendCallback(callbackUrl, {
      jobId, callbackKey, status: 'failed',
      result: { error: 'Task polling timed out after several attempts.' }
    });
  },
  generate: async (jobDetails) => {
    const { imageDataUri, prompt, size } = jobDetails;
    await ghibai.security.decrypt();

    try {
      const { data } = await axios.post(
        `${ghibai.api.base}${ghibai.api.endpoints.generate}`,
        { filesUrl: [''], files: [imageDataUri], prompt, size, nVariants: 1 },
        { headers: ghibai.headers }
      );
      const taskId = data?.data?.taskId;
      if (!taskId) {
        throw new Error('Failed to retrieve Task ID from the generation API.');
      }
      await ghibai.getTask({ ...jobDetails, taskId });
    } catch (err) {
      if (err.response?.status === 429) {
        await new Promise(r => setTimeout(r, 5000));
        return await ghibai.generate(jobDetails);
      }
      await sendCallback(jobDetails.callbackUrl, {
        jobId: jobDetails.jobId, callbackKey: jobDetails.callbackKey, status: 'failed',
        result: { error: 'Failed to initiate image generation.', details: err.message }
      });
    }
  }
};

module.exports = async function(req, res) {
  const { imageDataUri, prompt, size, jobId, callbackUrl, callbackKey } = req.body;

  if (!imageDataUri || !prompt || !jobId || !callbackUrl || !callbackKey) {
    return res.status(400).json({ error: 'Missing required parameters for worker.' });
  }

  res.status(202).json({ message: 'Job accepted by worker and is being processed in the background.' });

  ghibai.generate({ imageDataUri, prompt, size, jobId, callbackUrl, callbackKey });
};