const express = require('express'); const path = require('path'); const crypto = require('crypto'); const { nanoid } = require('nanoid'); const app = express(); const PORT = process.env.PORT || 3000; // ─── Middleware ──────────────────────────────────────────────────────────────── app.use(express.json({ limit: '5mb' })); app.use(express.urlencoded({ extended: true })); app.use(express.static(path.join(__dirname, 'public'))); // ─── In-memory URL store ────────────────────────────────────────────────────── const urlStore = new Map(); // ═══════════════════════════════════════════════════════════════════════════════ // API: JSON Formatter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/json/format', (req, res) => { try { const { json, indent } = req.body; const parsed = JSON.parse(json); const formatted = JSON.stringify(parsed, null, indent || 2); res.json({ success: true, result: formatted }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/json/minify', (req, res) => { try { const { json } = req.body; const parsed = JSON.parse(json); const minified = JSON.stringify(parsed); res.json({ success: true, result: minified }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/json/validate', (req, res) => { try { const { json } = req.body; JSON.parse(json); res.json({ success: true, valid: true, message: 'Valid JSON ✓' }); } catch (err) { res.json({ success: true, valid: false, message: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: URL Shortener // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/url/shorten', (req, res) => { try { const { url } = req.body; if (!url || !url.match(/^https?:\/\/.+/)) { return res.json({ success: false, error: 'Please enter a valid URL starting with http:// or https://' }); } const id = nanoid(7); urlStore.set(id, { url, createdAt: new Date(), clicks: 0 }); const shortUrl = `${req.protocol}://${req.get('host')}/s/${id}`; res.json({ success: true, shortUrl, id }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.get('/api/url/stats/:id', (req, res) => { const entry = urlStore.get(req.params.id); if (!entry) return res.json({ success: false, error: 'Short URL not found' }); res.json({ success: true, ...entry, id: req.params.id }); }); app.get('/s/:id', (req, res) => { const entry = urlStore.get(req.params.id); if (!entry) return res.status(404).send('Short URL not found'); entry.clicks++; res.redirect(entry.url); }); // ═══════════════════════════════════════════════════════════════════════════════ // API: YouTube Info // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/youtube/info', async (req, res) => { try { const { url } = req.body; if (!url) return res.json({ success: false, error: 'Please enter a YouTube URL' }); const patterns = [ /youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/, /youtu\.be\/([a-zA-Z0-9_-]{11})/, /youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/, /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/ ]; let videoId = null; for (const p of patterns) { const m = url.match(p); if (m) { videoId = m[1]; break; } } if (!videoId) return res.json({ success: false, error: 'Could not extract video ID from URL' }); const oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}&format=json`; const response = await fetch(oembedUrl); if (!response.ok) return res.json({ success: false, error: 'Video not found or unavailable' }); const data = await response.json(); res.json({ success: true, videoId, title: data.title, author: data.author_name, authorUrl: data.author_url, thumbnail: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, thumbnailHQ: `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`, embedUrl: `https://www.youtube.com/embed/${videoId}`, watchUrl: `https://www.youtube.com/watch?v=${videoId}` }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Hash Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/hash', (req, res) => { try { const { text, algorithm } = req.body; if (!text) return res.json({ success: false, error: 'Please enter some text' }); const algos = ['md5', 'sha1', 'sha256', 'sha512']; if (algorithm && algos.includes(algorithm)) { const hash = crypto.createHash(algorithm).update(text, 'utf8').digest('hex'); return res.json({ success: true, hashes: { [algorithm]: hash } }); } const hashes = {}; for (const a of algos) { hashes[a] = crypto.createHash(a).update(text, 'utf8').digest('hex'); } res.json({ success: true, hashes }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Base64 Encode / Decode // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/base64/encode', (req, res) => { try { const { text } = req.body; res.json({ success: true, result: Buffer.from(text || '', 'utf8').toString('base64') }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/base64/decode', (req, res) => { try { const { text } = req.body; res.json({ success: true, result: Buffer.from(text || '', 'base64').toString('utf8') }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: UUID Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/uuid', (req, res) => { try { const { count } = req.body; const n = Math.min(Math.max(parseInt(count) || 1, 1), 100); const uuids = []; for (let i = 0; i < n; i++) uuids.push(crypto.randomUUID()); res.json({ success: true, uuids }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Password Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/password', (req, res) => { try { const { length, uppercase, lowercase, numbers, symbols, count } = req.body; const len = Math.min(Math.max(parseInt(length) || 16, 4), 128); const n = Math.min(Math.max(parseInt(count) || 1, 1), 20); let chars = ''; if (uppercase !== false) chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; if (lowercase !== false) chars += 'abcdefghijklmnopqrstuvwxyz'; if (numbers !== false) chars += '0123456789'; if (symbols) chars += '!@#$%^&*()_+-=[]{}|;:,.<>?'; if (!chars) chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const passwords = []; for (let i = 0; i < n; i++) { const bytes = crypto.randomBytes(len); let pw = ''; for (let j = 0; j < len; j++) pw += chars[bytes[j] % chars.length]; passwords.push(pw); } res.json({ success: true, passwords }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Lorem Ipsum Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/lorem', (req, res) => { try { const words = 'lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua ut enim ad minim veniam quis nostrud exercitation ullamco laboris nisi aliquip ex ea commodo consequat duis aute irure dolor in reprehenderit voluptate velit esse cillum dolore eu fugiat nulla pariatur excepteur sint occaecat cupidatat non proident sunt culpa qui officia deserunt mollit anim id est laborum'.split(' '); const { paragraphs } = req.body; const n = Math.min(Math.max(parseInt(paragraphs) || 3, 1), 20); const result = []; for (let i = 0; i < n; i++) { const sentenceCount = 4 + Math.floor(Math.random() * 5); const sentences = []; for (let s = 0; s < sentenceCount; s++) { const wordCount = 8 + Math.floor(Math.random() * 12); const sentence = []; for (let w = 0; w < wordCount; w++) sentence.push(words[Math.floor(Math.random() * words.length)]); sentence[0] = sentence[0][0].toUpperCase() + sentence[0].slice(1); sentences.push(sentence.join(' ') + '.'); } result.push(sentences.join(' ')); } res.json({ success: true, result: result.join('\n\n') }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Color Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/color/convert', (req, res) => { try { const { color } = req.body; if (!color) return res.json({ success: false, error: 'No color provided' }); let r, g, b; const hexMatch = color.match(/^#?([0-9a-f]{3,8})$/i); if (hexMatch) { let hex = hexMatch[1]; if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; r = parseInt(hex.slice(0,2), 16); g = parseInt(hex.slice(2,4), 16); b = parseInt(hex.slice(4,6), 16); } const rgbMatch = color.match(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i); if (rgbMatch) { r = +rgbMatch[1]; g = +rgbMatch[2]; b = +rgbMatch[3]; } const hslMatch = color.match(/hsl\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)/i); if (hslMatch) { const h = +hslMatch[1] / 360, s = +hslMatch[2] / 100, l = +hslMatch[3] / 100; if (s === 0) { r = g = b = Math.round(l * 255); } else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1/6) return p + (q - p) * 6 * t; if (t < 1/2) return q; if (t < 2/3) return p + (q - p) * (2/3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = Math.round(hue2rgb(p, q, h + 1/3) * 255); g = Math.round(hue2rgb(p, q, h) * 255); b = Math.round(hue2rgb(p, q, h - 1/3) * 255); } } if (r === undefined) return res.json({ success: false, error: 'Unrecognized format. Use HEX (#ff0000), RGB (rgb(255,0,0)), or HSL (hsl(0,100,50))' }); const rn = r/255, gn = g/255, bn = b/255; const max = Math.max(rn, gn, bn), min = Math.min(rn, gn, bn); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case rn: h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6; break; case gn: h = ((bn - rn) / d + 2) / 6; break; case bn: h = ((rn - gn) / d + 4) / 6; break; } } const hex = '#' + [r, g, b].map(x => x.toString(16).padStart(2, '0')).join(''); res.json({ success: true, hex, rgb: `rgb(${r}, ${g}, ${b})`, hsl: `hsl(${Math.round(h*360)}, ${Math.round(s*100)}%, ${Math.round(l*100)}%)`, r, g, b }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: JWT Decoder // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/jwt/decode', (req, res) => { try { const { token } = req.body; if (!token) return res.json({ success: false, error: 'No token provided' }); const parts = token.split('.'); if (parts.length < 2) return res.json({ success: false, error: 'Invalid JWT format' }); const decodeB64 = s => JSON.parse(Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8')); const header = decodeB64(parts[0]); const payload = decodeB64(parts[1]); let expired = null; if (payload.exp) expired = Date.now() / 1000 > payload.exp; res.json({ success: true, header, payload, expired, signature: parts[2] || null }); } catch (err) { res.json({ success: false, error: 'Invalid JWT: ' + err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Timestamp Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/timestamp', (req, res) => { try { const { value } = req.body; let date; if (!value || value === 'now') date = new Date(); else if (/^\d{10}$/.test(value)) date = new Date(parseInt(value) * 1000); else if (/^\d{13}$/.test(value)) date = new Date(parseInt(value)); else date = new Date(value); if (isNaN(date.getTime())) return res.json({ success: false, error: 'Invalid date/timestamp' }); res.json({ success: true, unix: Math.floor(date.getTime() / 1000), unixMs: date.getTime(), iso: date.toISOString(), utc: date.toUTCString(), local: date.toString(), relative: getRelativeTime(date) }); } catch (err) { res.json({ success: false, error: err.message }); } }); function getRelativeTime(date) { const diff = (Date.now() - date.getTime()) / 1000; const abs = Math.abs(diff); const future = diff < 0; const prefix = future ? 'in ' : ''; const suffix = future ? '' : ' ago'; if (abs < 60) return prefix + Math.round(abs) + 's' + suffix; if (abs < 3600) return prefix + Math.round(abs/60) + 'm' + suffix; if (abs < 86400) return prefix + Math.round(abs/3600) + 'h' + suffix; if (abs < 2592000) return prefix + Math.round(abs/86400) + 'd' + suffix; if (abs < 31536000) return prefix + Math.round(abs/2592000) + 'mo' + suffix; return prefix + Math.round(abs/31536000) + 'y' + suffix; } // ═══════════════════════════════════════════════════════════════════════════════ // API: CSS Minifier // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/css/minify', (req, res) => { try { const { css } = req.body; if (!css) return res.json({ success: false, error: 'No CSS provided' }); const minified = css .replace(/\/\*[\s\S]*?\*\//g, '') .replace(/\s+/g, ' ') .replace(/\s*([{}:;,>~+])\s*/g, '$1') .replace(/;}/g, '}') .trim(); const saved = css.length - minified.length; const pct = css.length > 0 ? Math.round((saved / css.length) * 100) : 0; res.json({ success: true, result: minified, original: css.length, minified: minified.length, saved, percentage: pct }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: HTML Entity Encode / Decode // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/html/encode', (req, res) => { try { const { text } = req.body; const result = (text || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/html/decode', (req, res) => { try { const { text } = req.body; const result = (text || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(///g, '/'); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: String Escape / Unescape // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/escape', (req, res) => { try { const { text } = req.body; const result = JSON.stringify(text || ''); res.json({ success: true, result: result.slice(1, -1) }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/unescape', (req, res) => { try { const { text } = req.body; const result = JSON.parse('"' + (text || '') + '"'); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: IP Lookup // ═══════════════════════════════════════════════════════════════════════════════ app.get('/api/ip', async (req, res) => { try { const response = await fetch('http://ip-api.com/json/?fields=status,message,country,regionName,city,zip,lat,lon,timezone,isp,org,as,query'); const data = await response.json(); res.json({ success: true, ...data }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.get('/api/ip/:ip', async (req, res) => { try { const response = await fetch(`http://ip-api.com/json/${req.params.ip}?fields=status,message,country,regionName,city,zip,lat,lon,timezone,isp,org,as,query`); const data = await response.json(); res.json({ success: true, ...data }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Text Statistics // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/text/stats', (req, res) => { try { const { text } = req.body; const t = text || ''; const words = t.trim() ? t.trim().split(/\s+/) : []; const sentences = t.split(/[.!?]+/).filter(s => s.trim().length > 0); const paragraphs = t.split(/\n\s*\n/).filter(p => p.trim().length > 0); const readingTime = Math.max(1, Math.ceil(words.length / 200)); const freq = {}; for (const ch of t.toLowerCase()) { if (/[a-z]/.test(ch)) freq[ch] = (freq[ch] || 0) + 1; } const topChars = Object.entries(freq).sort((a,b) => b[1]-a[1]).slice(0, 10); res.json({ success: true, characters: t.length, charactersNoSpaces: t.replace(/\s/g, '').length, words: words.length, sentences: sentences.length, paragraphs: paragraphs.length, lines: t.split('\n').length, readingTime: readingTime + ' min', topChars }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Number Base Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/number/convert', (req, res) => { try { const { value, fromBase } = req.body; const base = parseInt(fromBase) || 10; const num = parseInt(value, base); if (isNaN(num)) return res.json({ success: false, error: 'Invalid number for the given base' }); res.json({ success: true, decimal: num.toString(10), binary: num.toString(2), octal: num.toString(8), hex: num.toString(16).toUpperCase() }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Case Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/text/case', (req, res) => { try { const { text } = req.body; const t = text || ''; res.json({ success: true, uppercase: t.toUpperCase(), lowercase: t.toLowerCase(), titleCase: t.replace(/\b\w/g, c => c.toUpperCase()), camelCase: t.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase()), snakeCase: t.toLowerCase().replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, ''), kebabCase: t.toLowerCase().replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, ''), dotCase: t.toLowerCase().replace(/[^a-zA-Z0-9]+/g, '.').replace(/^\.|\.$/g, ''), reversed: t.split('').reverse().join('') }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Cron Expression Parser // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/cron/parse', (req, res) => { try { const { expression } = req.body; if (!expression) return res.json({ success: false, error: 'No cron expression provided' }); const parts = expression.trim().split(/\s+/); if (parts.length < 5 || parts.length > 6) return res.json({ success: false, error: 'Invalid cron expression. Expected 5 fields: minute hour day month weekday' }); const [minute, hour, dom, month, dow] = parts; const fieldNames = { minute, hour, 'day of month': dom, month, 'day of week': dow }; // Build human-readable description const desc = describeCron(minute, hour, dom, month, dow); // Calculate next 5 run times const nextRuns = getNextCronRuns(parts, 5); res.json({ success: true, description: desc, fields: fieldNames, nextRuns }); } catch (err) { res.json({ success: false, error: err.message }); } }); function describeCron(min, hr, dom, mon, dow) { const segments = []; if (min === '*' && hr === '*') segments.push('Every minute'); else if (min.startsWith('*/')) segments.push(`Every ${min.slice(2)} minutes`); else if (hr.startsWith('*/')) { segments.push(`Every ${hr.slice(2)} hours`); if (min !== '0' && min !== '*') segments.push(`at minute ${min}`); } else if (min !== '*' && hr !== '*') { const h = parseInt(hr); const ampm = h >= 12 ? 'PM' : 'AM'; const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h; segments.push(`At ${h12}:${min.padStart(2, '0')} ${ampm}`); } else if (min !== '*') segments.push(`At minute ${min} of every hour`); else if (hr !== '*') segments.push(`Every minute during hour ${hr}`); const dowNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; const monNames = ['','January','February','March','April','May','June','July','August','September','October','November','December']; if (dom !== '*') segments.push(`on day ${dom} of the month`); if (mon !== '*') { const m = parseInt(mon); segments.push(`in ${(m >= 1 && m <= 12) ? monNames[m] : 'month ' + mon}`); } if (dow !== '*') { if (dow.includes('-')) { const [s, e] = dow.split('-').map(Number); segments.push(`on ${dowNames[s] || s} through ${dowNames[e] || e}`); } else { const days = dow.split(',').map(d => dowNames[parseInt(d)] || d).join(', '); segments.push(`on ${days}`); } } return segments.join(', ') || 'Every minute'; } function getNextCronRuns(parts, count) { const [minExpr, hrExpr, domExpr, monExpr, dowExpr] = parts; const runs = []; const now = new Date(); let check = new Date(now.getTime() + 60000); check.setSeconds(0, 0); function matches(val, expr, max) { if (expr === '*') return true; if (expr.startsWith('*/')) return val % parseInt(expr.slice(2)) === 0; if (expr.includes(',')) return expr.split(',').map(Number).includes(val); if (expr.includes('-')) { const [s,e] = expr.split('-').map(Number); return val >= s && val <= e; } return val === parseInt(expr); } let iterations = 0; while (runs.length < count && iterations < 525600) { iterations++; const m = check.getMinutes(), h = check.getHours(), d = check.getDate(), mo = check.getMonth() + 1, w = check.getDay(); if (matches(m, minExpr) && matches(h, hrExpr) && matches(d, domExpr) && matches(mo, monExpr) && matches(w, dowExpr)) { runs.push(check.toISOString().replace('T', ' ').slice(0, 16)); } check = new Date(check.getTime() + 60000); } return runs; } // ═══════════════════════════════════════════════════════════════════════════════ // API: JSON ↔ CSV Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/convert/json-to-csv', (req, res) => { try { const { json } = req.body; if (!json) return res.json({ success: false, error: 'No JSON provided' }); const data = JSON.parse(json); if (!Array.isArray(data) || data.length === 0) return res.json({ success: false, error: 'Input must be a non-empty JSON array of objects' }); const headers = [...new Set(data.flatMap(obj => Object.keys(obj)))]; const escapeCsv = v => { const s = String(v === null || v === undefined ? '' : v); return s.includes(',') || s.includes('"') || s.includes('\n') ? '"' + s.replace(/"/g, '""') + '"' : s; }; const rows = data.map(obj => headers.map(h => escapeCsv(obj[h])).join(',')); const csv = [headers.join(','), ...rows].join('\n'); res.json({ success: true, result: csv, rows: data.length, columns: headers.length }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/convert/csv-to-json', (req, res) => { try { const { csv } = req.body; if (!csv) return res.json({ success: false, error: 'No CSV provided' }); const lines = csv.trim().split('\n'); if (lines.length < 2) return res.json({ success: false, error: 'CSV must have a header row and at least one data row' }); const headers = parseCsvLine(lines[0]); const result = []; for (let i = 1; i < lines.length; i++) { if (!lines[i].trim()) continue; const vals = parseCsvLine(lines[i]); const obj = {}; headers.forEach((h, idx) => { obj[h] = vals[idx] || ''; }); result.push(obj); } res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); function parseCsvLine(line) { const result = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (inQuotes) { if (ch === '"' && line[i + 1] === '"') { current += '"'; i++; } else if (ch === '"') inQuotes = false; else current += ch; } else { if (ch === '"') inQuotes = true; else if (ch === ',') { result.push(current); current = ''; } else current += ch; } } result.push(current); return result; } // ═══════════════════════════════════════════════════════════════════════════════ // API: Text Encoder (ROT13, Binary, Morse, etc.) // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/text/encode', (req, res) => { try { const { text, method } = req.body; if (!text) return res.json({ success: false, error: 'No text provided' }); if (!method) return res.json({ success: false, error: 'No encoding method specified' }); let result; switch (method) { case 'rot13': result = text.replace(/[a-zA-Z]/g, c => { const base = c <= 'Z' ? 65 : 97; return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base); }); break; case 'binary': result = text.split('').map(c => c.charCodeAt(0).toString(2).padStart(8, '0')).join(' '); break; case 'morse': { const morseMap = { 'A':'.-','B':'-...','C':'-.-.','D':'-..','E':'.','F':'..-.','G':'--.','H':'....', 'I':'..','J':'.---','K':'-.-','L':'.-..','M':'--','N':'-.','O':'---','P':'.--.', 'Q':'--.-','R':'.-.','S':'...','T':'-','U':'..-','V':'...-','W':'.--','X':'-..-', 'Y':'-.--','Z':'--..','0':'-----','1':'.----','2':'..---','3':'...--','4':'....-', '5':'.....','6':'-....','7':'--...','8':'---..','9':'----.', ' ':'/' }; result = text.toUpperCase().split('').map(c => morseMap[c] || c).join(' '); break; } case 'reverse': result = text.split('').reverse().join(''); break; case 'leetspeak': { const leetMap = { 'A':'4','E':'3','G':'6','I':'1','O':'0','S':'5','T':'7','B':'8','L':'1' }; result = text.split('').map(c => leetMap[c.toUpperCase()] || c).join(''); break; } case 'upside_down': { const flipMap = { 'a':'ɐ','b':'q','c':'ɔ','d':'p','e':'ǝ','f':'ɟ','g':'ƃ','h':'ɥ','i':'ᴉ','j':'ɾ', 'k':'ʞ','l':'l','m':'ɯ','n':'u','o':'o','p':'d','q':'b','r':'ɹ','s':'s','t':'ʇ', 'u':'n','v':'ʌ','w':'ʍ','x':'x','y':'ʎ','z':'z', 'A':'∀','B':'q','C':'Ɔ','D':'p','E':'Ǝ','F':'Ⅎ','G':'פ','H':'H','I':'I','J':'ſ', 'K':'ʞ','L':'˥','M':'W','N':'N','O':'O','P':'Ԁ','Q':'Q','R':'ɹ','S':'S','T':'⊥', 'U':'∩','V':'Λ','W':'M','X':'X','Y':'⅄','Z':'Z', '1':'Ɩ','2':'ᄅ','3':'Ɛ','4':'ㄣ','5':'ϛ','6':'9','7':'ㄥ','8':'8','9':'6','0':'0', '.':'˙',',':'\'','?':'¿','!':'¡','\'':',','"':'„','(':')',')':'(','{':'}','}':'{', '[':']',']':'[','<':'>','>':'<','&':'⅋','_':'‾' }; result = text.split('').map(c => flipMap[c] || c).reverse().join(''); break; } default: return res.json({ success: false, error: `Unknown encoding method: ${method}` }); } res.json({ success: true, result, method }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: HTTP Status Code Lookup // ═══════════════════════════════════════════════════════════════════════════════ const HTTP_STATUS_CODES = { 100:'Continue',101:'Switching Protocols',102:'Processing',103:'Early Hints', 200:'OK',201:'Created',202:'Accepted',203:'Non-Authoritative Information',204:'No Content', 205:'Reset Content',206:'Partial Content',207:'Multi-Status',208:'Already Reported',226:'IM Used', 300:'Multiple Choices',301:'Moved Permanently',302:'Found',303:'See Other',304:'Not Modified', 307:'Temporary Redirect',308:'Permanent Redirect', 400:'Bad Request',401:'Unauthorized',402:'Payment Required',403:'Forbidden',404:'Not Found', 405:'Method Not Allowed',406:'Not Acceptable',407:'Proxy Authentication Required', 408:'Request Timeout',409:'Conflict',410:'Gone',411:'Length Required',412:'Precondition Failed', 413:'Payload Too Large',414:'URI Too Long',415:'Unsupported Media Type',416:'Range Not Satisfiable', 418:"I'm a Teapot",422:'Unprocessable Entity',425:'Too Early',429:'Too Many Requests', 451:'Unavailable For Legal Reasons', 500:'Internal Server Error',501:'Not Implemented',502:'Bad Gateway',503:'Service Unavailable', 504:'Gateway Timeout',505:'HTTP Version Not Supported',507:'Insufficient Storage', 508:'Loop Detected',511:'Network Authentication Required' }; app.get('/api/http-status', (req, res) => { const entries = Object.entries(HTTP_STATUS_CODES).map(([code, text]) => ({ code: parseInt(code), text, category: code[0] + 'xx' })); res.json({ success: true, statuses: entries }); }); app.get('/api/http-status/:code', (req, res) => { const code = req.params.code; const text = HTTP_STATUS_CODES[code]; if (!text) return res.json({ success: false, error: `Unknown status code: ${code}` }); res.json({ success: true, code: parseInt(code), text, category: code[0] + 'xx' }); }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Placeholder Image Generator // ═══════════════════════════════════════════════════════════════════════════════ app.get('/api/placeholder/:width/:height', (req, res) => { try { const w = Math.min(Math.max(parseInt(req.params.width) || 400, 1), 2000); const h = Math.min(Math.max(parseInt(req.params.height) || 300, 1), 2000); const bg = (req.query.bg || '2a2a3a').replace(/[^0-9a-fA-F]/g, '').slice(0, 6); const fg = (req.query.fg || '8888a0').replace(/[^0-9a-fA-F]/g, '').slice(0, 6); const text = req.query.text || `${w}×${h}`; const fontSize = Math.min(Math.max(parseInt(req.query.fontSize) || Math.min(w, h) / 8, 8), 200); // Generate SVG const svg = ` ${text.replace(/&/g,'&').replace(//g,'>')} `; res.setHeader('Content-Type', 'image/svg+xml'); res.setHeader('Cache-Control', 'public, max-age=86400'); res.send(svg); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: SQL Formatter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/sql/format', (req, res) => { try { const { sql } = req.body; if (!sql) return res.json({ success: false, error: 'No SQL provided' }); const majorKw = ['SELECT','FROM','WHERE','AND','OR','ORDER BY','GROUP BY','HAVING','LIMIT','OFFSET','INSERT INTO','VALUES','UPDATE','SET','DELETE FROM','CREATE TABLE','ALTER TABLE','DROP TABLE','JOIN','INNER JOIN','LEFT JOIN','RIGHT JOIN','FULL JOIN','CROSS JOIN','ON','UNION','UNION ALL','EXCEPT','INTERSECT','CASE','WHEN','THEN','ELSE','END','WITH','AS']; let formatted = sql.replace(/\s+/g, ' ').trim(); for (const kw of majorKw) { const re = new RegExp('\\b' + kw.replace(/ /g, '\\s+') + '\\b', 'gi'); formatted = formatted.replace(re, '\n' + kw.toUpperCase()); } // Indent non-keyword continuation lines const lines = formatted.split('\n').filter(l => l.trim()); const result = lines.map((line, i) => { const trimmed = line.trim(); if (i === 0) return trimmed; const isKw = majorKw.some(kw => trimmed.toUpperCase().startsWith(kw)); return isKw ? trimmed : ' ' + trimmed; }).join('\n'); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/sql/minify', (req, res) => { try { const { sql } = req.body; if (!sql) return res.json({ success: false, error: 'No SQL provided' }); const result = sql.replace(/\s+/g, ' ').replace(/\s*([,;()=<>!])\s*/g, '$1').trim(); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Byte Size Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/bytes/convert', (req, res) => { try { const { value, unit, mode } = req.body; if (value === undefined || value === null) return res.json({ success: false, error: 'No value provided' }); const base = mode === 'si' ? 1000 : 1024; const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; const idx = units.indexOf(unit || 'B'); if (idx === -1) return res.json({ success: false, error: 'Invalid unit. Use B, KB, MB, GB, TB, or PB' }); // Convert to bytes first const bytes = parseFloat(value) * Math.pow(base, idx); const result = {}; for (let i = 0; i < units.length; i++) { const val = bytes / Math.pow(base, i); result[units[i]] = val >= 1 ? parseFloat(val.toPrecision(10)) : parseFloat(val.toExponential(4)); } res.json({ success: true, ...result }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: HMAC Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/hmac', (req, res) => { try { const { message, secret, algorithm } = req.body; if (!message) return res.json({ success: false, error: 'No message provided' }); if (!secret) return res.json({ success: false, error: 'No secret key provided' }); const algo = algorithm || 'sha256'; const supported = ['sha256', 'sha512', 'sha1', 'md5']; if (!supported.includes(algo)) return res.json({ success: false, error: `Unsupported algorithm. Use: ${supported.join(', ')}` }); const hmac = crypto.createHmac(algo, secret).update(message, 'utf8').digest('hex'); res.json({ success: true, hmac, algorithm: algo }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Slug Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/text/slugify', (req, res) => { try { const { text, separator, lowercase } = req.body; if (!text) return res.json({ success: false, error: 'No text provided' }); const sep = separator || '-'; let slug = text.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); // strip diacritics if (lowercase !== false) slug = slug.toLowerCase(); slug = slug .replace(/[^a-zA-Z0-9\s-]/g, '') // remove special chars .replace(/[\s-]+/g, sep) // replace spaces/hyphens with separator .replace(new RegExp(`^\\${sep}+|\\${sep}+$`, 'g'), ''); // trim separator edges res.json({ success: true, result: slug }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: Chmod Calculator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/chmod/calculate', (req, res) => { try { const { numeric, symbolic } = req.body; if (!numeric && !symbolic) return res.json({ success: false, error: 'Provide numeric or symbolic permissions' }); const digitToPerms = d => ({ read: !!(d & 4), write: !!(d & 2), execute: !!(d & 1) }); const permsToDigit = (r, w, x) => (r ? 4 : 0) + (w ? 2 : 0) + (x ? 1 : 0); const permsToStr = p => (p.read ? 'r' : '-') + (p.write ? 'w' : '-') + (p.execute ? 'x' : '-'); let owner, group, others; if (numeric) { const digits = numeric.length === 4 ? numeric.slice(1) : numeric; if (!/^[0-7]{3}$/.test(digits)) return res.json({ success: false, error: 'Invalid numeric permissions (use 3 octal digits, e.g. 755)' }); owner = digitToPerms(parseInt(digits[0])); group = digitToPerms(parseInt(digits[1])); others = digitToPerms(parseInt(digits[2])); } else { const s = symbolic.replace(/^[-dlcbps]/, ''); // strip optional file type prefix if (s.length < 9) return res.json({ success: false, error: 'Invalid symbolic permissions (need 9 characters, e.g. rwxr-xr-x)' }); owner = { read: s[0] === 'r', write: s[1] === 'w', execute: s[2] === 'x' || s[2] === 's' }; group = { read: s[3] === 'r', write: s[4] === 'w', execute: s[5] === 'x' || s[5] === 's' }; others = { read: s[6] === 'r', write: s[7] === 'w', execute: s[8] === 'x' || s[8] === 't' }; } const numStr = '' + permsToDigit(owner.read, owner.write, owner.execute) + permsToDigit(group.read, group.write, group.execute) + permsToDigit(others.read, others.write, others.execute); const symStr = permsToStr(owner) + permsToStr(group) + permsToStr(others); res.json({ success: true, numeric: numStr, symbolic: symStr, owner, group, others }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: ASCII Art Generator // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/ascii/generate', (req, res) => { try { const { text } = req.body; if (!text) return res.json({ success: false, error: 'No text provided' }); const font = { A:[' █ ','█ █','█████','█ █','█ █'],B:['████ ','█ █','████ ','█ █','████ '], C:[' ████','█ ','█ ','█ ',' ████'],D:['████ ','█ █','█ █','█ █','████ '], E:['█████','█ ','████ ','█ ','█████'],F:['█████','█ ','████ ','█ ','█ '], G:[' ████','█ ','█ ██','█ █',' ████'],H:['█ █','█ █','█████','█ █','█ █'], I:['█████',' █ ',' █ ',' █ ','█████'],J:['█████',' █',' █','█ █',' ███ '], K:['█ █','█ █ ','███ ','█ █ ','█ █'],L:['█ ','█ ','█ ','█ ','█████'], M:['█ █','██ ██','█ █ █','█ █','█ █'],N:['█ █','██ █','█ █ █','█ ██','█ █'], O:[' ███ ','█ █','█ █','█ █',' ███ '],P:['████ ','█ █','████ ','█ ','█ '], Q:[' ███ ','█ █','█ █ █','█ █ ',' ██ █'],R:['████ ','█ █','████ ','█ █ ','█ █'], S:[' ████','█ ',' ███ ',' █','████ '],T:['█████',' █ ',' █ ',' █ ',' █ '], U:['█ █','█ █','█ █','█ █',' ███ '],V:['█ █','█ █','█ █',' █ █ ',' █ '], W:['█ █','█ █','█ █ █','██ ██','█ █'],X:['█ █',' █ █ ',' █ ',' █ █ ','█ █'], Y:['█ █',' █ █ ',' █ ',' █ ',' █ '],Z:['█████',' █ ',' █ ',' █ ','█████'], '0':[' ███ ','█ ██','█ █ █','██ █',' ███ '],'1':[' █ ',' ██ ',' █ ',' █ ','█████'], '2':[' ███ ','█ █',' ██ ',' █ ','█████'],'3':['████ ',' █',' ███ ',' █','████ '], '4':['█ █','█ █','█████',' █',' █'],'5':['█████','█ ','████ ',' █','████ '], '6':[' ███ ','█ ','████ ','█ █',' ███ '],'7':['█████',' █ ',' █ ',' █ ',' █ '], '8':[' ███ ','█ █',' ███ ','█ █',' ███ '],'9':[' ███ ','█ █',' ████',' █',' ███ '], '!':[' █ ',' █ ',' █ ',' ',' █ '],'?':[' ███ ','█ █',' ██ ',' ',' █ '], '.':[' ',' ',' ',' ',' █ '],'-':[' ',' ','█████',' ',' '], ' ':[' ',' ',' ',' ',' '] }; const input = text.toUpperCase().slice(0, 30); const lines = [[], [], [], [], []]; for (const ch of input) { const glyph = font[ch] || font[' ']; for (let row = 0; row < 5; row++) { lines[row].push(glyph[row]); } } const result = lines.map(row => row.join(' ')).join('\n'); res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // API: ENV ↔ JSON Converter // ═══════════════════════════════════════════════════════════════════════════════ app.post('/api/convert/env-to-json', (req, res) => { try { const { env } = req.body; if (!env) return res.json({ success: false, error: 'No .env content provided' }); const result = {}; const lines = env.split('\n'); for (const line of lines) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; // skip empty lines and comments const eqIdx = trimmed.indexOf('='); if (eqIdx === -1) continue; const key = trimmed.slice(0, eqIdx).trim(); let val = trimmed.slice(eqIdx + 1).trim(); // strip surrounding quotes if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) { val = val.slice(1, -1); } if (key) result[key] = val; } res.json({ success: true, result }); } catch (err) { res.json({ success: false, error: err.message }); } }); app.post('/api/convert/json-to-env', (req, res) => { try { const { json } = req.body; if (!json) return res.json({ success: false, error: 'No JSON provided' }); const data = typeof json === 'string' ? JSON.parse(json) : json; if (typeof data !== 'object' || Array.isArray(data)) return res.json({ success: false, error: 'JSON must be a flat object' }); const lines = []; for (const [key, val] of Object.entries(data)) { const v = String(val); const needsQuotes = v.includes(' ') || v.includes('=') || v.includes('#'); lines.push(`${key}=${needsQuotes ? '"' + v + '"' : v}`); } res.json({ success: true, result: lines.join('\n'), count: lines.length }); } catch (err) { res.json({ success: false, error: err.message }); } }); // ═══════════════════════════════════════════════════════════════════════════════ // SPA fallback // ═══════════════════════════════════════════════════════════════════════════════ app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // ─── Start ──────────────────────────────────────────────────────────────────── app.listen(PORT, () => { console.log(`\n ⚡ WinnieAPI-v2 running at http://localhost:${PORT}\n`); });