Spaces:
Sleeping
Sleeping
| const express = require('express'); | |
| const cors = require('cors'); | |
| const fetch = require('node-fetch'); | |
| const bodyParser = require('body-parser'); | |
| const app = express(); | |
| const PORT = 7860; // Default port for Hugging Face Spaces | |
| // --- CONFIGURATION --- | |
| // These tokens must be set in your Space Settings -> Variables and secrets | |
| const STORAGE_POOLS = [ | |
| { | |
| repo: "abhy60098/MY-STORAGE", | |
| token: process.env.HF_TOKEN_1 | |
| }, | |
| { | |
| repo: "sushmasinharnc/MY-STORAGE", | |
| token: process.env.HF_TOKEN_2 | |
| }, | |
| { | |
| repo: "shuarya2011/MY-STORAGE", | |
| token: process.env.HF_TOKEN_3 | |
| } | |
| ]; | |
| const DB_FILE = "db.json"; | |
| // --- MIDDLEWARE --- | |
| app.use(cors()); // Allows any website to connect | |
| app.use(bodyParser.json({ limit: '50mb' })); // Allows large image uploads | |
| // --- ROUTES --- | |
| // 1. Health Check / Home Page | |
| app.get('/', (req, res) => { | |
| res.send(` | |
| <div style="font-family: sans-serif; text-align: center; margin-top: 50px;"> | |
| <h1>🚀 Distributed Storage API is Online</h1> | |
| <p>Status: <b>Connected to ${STORAGE_POOLS.length} Storage Pools</b></p> | |
| <p>Use <code>/posts</code> (GET) to fetch data and <code>/posts</code> (POST) to upload.</p> | |
| </div> | |
| `); | |
| }); | |
| // 2. GET: Fetch data from all 3 datasets and merge them | |
| app.get('/posts', async (req, res) => { | |
| try { | |
| const allResults = await Promise.all(STORAGE_POOLS.map(async (pool) => { | |
| // We check if the token exists before calling | |
| if (!pool.token) return []; | |
| const url = `https://huggingface.co/api/datasets/${pool.repo}/raw/main/${DB_FILE}`; | |
| const response = await fetch(url, { | |
| headers: { Authorization: `Bearer ${pool.token}` } | |
| }); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| return data.posts || []; | |
| } | |
| return []; // Return empty if file doesn't exist yet | |
| })); | |
| // Flatten the array of arrays and sort by ID (timestamp) descending | |
| const mergedPosts = allResults.flat().sort((a, b) => b.id - a.id); | |
| res.json(mergedPosts); | |
| } catch (err) { | |
| console.error("GET Error:", err); | |
| res.status(500).json({ error: "Failed to fetch posts from pools." }); | |
| } | |
| }); | |
| // 3. POST: Upload image and update database in a random pool | |
| app.post('/posts', async (req, res) => { | |
| try { | |
| const { username, caption, imageBase64, fileName } = req.body; | |
| if (!imageBase64 || !username) { | |
| return res.status(400).json({ error: "Missing required fields (username or image)." }); | |
| } | |
| // Pick a random pool to distribute storage usage | |
| const pool = STORAGE_POOLS[Math.floor(Math.random() * STORAGE_POOLS.length)]; | |
| if (!pool.token) { | |
| return res.status(500).json({ error: "Storage pool token not configured in Secrets." }); | |
| } | |
| const timestamp = Date.now(); | |
| const imagePath = `uploads/${timestamp}-${fileName}`; | |
| // A. Upload the Image file | |
| const uploadImageUrl = `https://huggingface.co/api/datasets/${pool.repo}/upload/main/${imagePath}`; | |
| const imageUploadRes = await fetch(uploadImageUrl, { | |
| method: 'POST', | |
| headers: { | |
| Authorization: `Bearer ${pool.token}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| content: imageBase64, // Expecting raw base64 string | |
| message: `Upload image from ${username}` | |
| }) | |
| }); | |
| if (!imageUploadRes.ok) { | |
| const errorText = await imageUploadRes.text(); | |
| throw new Error(`Image Upload Failed: ${errorText}`); | |
| } | |
| const finalImageUrl = `https://huggingface.co/datasets/${pool.repo}/resolve/main/${imagePath}`; | |
| // B. Fetch existing db.json from the SAME pool | |
| const dbUrl = `https://huggingface.co/api/datasets/${pool.repo}/raw/main/${DB_FILE}`; | |
| const dbRes = await fetch(dbUrl, { | |
| headers: { Authorization: `Bearer ${pool.token}` } | |
| }); | |
| let db = { posts: [] }; | |
| if (dbRes.ok) { | |
| db = await dbRes.json(); | |
| } | |
| // C. Add new post to the beginning of the list | |
| db.posts.unshift({ | |
| id: timestamp, | |
| username, | |
| caption, | |
| imageUrl: finalImageUrl, | |
| pool: pool.repo | |
| }); | |
| // D. Upload the updated db.json back to Hugging Face | |
| const updateDbUrl = `https://huggingface.co/api/datasets/${pool.repo}/upload/main/${DB_FILE}`; | |
| const dbUploadRes = await fetch(updateDbUrl, { | |
| method: 'POST', | |
| headers: { | |
| Authorization: `Bearer ${pool.token}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| content: Buffer.from(JSON.stringify(db, null, 2)).toString('base64'), | |
| message: "Update database JSON" | |
| }) | |
| }); | |
| if (!dbUploadRes.ok) throw new Error("Database update failed."); | |
| res.status(201).json({ success: true, imageUrl: finalImageUrl }); | |
| } catch (err) { | |
| console.error("POST Error:", err); | |
| res.status(500).json({ error: err.message }); | |
| } | |
| }); | |
| // Start the server | |
| app.listen(PORT, () => { | |
| console.log(`Server is running on http://localhost:${PORT}`); | |
| }); |