// server.js - Node.js Backend with Express and Tesseract OCR const express = require('express'); const multer = require('multer'); const Tesseract = require('tesseract.js'); const path = require('path'); const fs = require('fs'); const app = express(); const PORT = process.env.PORT || 7860; // Multer configuration for file uploads const upload = multer({ dest: 'uploads/', limits: { fileSize: 10 * 1024 * 1024 } // 10MB limit }); // Middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static('public')); // Database configuration const DB_FILE = './database.json'; const ADMIN_PASSWORD = 'BTXHZ'; const ADMIN_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZX0.dummytoken'; // Initialize database file if doesn't exist if (!fs.existsSync(DB_FILE)) { fs.writeFileSync(DB_FILE, JSON.stringify([])); console.log('📁 Database file created'); } // Initialize uploads folder if doesn't exist if (!fs.existsSync('uploads')) { fs.mkdirSync('uploads'); console.log('📁 Uploads folder created'); } // Database helper functions const getDB = () => { try { const data = fs.readFileSync(DB_FILE, 'utf8'); return JSON.parse(data); } catch (error) { console.error('Error reading database:', error); return []; } }; const saveDB = (data) => { try { fs.writeFileSync(DB_FILE, JSON.stringify(data, null, 2)); return true; } catch (error) { console.error('Error saving database:', error); return false; } }; // Authentication middleware const authMiddleware = (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ success: false, error: 'Unauthorized - No token provided' }); } const token = authHeader.replace('Bearer ', ''); if (token !== ADMIN_TOKEN) { return res.status(401).json({ success: false, error: 'Unauthorized - Invalid token' }); } next(); }; // ============================================ // HTML ROUTES // ============================================ // Root route - redirect to home app.get('/', (req, res) => { res.redirect('/home'); }); // Home page app.get('/home', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'home.html')); }); // Admin create page app.get('/home/admin/create', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'admin.html')); }); // Verification page app.get('/home/url/:name/:id', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'verify.html')); }); // ============================================ // API ROUTES // ============================================ // Login endpoint app.post('/api/login', (req, res) => { const { password } = req.body; if (!password) { return res.status(400).json({ success: false, error: 'Password is required' }); } if (password === ADMIN_PASSWORD) { res.json({ success: true, token: ADMIN_TOKEN, message: 'Login successful' }); } else { res.status(401).json({ success: false, error: 'Invalid password' }); } }); // Create short link endpoint (protected) app.post('/api/create', authMiddleware, (req, res) => { const { title, redirect, name, verify } = req.body; // Validation if (!title || !redirect || !name) { return res.status(400).json({ success: false, error: 'Title, redirect, and name are required' }); } // Check if name already exists const db = getDB(); const existingLink = db.find(link => link.name === name); if (existingLink) { return res.status(400).json({ success: false, error: 'Short name already exists' }); } // Create new link const id = Date.now().toString(); const newLink = { id, title, redirect, name, verify: verify === true, createdAt: new Date().toISOString() }; db.push(newLink); if (saveDB(db)) { console.log(`✅ New link created: ${name} (ID: ${id})`); res.json({ success: true, link: newLink, message: 'Short link created successfully' }); } else { res.status(500).json({ success: false, error: 'Failed to save link' }); } }); // Get all links endpoint app.get('/api/links', (req, res) => { const db = getDB(); res.json(db); }); // Get specific link endpoint app.get('/api/link/:name/:id', (req, res) => { const { name, id } = req.params; const db = getDB(); const link = db.find(l => l.name === name && l.id === id); if (link) { res.json(link); } else { res.status(404).json({ error: 'Link not found', message: 'The requested short link does not exist' }); } }); // OCR Verification endpoint app.post('/api/verify', upload.single('image'), async (req, res) => { if (!req.file) { return res.status(400).json({ success: false, error: 'No image file uploaded' }); } const filePath = req.file.path; console.log(`🔍 Processing OCR for file: ${req.file.originalname}`); try { // Perform OCR using Tesseract const { data: { text } } = await Tesseract.recognize( filePath, 'eng', { logger: info => { if (info.status === 'recognizing text') { console.log(`OCR Progress: ${Math.round(info.progress * 100)}%`); } } } ); console.log('📝 OCR Text extracted:', text.substring(0, 100) + '...'); // Clean up uploaded file fs.unlinkSync(filePath); // Normalize text for better matching const normalizedText = text.toLowerCase(); // Check for required conditions const hasNotmebotz = normalizedText.includes('notmebotz') || normalizedText.includes('notme botz'); const hasFollowButton = normalizedText.includes('follow channel') || normalizedText.includes('follow') && normalizedText.includes('channel'); const hasPrivacy = normalizedText.includes('added privacy') || normalizedText.includes('this channel has added privacy'); // Verification logic const verified = hasNotmebotz && !hasFollowButton && !hasPrivacy; console.log(`✅ Verification result: ${verified ? 'PASSED' : 'FAILED'}`); console.log(` - Channel: ${hasNotmebotz ? 'NOTMEBOTZ ✓' : 'NOT FOUND ✗'}`); console.log(` - Follow Button: ${hasFollowButton ? 'PRESENT ✗' : 'ABSENT ✓'}`); console.log(` - Privacy Text: ${hasPrivacy ? 'PRESENT ✗' : 'ABSENT ✓'}`); res.json({ success: true, verified, channelName: hasNotmebotz ? 'NOTMEBOTZ' : 'UNKNOWN', hasFollowButton, hasPrivacy, extractedText: text.substring(0, 200) // Return first 200 chars for debugging }); } catch (error) { console.error('❌ OCR Error:', error); // Clean up file if it still exists if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } res.status(500).json({ success: false, error: 'OCR processing failed', message: error.message }); } }); // Delete link endpoint (optional - for admin) app.delete('/api/link/:id', authMiddleware, (req, res) => { const { id } = req.params; const db = getDB(); const index = db.findIndex(link => link.id === id); if (index !== -1) { const deletedLink = db.splice(index, 1)[0]; saveDB(db); console.log(`🗑️ Link deleted: ${deletedLink.name} (ID: ${id})`); res.json({ success: true, message: 'Link deleted successfully', deletedLink }); } else { res.status(404).json({ success: false, error: 'Link not found' }); } }); // Health check endpoint app.get('/api/health', (req, res) => { res.json({ status: 'OK', uptime: process.uptime(), timestamp: new Date().toISOString() }); }); // ============================================ // ERROR HANDLING // ============================================ // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Not Found', message: 'The requested endpoint does not exist', path: req.path }); }); // Global error handler app.use((err, req, res, next) => { console.error('❌ Server Error:', err); res.status(err.status || 500).json({ error: 'Internal Server Error', message: err.message || 'Something went wrong' }); }); // ============================================ // START SERVER // ============================================ app.listen(PORT, () => { console.log(''); console.log('╔════════════════════════════════════════╗'); console.log('║ 🚀 ShortLink OCR Server Running ║'); console.log('╚════════════════════════════════════════╝'); console.log(''); console.log(`🌐 Server: http://localhost:${PORT}`); console.log(`📊 Admin Panel: http://localhost:${PORT}/home/admin/create`); console.log(`🔐 Admin Password: ${ADMIN_PASSWORD}`); console.log(''); console.log('📁 Database:', DB_FILE); console.log('📂 Uploads:', path.join(__dirname, 'uploads')); console.log(''); console.log('Press Ctrl+C to stop the server'); console.log(''); });