sidmazak commited on
Commit
d82cccb
·
verified ·
1 Parent(s): e58ce31

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +173 -0
main.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import sqlite3
3
+ import uuid
4
+ from datetime import datetime
5
+ from typing import List, Dict
6
+
7
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException
8
+ from fastapi.middleware.cors import CORSMiddleware
9
+ from pydantic import BaseModel
10
+
11
+ # --- Configuration ---
12
+ DB_FILE = "messaging_app.db"
13
+
14
+ # --- Database Setup ---
15
+ def init_db():
16
+ """Initialize the SQLite database safely."""
17
+ try:
18
+ conn = sqlite3.connect(DB_FILE)
19
+ cursor = conn.cursor()
20
+ cursor.execute("""
21
+ CREATE TABLE IF NOT EXISTS messages (
22
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
23
+ room_id TEXT NOT NULL,
24
+ username TEXT NOT NULL,
25
+ content TEXT NOT NULL,
26
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
27
+ )
28
+ """)
29
+ cursor.execute("CREATE INDEX IF NOT EXISTS idx_room_id ON messages(room_id)")
30
+ conn.commit()
31
+ conn.close()
32
+ print("Database initialized successfully.")
33
+ except Exception as e:
34
+ print(f"Error initializing database: {e}")
35
+
36
+ # --- Lifespan Management ---
37
+ # This ensures DB init happens only when the app actually starts,
38
+ # preventing the "Import Error" you saw.
39
+ @contextlib.asynccontextmanager
40
+ async def lifespan(app: FastAPI):
41
+ # Startup
42
+ init_db()
43
+ yield
44
+ # Shutdown (Cleanup code goes here if needed)
45
+
46
+ # --- Pydantic Models ---
47
+ class Message(BaseModel):
48
+ room_id: str
49
+ username: str
50
+ content: str
51
+
52
+ class RoomResponse(BaseModel):
53
+ room_id: str
54
+ link: str
55
+
56
+ # --- Connection Manager ---
57
+ class ConnectionManager:
58
+ def __init__(self):
59
+ self.active_connections: Dict[str, List[WebSocket]] = {}
60
+
61
+ async def connect(self, websocket: WebSocket, room_id: str):
62
+ await websocket.accept()
63
+ if room_id not in self.active_connections:
64
+ self.active_connections[room_id] = []
65
+ self.active_connections[room_id].append(websocket)
66
+
67
+ def disconnect(self, websocket: WebSocket, room_id: str):
68
+ if room_id in self.active_connections:
69
+ self.active_connections[room_id].remove(websocket)
70
+ if not self.active_connections[room_id]:
71
+ del self.active_connections[room_id]
72
+
73
+ async def broadcast(self, message: dict, room_id: str):
74
+ if room_id in self.active_connections:
75
+ for connection in self.active_connections[room_id]:
76
+ try:
77
+ await connection.send_json(message)
78
+ except Exception:
79
+ pass
80
+
81
+ manager = ConnectionManager()
82
+
83
+ # --- FastAPI App Initialization ---
84
+ # Pass the lifespan handler here
85
+ app = FastAPI(title="Room-Based Messaging API", lifespan=lifespan)
86
+
87
+ # --- CORS Middleware ---
88
+ app.add_middleware(
89
+ CORSMiddleware,
90
+ allow_origins=["*"],
91
+ allow_credentials=True,
92
+ allow_methods=["*"],
93
+ allow_headers=["*"],
94
+ )
95
+
96
+ # --- API Endpoints ---
97
+
98
+ @app.get("/")
99
+ async def root():
100
+ return {"status": "online", "message": "Messaging Backend is running"}
101
+
102
+ @app.post("/api/room/create", response_model=RoomResponse)
103
+ async def create_room():
104
+ room_id = str(uuid.uuid4())
105
+ return {"room_id": room_id, "link": f"/room/{room_id}"}
106
+
107
+ @app.get("/api/room/{room_id}/history")
108
+ async def get_room_history(room_id: str):
109
+ conn = sqlite3.connect(DB_FILE)
110
+ conn.row_factory = sqlite3.Row
111
+ cursor = conn.cursor()
112
+ try:
113
+ cursor.execute(
114
+ "SELECT username, content, timestamp FROM messages WHERE room_id = ? ORDER BY timestamp ASC",
115
+ (room_id,)
116
+ )
117
+ rows = cursor.fetchall()
118
+ history = [
119
+ {
120
+ "username": row["username"],
121
+ "content": row["content"],
122
+ "timestamp": row["timestamp"],
123
+ "type": "history"
124
+ }
125
+ for row in rows
126
+ ]
127
+ return {"room_id": room_id, "messages": history}
128
+ except Exception as e:
129
+ raise HTTPException(status_code=500, detail=str(e))
130
+ finally:
131
+ conn.close()
132
+
133
+ # --- WebSocket Endpoint ---
134
+
135
+ @app.websocket("/ws/{room_id}/{username}")
136
+ async def websocket_endpoint(websocket: WebSocket, room_id: str, username: str):
137
+ await manager.connect(websocket, room_id)
138
+
139
+ async def process_message(content: str):
140
+ message_payload = {
141
+ "username": username,
142
+ "content": content,
143
+ "timestamp": datetime.now().isoformat(),
144
+ "type": "live"
145
+ }
146
+ try:
147
+ conn = sqlite3.connect(DB_FILE)
148
+ cursor = conn.cursor()
149
+ cursor.execute(
150
+ "INSERT INTO messages (room_id, username, content) VALUES (?, ?, ?)",
151
+ (room_id, username, content)
152
+ )
153
+ conn.commit()
154
+ conn.close()
155
+ except Exception as e:
156
+ print(f"Database error: {e}")
157
+
158
+ await manager.broadcast(message_payload, room_id)
159
+
160
+ try:
161
+ while True:
162
+ data = await websocket.receive_text()
163
+ await process_message(data)
164
+ except WebSocketDisconnect:
165
+ manager.disconnect(websocket, room_id)
166
+ await manager.broadcast({
167
+ "username": "System",
168
+ "content": f"{username} left the chat",
169
+ "type": "system"
170
+ }, room_id)
171
+ except Exception as e:
172
+ print(f"Error: {e}")
173
+ manager.disconnect(websocket, room_id)