chatapk / server.js
ibrohm's picture
Upload server.js
0a86239 verified
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
// MongoDB Connection - FIXED URI with correct cluster ID
const MONGO_URI = "mongodb+srv://ibrohm135:mansur5754@cluster0.intw8qq.mongodb.net/chat_app?retryWrites=true&w=majority";
const connectDB = async () => {
try {
await mongoose.connect(MONGO_URI);
console.log('✅ Connected to MongoDB Atlas');
} catch (err) {
console.error('❌ MongoDB Connection Error:', err.message);
console.log('🔄 Retrying connection in 5 seconds...');
setTimeout(connectDB, 5000);
}
};
connectDB();
// --- SCHEMAS ---
// Message Schema (Updated with imageUrl, groupId, isRead)
const MessageSchema = new mongoose.Schema({
text: String,
senderId: String,
senderName: String,
isAi: Boolean,
receiverId: String,
isGroup: Boolean,
groupId: String,
imageUrl: String,
isRead: { type: Boolean, default: false },
timestamp: { type: Date, default: Date.now }
});
const Message = mongoose.model('Message', MessageSchema);
// User Schema (Added username)
const UserSchema = new mongoose.Schema({
uid: { type: String, unique: true, required: true },
email: String,
displayName: String,
username: { type: String, unique: true, sparse: true },
photoURL: String,
lastLogin: { type: Date, default: Date.now }
});
const User = mongoose.model('User', UserSchema);
// Status (Story) Schema
const StatusSchema = new mongoose.Schema({
userId: String,
userName: String,
userImage: String,
imageUrl: String,
type: { type: String, default: 'image' },
caption: String,
color: String,
timestamp: { type: Date, default: Date.now },
expiresAt: { type: Date, default: () => Date.now() + 24 * 60 * 60 * 1000 }
});
const Status = mongoose.model('Status', StatusSchema);
// --- ROUTES ---
app.get('/', (req, res) => {
res.json({
status: 'running',
message: 'Chat App Server - v17 FINAL (Fixed Connection)',
endpoints: {
messages: 'GET/POST /messages',
users: 'POST /users, GET /users/search',
status: 'GET/POST /status'
}
});
});
// Messages
app.get('/messages', async (req, res) => {
try {
const messages = await Message.find().sort({ timestamp: -1 }).limit(100);
res.json(messages);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
app.post('/messages', async (req, res) => {
try {
const { text, senderId, senderName, isAi, receiverId, isGroup, groupId, imageUrl } = req.body;
const newMessage = new Message({
text, senderId, senderName, isAi, receiverId, isGroup, groupId, imageUrl
});
await newMessage.save();
res.json(newMessage);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Get User's Chats (Conversations list with last message and unread count)
app.get('/chats/:userId', async (req, res) => {
try {
const { userId } = req.params;
// Find all messages where user is sender or receiver
const messages = await Message.find({
$or: [{ senderId: userId }, { receiverId: userId }]
}).sort({ timestamp: -1 });
// Group by conversation partner
const chatsMap = new Map();
for (const msg of messages) {
const partnerId = msg.senderId === userId ? msg.receiverId : msg.senderId;
if (!partnerId || partnerId === 'AI') continue;
if (!chatsMap.has(partnerId)) {
// Get partner info
const partner = await User.findOne({ uid: partnerId });
const unreadCount = await Message.countDocuments({
senderId: partnerId,
receiverId: userId,
isRead: false
});
chatsMap.set(partnerId, {
id: partnerId,
name: partner?.displayName || 'Unknown',
image: partner?.photoURL || `https://api.dicebear.com/7.x/initials/png?seed=${partner?.displayName || 'U'}`,
lastMessage: msg.text || (msg.imageUrl ? '📷 Photo' : ''),
time: msg.timestamp,
unread: unreadCount,
isOnline: partner?.lastLogin ? (Date.now() - new Date(partner.lastLogin).getTime() < 300000) : false
});
}
}
res.json(Array.from(chatsMap.values()));
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Mark messages as read
app.post('/messages/read', async (req, res) => {
try {
const { senderId, receiverId } = req.body;
await Message.updateMany(
{ senderId, receiverId, isRead: false },
{ isRead: true }
);
res.json({ success: true });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// User Sync
app.post('/users', async (req, res) => {
try {
const { uid, email, displayName, photoURL, username } = req.body;
const updateData = { email, displayName, photoURL, lastLogin: new Date() };
if (username) updateData.username = username;
const user = await User.findOneAndUpdate(
{ uid },
updateData,
{ new: true, upsert: true }
);
res.json(user);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Search Users
app.get('/users/search', async (req, res) => {
try {
const { q } = req.query;
if (!q) return res.json([]);
const cleanQ = q.startsWith('@') ? q.substring(1) : q;
const users = await User.find({
$or: [
{ displayName: { $regex: cleanQ, $options: 'i' } },
{ email: { $regex: cleanQ, $options: 'i' } },
{ username: { $regex: cleanQ, $options: 'i' } }
]
}).limit(20);
res.json(users);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Status Routes
app.get('/status', async (req, res) => {
try {
const statuses = await Status.find({ expiresAt: { $gt: new Date() } }).sort({ timestamp: -1 });
res.json(statuses);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
app.post('/status', async (req, res) => {
try {
const { userId, userName, userImage, imageUrl, type, caption, color } = req.body;
const newStatus = new Status({
userId, userName, userImage, imageUrl, type, caption, color
});
await newStatus.save();
res.json(newStatus);
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// AI Chat Endpoint (using Hugging Face Inference API with Token)
const HF_TOKEN = process.env.HF_TOKEN || '';
app.post('/ai', async (req, res) => {
try {
const { text } = req.body;
if (!text) {
return res.status(400).json({ error: 'Text is required' });
}
// Use Hugging Face Inference API with authentication
const response = await fetch('https://api-inference.huggingface.co/models/microsoft/DialoGPT-large', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${HF_TOKEN}`
},
body: JSON.stringify({
inputs: text,
parameters: {
max_length: 150,
temperature: 0.7
}
})
});
if (response.ok) {
const data = await response.json();
// DialoGPT returns generated text
let aiText = '';
if (Array.isArray(data) && data[0]?.generated_text) {
aiText = data[0].generated_text;
} else if (data.generated_text) {
aiText = data.generated_text;
} else {
aiText = "Tushundim. Yana nima so'rashni xohlaysiz?";
}
res.json({ text: aiText });
} else {
console.error('HF API Error:', response.status, await response.text());
// Fallback response if API fails
const fallbackResponses = [
"Salom! Men sizga yordam berishga tayyorman. 😊",
"Qiziq savol! Batafsil tushuntirib bera olasizmi?",
"Bu juda yaxshi fikr! Davom eting.",
"Tushunarli. Yana nima so'rashni xohlaysiz?",
"Rahmat! Sizga yana qanday yordam bera olaman?"
];
const randomResponse = fallbackResponses[Math.floor(Math.random() * fallbackResponses.length)];
res.json({ text: randomResponse });
}
} catch (e) {
console.error('AI Error:', e.message);
res.json({ text: "Uzr, texnik xatolik yuz berdi. Keyinroq urinib ko'ring." });
}
});
const PORT = process.env.PORT || 7860;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});