Files
Greenlens/server/scripts/temp-carousel-upload.js
2026-07-10 11:29:04 +02:00

112 lines
4.9 KiB
JavaScript

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);
}
});
})();