Пряме завантаження зображень на steemimages: робоче рішення

in Ukraine on Steem6 days ago

Пряме завантаження зображень на steemimages: робоче рішення
код_превью.png
Було в мене якось бажання отримати можливісьнапряму завантажувти зображеня на сервер прив'язаний до steemit.com. Тобто той, куди вони завантажуютьс через сайт. А в блокчейн іде лише лінк на них.

Це для особистого використання, давало дві переваги:

  1. Пряме завантаження без зайвих клацань і відкривать сторінок, як із пулікацією допису, коли потрібно для цього авторизуватись іще відкрити декілька сторінок;
  2. Пряма комунікація із сервером навіть тоді коли інтерфейс steemit.com не стабільний.

Що заважало раніше?

Власне, відсутність спеціалізованих технічних знань. Бо на сайті розробників вказаний не найширший набір інструментів. А у мене із тією node.js все якісь нестиковки, то залежності не дотягуються то бібліотеки шось не так роблять, ще й ШІ шось плете не те. Загалом не вистачило наполегливості, а точніше до самої node.js руки й не дійшли. Бо спроби виконувались через cdn запити браузера, аттам ще політка CORS (якась така, що забороняє html сторінкам із локального сховища, оперувати файлами через веб браузер).

Як виникла ідея рішення

Цьому сприяла розробка скрипта для публікації дописів на блокчейні. Який розміщується на сервері і запускається поанувальником. А там ще ьули якісь рішення. І бібліотека Deno, чи чк воно називається. Тож шлях прости, те що працює те й пробується далі. Тому й для зображень саме це й захотілось спробувати. Й воно наче краще й безпечніше ніж node.js.

Безпека ключів

Для цього є як мінімум три шляхи:

  1. Просто окремий акаунт із ключем для завантаження зображень, адже на це не витрачається й не вимагається SteemPower. Принаймні мені так здалось
  2. Створення окремого файла із ключами. З якого вони булуть боатись. Це змінні, як і на сервері, тільки більше ручних маніпуляцій для потрібного результату.
  3. Зашифрований файл із ключами ( не перевірено на практиці). Це доопрацювання другого пункту, де файл, який вілтно читався до цього й будь хто міх побачити вміст, ьеретьй й шифрується і тоді там нічого не зрозуміло, лише правильно ввелений пароль дає доступ на їх використання у скриптові. ШІ каже, що таким чином їх ніхто не зможе розшифрувати, але тому не вірю. Проте це підвищує рівень безпеки.

Успішний досвід

По суті це просто із якоїсь точки зору. Бо ШІ підказує майже все. Не завжди правильно, та в аотрібному руслі. Щодо зручного сворення структури файлів і папок у розділі. Інших органіщаційних моментів. Але не завжди підбирає актуальні й робочі бібліотеки чи методи. Хоча то тестови Джемінай, але ж база свіжа. А може шоб не розганялись люди вайбкодить на халявних ресурсах.

Власне зводитьс все до багаторазових перевірок і правок фалу коду скрипта.

Основні кроки:

  1. Створення файлової структури у потрібній папці, де будуть директорії із зображеннями на вивантаження, та вже опрацюваними. Туди йї перемістить скрипт. Щоб не руцями чистить чи переміщати і було видно, що виконано. Ще там буде створюватись текстовий файл із лінками на зображення. Для зручного вставляння їх у текст достатньо навайбкодить собі html редактор, що в одне віконце вставити лінки, а в іншому повпихать їх в текст із візуальної галереї.
  2. Створення файлу із ключами
  3. Створення файлу із скриптом

По суті вся робота припадає на йорзання скрипта і його тестування. А ще deno має дещо іншу політику на читання й роботу із файлами в системі, що значно безпечніше за node.js (згідно із інформацією ШІ). Тож, само нічого не зробить, а у терміналі додатково запитує дозвіл на читання, запису й доступу до інтернету. Хоча треба зробить, шоб скрипт запускався одим натисненням на ярличок і все, хоч там два-три десятка зображень вони поїдуть в порядку черги, без тих обмежень в 10 зображень на одне завантаження. Проте це не перевірялось, мабуть за раз лише шість штук було завантажено.

По суті мануал робить мені не цікаво, як треба ШІ все пошуршить із порадами, й відповідями в реальному часі, тим паче в мене Лінукс. Тож може додам робочий код для deno.

Цікава новина

У ході комунцікації перевірки, чи не можна на сервер запхнуть які фали інші у контейнері зображень, то він надав відповідь. Тобто повідомлення, що лище jpg , png, jpeg gifта webp. Так вебп формат тепер приймає, але не всі сайти його обробляють належним чином, тож анімується воно лише, якщо відкрити за пряии лінком (залежно від сайту посередника). На steemx.org не шевелилось, лише статично. Може steemit розродиться й буде відображати.

Робочий код із залежностями. Для нього мють бути створені файли із ключами і відповідні розділи у місці його розміщення. Для цього достатньо показать код ШІ і запитать що для нього порібно зробити. А моя основна задача повідомити, що така можливість є. І наче можна портувати на андроїд через AndroidJs, який і так пхає цілу робочу ноду node.js у застосунок. Але то треба розбираться....
Код поміщається у створений порожній файл із розширенням .ts

import { load } from "https://deno.land/std@0.224.0/dotenv/mod.ts";  
import { PrivateKey } from "npm:dsteem";  
import { Buffer } from "node:buffer";  
import { crypto } from "https://deno.land/std@0.224.0/crypto/mod.ts";  
import { move } from "https://deno.land/std@0.224.0/fs/move.ts";  
import { ensureDir } from "https://deno.land/std@0.224.0/fs/ensure_dir.ts";  

// 1. Завантаження ключів (твій робочий метод)  
const env = await load();  
const USERNAME = env["STEEM_USER"];  
const POSTING_KEY = env["STEEM_POSTING_KEY"];  
const LOG_FILE = "./links.txt";  

if (!USERNAME || !POSTING_KEY) {  
    console.error("❌ Помилка: Ключі не знайдено у .env!");  
    Deno.exit(1);  
}  

await ensureDir("./papka"); // Папка для вивантаження (шлях)  
await ensureDir("./mamka"); // Мамка куди перемістяться вивантажені (шлях)  

async function startProcess() {  
    console.log(`--- 🚀 СТАРТ ЗАВАНТАЖЕННЯ: ${new Date().toLocaleString()} ---`);  

    const files = [];  
    for await (const entry of Deno.readDir("./papka")) {  
        if (entry.isFile && /\.(jpg|jpeg|png|gif)$/i.test(entry.name)) {  
            files.push(entry.name);  
        }  
    }  

    if (files.length === 0) {  
        console.log("📂 Папка 'papka' порожня.");  
        return;  
    }  

    const privKey = PrivateKey.fromString(POSTING_KEY);  

    for (const fileName of files) {  
        console.log(`\n📄 Обробка: ${fileName}`);  
        const filePath = `./papka/${fileName}`;  

        try {  
            const fileData = await Deno.readFile(filePath);  

            // КРОК 1: Підпис (Challenge + Image Data)  
            const prefix = Buffer.from("ImageSigningChallenge");  
            const combined = Buffer.concat([prefix, Buffer.from(fileData)]);  
            const hash = await crypto.subtle.digest("SHA-256", combined);  
            const signature = privKey.sign(Buffer.from(hash)).toString();  

            const uploadUrl = `https://steemitimages.com/${USERNAME}/${signature}`;  

            // КРОК 2: РУЧНЕ ФОРМУВАННЯ MULTIPART (Вирішує проблему сервера)  
            const boundary = "---------------------------" + Math.random().toString(16).slice(2);  
            const ext = fileName.split('.').pop()?.toLowerCase();  
            const mimeType = ext === 'png' ? 'image/png' : 'image/jpeg';  

            // Створюємо частини тіла запиту  
            const encoder = new TextEncoder();  
            const header = encoder.encode(  
                `--${boundary}\r\n` +  
                `Content-Disposition: form-data; name="upload_file"; filename="${fileName}"\r\n` +  
                `Content-Type: ${mimeType}\r\n\r\n`  
            );  
            const footer = encoder.encode(`\r\n--${boundary}--\r\n`);  

            // Збираємо все в один масив байтів  
            const body = new Uint8Array(header.length + fileData.length + footer.length);  
            body.set(header);  
            body.set(fileData, header.length);  
            body.set(footer, header.length + fileData.length);  

            console.log(`   📡 Надсилання запиту (Multipart)...`);  

            const response = await fetch(uploadUrl, {  
                method: "POST",  
                headers: {  
                    "Content-Type": `multipart/form-data; boundary=${boundary}`,  
                },  
                body: body,  
            });  

            const responseText = await response.text();  

            if (response.ok) {  
                try {  
                    const result = JSON.parse(responseText);  
                    if (result.url) {  
                        console.log(`   ✅ УСПІШНО!`);  
                        console.log(`   🔗 ЛІНК: ${result.url}`);  

                        // Запис у файл links.txt  
                        await Deno.writeTextFile(LOG_FILE, `${result.url}\n`, { append: true });  

                        // Переміщення в mamka  
                        await move(filePath, `./mamka/${fileName}`, { overwrite: true });  
                        console.log(`   📦 Перенесено в /mamka`);  
                    } else {  
                        console.log(`   ❌ Помилка: Сервер повернув успіх, але без URL.`, result);  
                    }  
                } catch {  
                    console.log(`   ❌ Не вдалося розпізнати відповідь сервера:`, responseText);  
                }  
            } else {  
                console.error(`   ❌ Помилка сервера (${response.status}): ${responseText}`);  
            }  
        } catch (err) {  
            console.error(`   ❌ Критична помилка: ${err.message}`);  
        }  
    }  
}  

await startProcess();  

Після запускається у терміналі командою:

deno run --allow-read --allow-write --allow-net --allow-env script_name.ts  

із_терміналу.png

Coin Marketplace

STEEM 0.06
TRX 0.31
JST 0.060
BTC 66355.93
ETH 1999.97
USDT 1.00
SBD 0.50