| const express = require('express');
|
| const mongoose = require('mongoose');
|
| const cors = require('cors');
|
|
|
| const app = express();
|
| app.use(cors());
|
| app.use(express.json());
|
|
|
|
|
| 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();
|
|
|
|
|
|
|
|
|
| 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);
|
|
|
|
|
| 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);
|
|
|
|
|
| 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);
|
|
|
|
|
|
|
| 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'
|
| }
|
| });
|
| });
|
|
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| app.get('/chats/:userId', async (req, res) => {
|
| try {
|
| const { userId } = req.params;
|
|
|
|
|
| const messages = await Message.find({
|
| $or: [{ senderId: userId }, { receiverId: userId }]
|
| }).sort({ timestamp: -1 });
|
|
|
|
|
| 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)) {
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| 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 });
|
| }
|
| });
|
|
|
|
|
| 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' });
|
| }
|
|
|
|
|
| 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();
|
|
|
| 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());
|
|
|
| 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}`);
|
| });
|
|
|