const http = require('http'); const fs = require('fs'); const path = require('path'); const ADMIN_KEY = process.env.GREENLENS_ADMIN_KEY || 'greenlens-admin-2024'; const PORT = 5173; const IMAGES = process.argv.slice(2); if (!IMAGES.length) { console.error('Provide image paths'); process.exit(1); } (async () => { const upstream = await import('axios'); const axios = upstream.default || upstream; const server = http.createServer(async (req, res) => { if (req.url === '/api/tiktok/upload/photo' && req.method === 'POST') { const chunks = []; req.on('data', chunk => chunks.push(chunk)); req.on('end', async () => { const raw = Buffer.concat(chunks).toString(); const contentType = req.headers['content-type'] || ''; let payload; if (contentType.includes('multipart/form-data')) { const busboy = (await import('@fastify/busboy')).default || (await import('busboy')).default; } else { try { payload = JSON.parse(raw); } catch { res.writeHead(400, {'content-type':'application/json'}); return res.end(JSON.stringify({'code':'BAD_REQUEST','message':'Invalid JSON'})); } } let files = []; if (Array.isArray(payload?.files)) { files = payload.files || []; } else if (Array.isArray(payload?.photos)) { files = payload.photos.map((photo) => { if (Buffer.isBuffer(photo?.buffer)) { return { buffer: photo.buffer, contentType: photo.mimeType || photo.contentType || 'image/jpeg' }; } if (typeof photo?.url === 'string' && /^https?:\/\//i.test(photo.url)) { return { buffer: Buffer.from(photo.url), contentType: photo.mimeType || photo.contentType || 'image/jpeg', url: photo.url }; } return null; }).filter(Boolean); } try { const { uploadImage } = require('./lib/storage'); const fileInfos = []; for (const file of files) { const buffer = file.buffer || file.data; const mimeType = file.contentType || file.mimeType || 'image/jpeg'; if (!Buffer.isBuffer(buffer)) { res.writeHead(400, {'content-type':'application/json'}); return res.end(JSON.stringify({'code':'BAD_REQUEST','message':'Each file must include a binary buffer.'})); } const uploadRes = await uploadImage(buffer.toString('base64'), mimeType); fileInfos.push({ url: uploadRes.url, mime_type: mimeType }); } console.log('Uploaded URLs:', fileInfos.map(f => f.url)); const { getLiveTiktokAccessToken } = require('./lib/tiktok'); async function tiktokApi(url, options = {}) { const tokens = await getLiveTiktokAccessToken(); const r = await axios({ url, ...options, headers: { ...(options.headers || {}), Authorization: 'Bearer ' + tokens.access_token, 'Content-Type': 'application/json; charset=UTF-8' } }); if (r.status >= 400) throw new Error('TikTok API error: ' + r.status + ' ' + JSON.stringify(r.data)); return r.data; } const initBody = { media_type: 'PHOTO', photo_cover_index: 0, file_paths: fileInfos.map(info => info.url), file_extensions: fileInfos.map(info => (info.mime_type || 'image/jpeg').split('/').pop()), post_mode: 'DRAFT', }; const initResult = await tiktokApi('https://open.tiktokapis.com/v2/post/publish/content/init/', { method: 'POST', data: initBody }); console.log('TikTok init:', JSON.stringify(initResult)); res.writeHead(200, {'content-type':'application/json'}); res.end(JSON.stringify({ publish_id: initResult?.data?.publish_id, status: initResult?.data?.status || 'INITIATED' })); } catch (error) { console.error('Handler error:', error); res.writeHead(error.status || 500, {'content-type':'application/json'}); res.end(JSON.stringify({ code: error.code || 'SERVER_ERROR', message: error.message })); } }); return; } res.writeHead(404, {'content-type':'application/json'}); res.end(JSON.stringify({ error: 'Not found' })); }); server.listen(PORT, '127.0.0.1', async () => { try { const form = new FormData(); for (const file of IMAGES) { form.append('photos', fs.createReadStream(file), { filename: path.basename(file), contentType: 'image/png' }); } const { default: axios } = await import('axios'); const resp = await axios.post('http://127.0.0.1:' + PORT + '/api/tiktok/upload/photo', form, { headers: { ...form.getHeaders(), 'x-admin-key': ADMIN_KEY }, maxBodyLength: Infinity, maxContentLength: Infinity }); console.log('UPLOAD_RESPONSE', JSON.stringify(resp.data)); } finally { server.close(); process.exit(0); } }); })();