zenaight commited on
Commit ·
a758b0c
1
Parent(s): fe33f65
message and session management
Browse files- ai_chat.py +56 -12
- database.py +102 -1
- main.py +18 -4
- supabase_setup.sql +53 -4
ai_chat.py
CHANGED
|
@@ -2,52 +2,96 @@ from langgraph.graph import StateGraph, END
|
|
| 2 |
from langchain_core.runnables import RunnableLambda
|
| 3 |
from typing import TypedDict
|
| 4 |
from config import llm, OPENAI_API_KEY
|
|
|
|
| 5 |
|
| 6 |
-
def
|
| 7 |
-
"""Chat function
|
| 8 |
user_message = state["user_message"]
|
| 9 |
user_info = state.get("user_info", {})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
# Add system message with user context
|
| 12 |
system_message = "You are a helpful and concise assistant."
|
| 13 |
if user_info.get("name") and user_info["name"] != "Unknown":
|
| 14 |
system_message += f" The user's name is {user_info['name']}."
|
| 15 |
|
| 16 |
-
messages
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
try:
|
| 22 |
if not OPENAI_API_KEY:
|
| 23 |
return {"response": "Sorry, AI chat is not available. Please check your OpenAI API key configuration."}
|
| 24 |
|
| 25 |
response = llm.invoke(messages)
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
except Exception as e:
|
| 28 |
-
print(f"Error in
|
| 29 |
return {"response": "Sorry, something went wrong: " + str(e)}
|
| 30 |
|
| 31 |
class ChatState(TypedDict):
|
| 32 |
user_message: str
|
| 33 |
response: str
|
| 34 |
user_info: dict
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# --- Build LangGraph ---
|
| 37 |
graph = StateGraph(ChatState)
|
| 38 |
-
graph.add_node("chat", RunnableLambda(
|
| 39 |
graph.set_entry_point("chat")
|
| 40 |
graph.add_edge("chat", END)
|
| 41 |
chat_graph = graph.compile()
|
| 42 |
|
| 43 |
-
async def process_message(user_message: str, user_info: dict = None):
|
| 44 |
-
"""Process a message through the AI chat system"""
|
| 45 |
if user_info is None:
|
| 46 |
user_info = {}
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
result = await chat_graph.ainvoke({
|
| 49 |
"user_message": user_message,
|
| 50 |
-
"user_info": user_info
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
})
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
return result["response"]
|
|
|
|
| 2 |
from langchain_core.runnables import RunnableLambda
|
| 3 |
from typing import TypedDict
|
| 4 |
from config import llm, OPENAI_API_KEY
|
| 5 |
+
from database import get_session_messages, save_message
|
| 6 |
|
| 7 |
+
def chat_with_session_memory(state):
|
| 8 |
+
"""Chat function with session-based memory"""
|
| 9 |
user_message = state["user_message"]
|
| 10 |
user_info = state.get("user_info", {})
|
| 11 |
+
session_id = state.get("session_id")
|
| 12 |
+
wa_id = state.get("wa_id")
|
| 13 |
+
wamid = state.get("wamid")
|
| 14 |
+
|
| 15 |
+
# Get conversation history from database
|
| 16 |
+
session_messages = []
|
| 17 |
+
if session_id:
|
| 18 |
+
# This will be populated by the async wrapper
|
| 19 |
+
session_messages = state.get("session_messages", [])
|
| 20 |
|
| 21 |
# Add system message with user context
|
| 22 |
system_message = "You are a helpful and concise assistant."
|
| 23 |
if user_info.get("name") and user_info["name"] != "Unknown":
|
| 24 |
system_message += f" The user's name is {user_info['name']}."
|
| 25 |
|
| 26 |
+
# Build messages array with history
|
| 27 |
+
messages = [{"role": "system", "content": system_message}]
|
| 28 |
+
|
| 29 |
+
# Add conversation history (last 10 messages)
|
| 30 |
+
for msg in session_messages[-30:]:
|
| 31 |
+
messages.append({"role": msg["role"], "content": msg["content"]})
|
| 32 |
+
|
| 33 |
+
# Add current user message
|
| 34 |
+
messages.append({"role": "user", "content": user_message})
|
| 35 |
|
| 36 |
try:
|
| 37 |
if not OPENAI_API_KEY:
|
| 38 |
return {"response": "Sorry, AI chat is not available. Please check your OpenAI API key configuration."}
|
| 39 |
|
| 40 |
response = llm.invoke(messages)
|
| 41 |
+
ai_response = response.content
|
| 42 |
+
|
| 43 |
+
# Save messages to database (this will be handled by the async wrapper)
|
| 44 |
+
return {
|
| 45 |
+
"response": ai_response,
|
| 46 |
+
"user_message": user_message,
|
| 47 |
+
"ai_response": ai_response,
|
| 48 |
+
"session_id": session_id,
|
| 49 |
+
"wa_id": wa_id,
|
| 50 |
+
"wamid": wamid
|
| 51 |
+
}
|
| 52 |
except Exception as e:
|
| 53 |
+
print(f"Error in chat_with_session_memory: {e}")
|
| 54 |
return {"response": "Sorry, something went wrong: " + str(e)}
|
| 55 |
|
| 56 |
class ChatState(TypedDict):
|
| 57 |
user_message: str
|
| 58 |
response: str
|
| 59 |
user_info: dict
|
| 60 |
+
session_id: str
|
| 61 |
+
wa_id: str
|
| 62 |
+
wamid: str
|
| 63 |
+
session_messages: list
|
| 64 |
|
| 65 |
# --- Build LangGraph ---
|
| 66 |
graph = StateGraph(ChatState)
|
| 67 |
+
graph.add_node("chat", RunnableLambda(chat_with_session_memory))
|
| 68 |
graph.set_entry_point("chat")
|
| 69 |
graph.add_edge("chat", END)
|
| 70 |
chat_graph = graph.compile()
|
| 71 |
|
| 72 |
+
async def process_message(user_message: str, user_info: dict = None, session_id: str = None, wa_id: str = None, wamid: str = None):
|
| 73 |
+
"""Process a message through the AI chat system with session memory"""
|
| 74 |
if user_info is None:
|
| 75 |
user_info = {}
|
| 76 |
|
| 77 |
+
# Get session messages for context
|
| 78 |
+
session_messages = []
|
| 79 |
+
if session_id:
|
| 80 |
+
session_messages = await get_session_messages(session_id, limit=10)
|
| 81 |
+
|
| 82 |
+
# Process with AI
|
| 83 |
result = await chat_graph.ainvoke({
|
| 84 |
"user_message": user_message,
|
| 85 |
+
"user_info": user_info,
|
| 86 |
+
"session_id": session_id,
|
| 87 |
+
"wa_id": wa_id,
|
| 88 |
+
"wamid": wamid,
|
| 89 |
+
"session_messages": session_messages
|
| 90 |
})
|
| 91 |
|
| 92 |
+
# Save messages to database
|
| 93 |
+
if session_id and wa_id and wamid:
|
| 94 |
+
await save_message(session_id, wa_id, wamid, "user", user_message)
|
| 95 |
+
await save_message(session_id, wa_id, f"{wamid}_ai", "assistant", result["response"])
|
| 96 |
+
|
| 97 |
return result["response"]
|
database.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from datetime import datetime
|
| 2 |
from config import supabase
|
| 3 |
|
| 4 |
async def get_or_create_user(wa_id: str, name: str = None) -> dict:
|
|
@@ -42,6 +42,107 @@ async def update_user_activity(wa_id: str):
|
|
| 42 |
except Exception as e:
|
| 43 |
print(f"Error updating user activity: {e}")
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
async def get_user(wa_id: str):
|
| 46 |
"""Get user by WhatsApp ID"""
|
| 47 |
if not supabase:
|
|
|
|
| 1 |
+
from datetime import datetime, timedelta
|
| 2 |
from config import supabase
|
| 3 |
|
| 4 |
async def get_or_create_user(wa_id: str, name: str = None) -> dict:
|
|
|
|
| 42 |
except Exception as e:
|
| 43 |
print(f"Error updating user activity: {e}")
|
| 44 |
|
| 45 |
+
# --- Session Management ---
|
| 46 |
+
async def get_active_session(wa_id: str) -> dict:
|
| 47 |
+
"""Get the active session for a user (not ended and within 3 hours)"""
|
| 48 |
+
if not supabase:
|
| 49 |
+
return None
|
| 50 |
+
|
| 51 |
+
try:
|
| 52 |
+
# Calculate 3 hours ago
|
| 53 |
+
three_hours_ago = datetime.utcnow() - timedelta(hours=3)
|
| 54 |
+
|
| 55 |
+
response = supabase.table("chat_sessions").select("*").eq("wa_id", wa_id).eq("ended", False).gte("last_activity", three_hours_ago.isoformat()).order("last_activity", desc=True).limit(1).execute()
|
| 56 |
+
|
| 57 |
+
return response.data[0] if response.data else None
|
| 58 |
+
except Exception as e:
|
| 59 |
+
print(f"Error getting active session: {e}")
|
| 60 |
+
return None
|
| 61 |
+
|
| 62 |
+
async def create_new_session(wa_id: str) -> dict:
|
| 63 |
+
"""Create a new chat session for a user"""
|
| 64 |
+
if not supabase:
|
| 65 |
+
return {"id": "local_session", "wa_id": wa_id, "started_at": datetime.utcnow().isoformat(), "last_activity": datetime.utcnow().isoformat(), "ended": False}
|
| 66 |
+
|
| 67 |
+
try:
|
| 68 |
+
new_session = {
|
| 69 |
+
"wa_id": wa_id,
|
| 70 |
+
"started_at": datetime.utcnow().isoformat(),
|
| 71 |
+
"last_activity": datetime.utcnow().isoformat(),
|
| 72 |
+
"ended": False
|
| 73 |
+
}
|
| 74 |
+
response = supabase.table("chat_sessions").insert(new_session).execute()
|
| 75 |
+
return response.data[0] if response.data else new_session
|
| 76 |
+
except Exception as e:
|
| 77 |
+
print(f"Error creating new session: {e}")
|
| 78 |
+
return {"id": "local_session", "wa_id": wa_id, "started_at": datetime.utcnow().isoformat(), "last_activity": datetime.utcnow().isoformat(), "ended": False}
|
| 79 |
+
|
| 80 |
+
async def update_session_activity(session_id: str):
|
| 81 |
+
"""Update session's last activity timestamp"""
|
| 82 |
+
if not supabase or session_id == "local_session":
|
| 83 |
+
return
|
| 84 |
+
|
| 85 |
+
try:
|
| 86 |
+
supabase.table("chat_sessions").update({"last_activity": datetime.utcnow().isoformat()}).eq("id", session_id).execute()
|
| 87 |
+
except Exception as e:
|
| 88 |
+
print(f"Error updating session activity: {e}")
|
| 89 |
+
|
| 90 |
+
async def end_session(session_id: str):
|
| 91 |
+
"""Mark a session as ended"""
|
| 92 |
+
if not supabase or session_id == "local_session":
|
| 93 |
+
return
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
supabase.table("chat_sessions").update({"ended": True}).eq("id", session_id).execute()
|
| 97 |
+
except Exception as e:
|
| 98 |
+
print(f"Error ending session: {e}")
|
| 99 |
+
|
| 100 |
+
# --- Message Management ---
|
| 101 |
+
async def save_message(session_id: str, wa_id: str, wamid: str, role: str, content: str):
|
| 102 |
+
"""Save a message to the database"""
|
| 103 |
+
if not supabase or session_id == "local_session":
|
| 104 |
+
return
|
| 105 |
+
|
| 106 |
+
try:
|
| 107 |
+
message = {
|
| 108 |
+
"session_id": session_id,
|
| 109 |
+
"wa_id": wa_id,
|
| 110 |
+
"wamid": wamid,
|
| 111 |
+
"role": role,
|
| 112 |
+
"content": content,
|
| 113 |
+
"created_at": datetime.utcnow().isoformat()
|
| 114 |
+
}
|
| 115 |
+
supabase.table("messages").insert(message).execute()
|
| 116 |
+
except Exception as e:
|
| 117 |
+
print(f"Error saving message: {e}")
|
| 118 |
+
|
| 119 |
+
async def get_session_messages(session_id: str, limit: int = 10) -> list:
|
| 120 |
+
"""Get recent messages for a session (for memory context)"""
|
| 121 |
+
if not supabase or session_id == "local_session":
|
| 122 |
+
return []
|
| 123 |
+
|
| 124 |
+
try:
|
| 125 |
+
response = supabase.table("messages").select("*").eq("session_id", session_id).order("created_at", desc=True).limit(limit).execute()
|
| 126 |
+
# Reverse to get chronological order
|
| 127 |
+
messages = response.data[::-1] if response.data else []
|
| 128 |
+
return messages
|
| 129 |
+
except Exception as e:
|
| 130 |
+
print(f"Error getting session messages: {e}")
|
| 131 |
+
return []
|
| 132 |
+
|
| 133 |
+
# --- Session Management Helper ---
|
| 134 |
+
async def get_or_create_active_session(wa_id: str) -> dict:
|
| 135 |
+
"""Get active session or create new one if needed"""
|
| 136 |
+
active_session = await get_active_session(wa_id)
|
| 137 |
+
|
| 138 |
+
if active_session:
|
| 139 |
+
# Update activity
|
| 140 |
+
await update_session_activity(active_session["id"])
|
| 141 |
+
return active_session
|
| 142 |
+
else:
|
| 143 |
+
# Create new session
|
| 144 |
+
return await create_new_session(wa_id)
|
| 145 |
+
|
| 146 |
async def get_user(wa_id: str):
|
| 147 |
"""Get user by WhatsApp ID"""
|
| 148 |
if not supabase:
|
main.py
CHANGED
|
@@ -3,7 +3,7 @@ from fastapi.responses import JSONResponse
|
|
| 3 |
|
| 4 |
# Import our modular components
|
| 5 |
from config import supabase
|
| 6 |
-
from database import get_or_create_user, update_user_activity
|
| 7 |
from whatsapp import send_whatsapp_message
|
| 8 |
from ai_chat import process_message
|
| 9 |
from api_routes import router
|
|
@@ -35,6 +35,7 @@ async def receive_message(req: Request):
|
|
| 35 |
|
| 36 |
if msg.get("type") == "text":
|
| 37 |
user_message = msg.get("text", {}).get("body", "")
|
|
|
|
| 38 |
|
| 39 |
# Get or create user in database
|
| 40 |
user_info = await get_or_create_user(wa_id, contact_name)
|
|
@@ -42,13 +43,26 @@ async def receive_message(req: Request):
|
|
| 42 |
# Update user activity
|
| 43 |
await update_user_activity(wa_id)
|
| 44 |
|
| 45 |
-
#
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
# Send response back to WhatsApp
|
| 49 |
await send_whatsapp_message(wa_id, ai_response)
|
| 50 |
|
| 51 |
-
return JSONResponse({
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
except Exception as e:
|
| 54 |
print("Error processing message:", e)
|
|
|
|
| 3 |
|
| 4 |
# Import our modular components
|
| 5 |
from config import supabase
|
| 6 |
+
from database import get_or_create_user, update_user_activity, get_or_create_active_session
|
| 7 |
from whatsapp import send_whatsapp_message
|
| 8 |
from ai_chat import process_message
|
| 9 |
from api_routes import router
|
|
|
|
| 35 |
|
| 36 |
if msg.get("type") == "text":
|
| 37 |
user_message = msg.get("text", {}).get("body", "")
|
| 38 |
+
wamid = msg.get("id") # Meta message ID
|
| 39 |
|
| 40 |
# Get or create user in database
|
| 41 |
user_info = await get_or_create_user(wa_id, contact_name)
|
|
|
|
| 43 |
# Update user activity
|
| 44 |
await update_user_activity(wa_id)
|
| 45 |
|
| 46 |
+
# Get or create active session
|
| 47 |
+
session = await get_or_create_active_session(wa_id)
|
| 48 |
+
|
| 49 |
+
# Process with AI including session memory
|
| 50 |
+
ai_response = await process_message(
|
| 51 |
+
user_message=user_message,
|
| 52 |
+
user_info=user_info,
|
| 53 |
+
session_id=session["id"],
|
| 54 |
+
wa_id=wa_id,
|
| 55 |
+
wamid=wamid
|
| 56 |
+
)
|
| 57 |
|
| 58 |
# Send response back to WhatsApp
|
| 59 |
await send_whatsapp_message(wa_id, ai_response)
|
| 60 |
|
| 61 |
+
return JSONResponse({
|
| 62 |
+
"status": "replied",
|
| 63 |
+
"user_id": wa_id,
|
| 64 |
+
"session_id": session["id"]
|
| 65 |
+
})
|
| 66 |
|
| 67 |
except Exception as e:
|
| 68 |
print("Error processing message:", e)
|
supabase_setup.sql
CHANGED
|
@@ -6,17 +6,52 @@ CREATE TABLE IF NOT EXISTS users (
|
|
| 6 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
| 7 |
);
|
| 8 |
|
| 9 |
-
-- Create
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at DESC);
|
| 11 |
CREATE INDEX IF NOT EXISTS idx_users_updated_at ON users(updated_at DESC);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-- Enable Row Level Security (RLS) - optional but recommended
|
| 14 |
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
-- Create
|
| 17 |
CREATE POLICY "Allow all operations on users" ON users
|
| 18 |
FOR ALL USING (true);
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
-- Optional: Create a function to automatically update the updated_at timestamp
|
| 21 |
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
| 22 |
RETURNS TRIGGER AS $$
|
|
@@ -26,8 +61,22 @@ BEGIN
|
|
| 26 |
END;
|
| 27 |
$$ language 'plpgsql';
|
| 28 |
|
| 29 |
-
-- Create trigger to automatically update updated_at
|
| 30 |
CREATE TRIGGER update_users_updated_at
|
| 31 |
BEFORE UPDATE ON users
|
| 32 |
FOR EACH ROW
|
| 33 |
-
EXECUTE FUNCTION update_updated_at_column();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
| 7 |
);
|
| 8 |
|
| 9 |
+
-- Create chat_sessions table
|
| 10 |
+
CREATE TABLE IF NOT EXISTS chat_sessions (
|
| 11 |
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
| 12 |
+
wa_id TEXT NOT NULL REFERENCES users(wa_id) ON DELETE CASCADE,
|
| 13 |
+
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 14 |
+
last_activity TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 15 |
+
ended BOOLEAN DEFAULT FALSE
|
| 16 |
+
);
|
| 17 |
+
|
| 18 |
+
-- Create messages table
|
| 19 |
+
CREATE TABLE IF NOT EXISTS messages (
|
| 20 |
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
| 21 |
+
session_id UUID NOT NULL REFERENCES chat_sessions(id) ON DELETE CASCADE,
|
| 22 |
+
wa_id TEXT NOT NULL REFERENCES users(wa_id) ON DELETE CASCADE,
|
| 23 |
+
wamid TEXT NOT NULL, -- Meta message ID
|
| 24 |
+
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
| 25 |
+
content TEXT NOT NULL,
|
| 26 |
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
| 27 |
+
);
|
| 28 |
+
|
| 29 |
+
-- Create indexes for better query performance
|
| 30 |
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at DESC);
|
| 31 |
CREATE INDEX IF NOT EXISTS idx_users_updated_at ON users(updated_at DESC);
|
| 32 |
+
CREATE INDEX IF NOT EXISTS idx_chat_sessions_wa_id ON chat_sessions(wa_id);
|
| 33 |
+
CREATE INDEX IF NOT EXISTS idx_chat_sessions_last_activity ON chat_sessions(last_activity DESC);
|
| 34 |
+
CREATE INDEX IF NOT EXISTS idx_chat_sessions_ended ON chat_sessions(ended);
|
| 35 |
+
CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages(session_id);
|
| 36 |
+
CREATE INDEX IF NOT EXISTS idx_messages_wa_id ON messages(wa_id);
|
| 37 |
+
CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC);
|
| 38 |
+
CREATE INDEX IF NOT EXISTS idx_messages_wamid ON messages(wamid);
|
| 39 |
|
| 40 |
-- Enable Row Level Security (RLS) - optional but recommended
|
| 41 |
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
| 42 |
+
ALTER TABLE chat_sessions ENABLE ROW LEVEL SECURITY;
|
| 43 |
+
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
| 44 |
|
| 45 |
+
-- Create policies to allow all operations (you can restrict this based on your needs)
|
| 46 |
CREATE POLICY "Allow all operations on users" ON users
|
| 47 |
FOR ALL USING (true);
|
| 48 |
|
| 49 |
+
CREATE POLICY "Allow all operations on chat_sessions" ON chat_sessions
|
| 50 |
+
FOR ALL USING (true);
|
| 51 |
+
|
| 52 |
+
CREATE POLICY "Allow all operations on messages" ON messages
|
| 53 |
+
FOR ALL USING (true);
|
| 54 |
+
|
| 55 |
-- Optional: Create a function to automatically update the updated_at timestamp
|
| 56 |
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
| 57 |
RETURNS TRIGGER AS $$
|
|
|
|
| 61 |
END;
|
| 62 |
$$ language 'plpgsql';
|
| 63 |
|
| 64 |
+
-- Create trigger to automatically update updated_at for users
|
| 65 |
CREATE TRIGGER update_users_updated_at
|
| 66 |
BEFORE UPDATE ON users
|
| 67 |
FOR EACH ROW
|
| 68 |
+
EXECUTE FUNCTION update_updated_at_column();
|
| 69 |
+
|
| 70 |
+
-- Create trigger to automatically update last_activity for chat_sessions
|
| 71 |
+
CREATE OR REPLACE FUNCTION update_last_activity_column()
|
| 72 |
+
RETURNS TRIGGER AS $$
|
| 73 |
+
BEGIN
|
| 74 |
+
NEW.last_activity = NOW();
|
| 75 |
+
RETURN NEW;
|
| 76 |
+
END;
|
| 77 |
+
$$ language 'plpgsql';
|
| 78 |
+
|
| 79 |
+
CREATE TRIGGER update_chat_sessions_last_activity
|
| 80 |
+
BEFORE UPDATE ON chat_sessions
|
| 81 |
+
FOR EACH ROW
|
| 82 |
+
EXECUTE FUNCTION update_last_activity_column();
|