Okay, zatiaľ beží https://app.hrubos.tech/ dobre: https://hrubos.tech/repository/chat-app20.zip Toto obsluhuje chat app pre node.js:
// index.js
const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 3000;
const MAX_MESSAGES = 100;
// Upload directory + JSON file
const uploadDir = path.join(__dirname, "uploads");
const dataFile = path.join(uploadDir, "data.json");
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
if (!fs.existsSync(dataFile)) fs.writeFileSync(dataFile, JSON.stringify([]));
// Multer storage pre obrázky
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, uploadDir),
filename: (req, file, cb) =>
cb(
null,
Date.now() +
"_" +
Math.random().toString(36).substr(2, 9) +
path.extname(file.originalname)
)
});
const upload = multer({
storage,
limits: { fileSize: 5 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowed = /jpeg|jpg|png|gif|webp|bmp/;
const ext = allowed.test(path.extname(file.originalname).toLowerCase());
const mime = allowed.test(file.mimetype);
if (ext && mime) cb(null, true);
else cb(
new Error("Povolené súbory: JPG, PNG, GIF, WebP, BMP (max 5MB)")
);
}
}).single("image");
// Static files
app.use(express.static(path.join(__dirname, "client")));
app.use("/uploads", express.static(uploadDir));
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
// Pomocné funkcie
function readData() {
try { return JSON.parse(fs.readFileSync(dataFile)); }
catch { return []; }
}
function writeData(data) {
fs.writeFileSync(dataFile, JSON.stringify(data, null, 2));
}
// Automatické čistenie po prekročení MAX_MESSAGES
function cleanIfNeeded() {
const data = readData();
if (data.length > MAX_MESSAGES) {
// vymaž všetky súbory okrem data.json
fs.readdirSync(uploadDir).forEach(file => {
if (file !== "data.json") fs.unlinkSync(path.join(uploadDir, file));
});
// vymaž data.json
writeData([]);
console.log("♻️ Vymazané všetky správy a obrázky po prekročení limitu");
}
}
// Endpoint pre textové správy
app.post("/message", (req, res) => {
const msg = {
user: req.body.user || "Anon",
text: req.body.text || "",
image: null,
time: new Date().toLocaleString()
};
const data = readData();
data.push(msg);
// Spočítať zostávajúce správy do premazania
const remaining = MAX_MESSAGES - data.length;
writeData(data);
cleanIfNeeded();
res.json({
status: "ok",
remaining: remaining > 0 ? remaining : 0,
message:
remaining > 0
? `Zostáva ${remaining} správ do premazania`
: "Chat sa práve premazal"
});
});
// Endpoint pre upload obrázka + správa
app.post("/upload", (req, res) => {
upload(req, res, (err) => {
if (err) return res.json({ error: err.message || "Chyba uploadu" });
if (!req.file) return res.json({ error: "Žiadny súbor" });
const msg = {
user: req.body.user || "Anon",
text: req.body.text || "",
image: "/uploads/" + req.file.filename,
time: new Date().toLocaleString()
};
const data = readData();
data.push(msg);
const remaining = MAX_MESSAGES - data.length;
writeData(data);
cleanIfNeeded();
res.json({
url: msg.image,
remaining: remaining > 0 ? remaining : 0,
message:
remaining > 0
? `Zostáva ${remaining} správ do premazania`
: "Chat sa práve premazal"
});
});
});
// Health endpoint
app.get("/health", (req, res) => {
const data = readData();
res.json({ status: "ok", messages: data.length });
});
// Spusti server
app.listen(PORT, () => {
console.log(`🚀 Server beží na http://localhost:${PORT}`);
});
<!DOCTYPE html>
<html lang="sk">
<head>
<meta charset="UTF-8">
<title>Chat App</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { box-sizing:border-box; }
body {
margin:0; padding:0; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
display:flex; flex-direction:column; height:100vh; background: linear-gradient(135deg,#667eea 0%,#764ba2 100%);
}
#messages {
list-style:none; padding:20px; margin:0; flex-grow:1; overflow-y:auto; display:flex; flex-direction:column; gap:12px;
}
#messages li {
max-width:70%; padding:12px 16px; border-radius:18px; background:white; word-wrap:break-word;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
#messages li.me { align-self:flex-end; background:#2196F3; color:white; border-bottom-right-radius:4px; }
#messages li.system { align-self:center; background:#e0e0e0; color:#666; font-size:0.85em; font-style:italic; max-width:unset; }
#controls { display:flex; flex-wrap:wrap; align-items:center; padding:15px; background:rgba(255,255,255,0.95); gap:10px; }
#username,#input { flex:1; min-width:120px; padding:12px 16px; border-radius:25px; border:2px solid #e0e0e0; font-size:16px; }
#send,#fileLabel { padding:12px 20px; border-radius:25px; cursor:pointer; font-weight:500; border:none; }
#send { background:linear-gradient(45deg,#2196F3,#21CBF3); color:white; }
#fileLabel { background:linear-gradient(45deg,#4CAF50,#45a049); color:white; }
#fileInput { display:none; }
#errorMsg { color:#d32f2f; width:100%; text-align:center; font-size:14px; padding:10px; background:#ffebee; border-radius:8px; margin-top:5px; display:none; }
#loading { width:24px; height:24px; border:3px solid #f3f3f3; border-top:3px solid #2196F3; border-radius:50%; animation:spin 1s linear infinite; display:none; margin-left:10px; }
#imagePreview { max-width:80px; max-height:80px; border-radius:12px; box-shadow:0 2px 8px rgba(0,0,0,0.2); margin-left:10px; }
img { max-width:100%; height:auto; border-radius:12px; margin-top:8px; }
@keyframes spin { 0% { transform:rotate(0deg); } 100% { transform:rotate(360deg); } }
@media(max-width:600px){
#controls{flex-direction:column; align-items:stretch; padding:10px;}
#username,#input,#send,#fileLabel{width:100%; margin:5px 0;}
#messages li{max-width:90%;}
#loading,#imagePreview{margin-left:0;}
}
</style>
</head>
<body>
<ul id="messages"></ul>
<div id="controls">
<input id="username" placeholder="Tvoja prezývka" maxlength="20">
<input id="input" placeholder="Napíš správu..." autocomplete="off">
<label id="fileLabel" for="fileInput">🖼️ Obrázok</label>
<input type="file" id="fileInput" accept="image/*">
<button id="send">➤ Pošli</button>
<div id="loading"></div>
<div id="imagePreview"></div>
<div id="errorMsg"></div>
</div>
<script>
const messagesEl = document.getElementById("messages");
const input = document.getElementById("input");
const username = document.getElementById("username");
const send = document.getElementById("send");
const fileInput = document.getElementById("fileInput");
const errorMsg = document.getElementById("errorMsg");
const loading = document.getElementById("loading");
const imagePreview = document.getElementById("imagePreview");
// ENTER support
input.addEventListener("keydown", e => { if(e.key==="Enter") send.click(); });
// Mini náhľad obrázka
fileInput.addEventListener("change", ()=>{
const file = fileInput.files[0];
if(!file) { imagePreview.style.display="none"; return; }
const reader = new FileReader();
reader.onload = e => {
imagePreview.innerHTML = `<img src="${e.target.result}">`;
imagePreview.style.display="block";
};
reader.readAsDataURL(file);
});
// Systémová správa
function addSystemMessage(text){
const li = document.createElement("li");
li.className = "system";
li.textContent = text;
messagesEl.appendChild(li);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
// Zobraz chat správu
function displayMessage(msg){
const li = document.createElement("li");
li.className = (msg.user === (username.value||"Anon")) ? "me" : "";
li.innerHTML = `<strong>${msg.user}:</strong> ${msg.text}`;
if(msg.image){
const img = document.createElement("img");
img.src = msg.image;
img.onerror = ()=>img.alt="Chyba pri načítaní obrázka";
li.appendChild(document.createElement("br"));
li.appendChild(img);
}
messagesEl.appendChild(li);
messagesEl.scrollTop = messagesEl.scrollHeight;
}
// Chyba
function showError(msg){
errorMsg.textContent = msg;
errorMsg.style.display = "block";
setTimeout(()=>{ errorMsg.style.display="none"; },5000);
}
// Načítanie všetkých správ z data.json
async function loadMessages(){
try{
const res = await fetch("/uploads/data.json");
const data = await res.json();
messagesEl.innerHTML="";
data.forEach(displayMessage);
} catch(e){
console.error("Nepodarilo sa načítať správy:",e);
}
}
// Odoslanie správy
send.onclick = async ()=>{
const text = input.value.trim();
const user = username.value.trim() || "Anon";
if(!text && !fileInput.files[0]) return showError("Napíš správu alebo pridaj obrázok");
send.disabled=true;
loading.style.display="block";
try{
if(fileInput.files[0]){
const fd = new FormData();
fd.append("image", fileInput.files[0]);
fd.append("user", user);
fd.append("text", text);
const res = await fetch("/upload",{method:"POST",body:fd});
const data = await res.json();
if(data.error) throw new Error(data.error);
if(data.message) addSystemMessage(data.message);
await loadMessages();
} else {
const res = await fetch("/message",{
method:"POST",
headers:{"Content-Type":"application/json"},
body: JSON.stringify({user,text})
});
const data = await res.json();
if(data.message) addSystemMessage(data.message);
await loadMessages();
}
input.value="";
fileInput.value="";
imagePreview.style.display="none";
} catch(e){ showError(e.message); }
finally{ send.disabled=false; loading.style.display="none"; }
};
// REKURZÍVNY POLLING
async function pollMessages(){
try {
await loadMessages();
} catch(e){
console.error(e);
} finally {
setTimeout(pollMessages,9000);
}
}
pollMessages();
</script>
</body>
</html>

Comments “Dnes zdroják už v prevádzke preverenej pokec appky pre node.js”