channels.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. import json
  2. import logging
  3. from typing import Optional
  4. from fastapi import APIRouter, Depends, HTTPException, Request, status, BackgroundTasks
  5. from pydantic import BaseModel
  6. from open_webui.socket.main import sio, get_user_ids_from_room
  7. from open_webui.models.users import Users, UserNameResponse
  8. from open_webui.models.channels import Channels, ChannelModel, ChannelForm
  9. from open_webui.models.messages import (
  10. Messages,
  11. MessageModel,
  12. MessageResponse,
  13. MessageForm,
  14. )
  15. from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
  16. from open_webui.constants import ERROR_MESSAGES
  17. from open_webui.env import SRC_LOG_LEVELS
  18. from open_webui.utils.auth import get_admin_user, get_verified_user
  19. from open_webui.utils.access_control import has_access, get_users_with_access
  20. from open_webui.utils.webhook import post_webhook
  21. log = logging.getLogger(__name__)
  22. log.setLevel(SRC_LOG_LEVELS["MODELS"])
  23. router = APIRouter()
  24. ############################
  25. # GetChatList
  26. ############################
  27. @router.get("/", response_model=list[ChannelModel])
  28. async def get_channels(user=Depends(get_verified_user)):
  29. if user.role == "admin":
  30. return Channels.get_channels()
  31. else:
  32. return Channels.get_channels_by_user_id(user.id)
  33. ############################
  34. # CreateNewChannel
  35. ############################
  36. @router.post("/create", response_model=Optional[ChannelModel])
  37. async def create_new_channel(form_data: ChannelForm, user=Depends(get_admin_user)):
  38. try:
  39. channel = Channels.insert_new_channel(None, form_data, user.id)
  40. return ChannelModel(**channel.model_dump())
  41. except Exception as e:
  42. log.exception(e)
  43. raise HTTPException(
  44. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  45. )
  46. ############################
  47. # GetChannelById
  48. ############################
  49. @router.get("/{id}", response_model=Optional[ChannelModel])
  50. async def get_channel_by_id(id: str, user=Depends(get_verified_user)):
  51. channel = Channels.get_channel_by_id(id)
  52. if not channel:
  53. raise HTTPException(
  54. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  55. )
  56. if user.role != "admin" and not has_access(
  57. user.id, type="read", access_control=channel.access_control
  58. ):
  59. raise HTTPException(
  60. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  61. )
  62. return ChannelModel(**channel.model_dump())
  63. ############################
  64. # UpdateChannelById
  65. ############################
  66. @router.post("/{id}/update", response_model=Optional[ChannelModel])
  67. async def update_channel_by_id(
  68. id: str, form_data: ChannelForm, user=Depends(get_admin_user)
  69. ):
  70. channel = Channels.get_channel_by_id(id)
  71. if not channel:
  72. raise HTTPException(
  73. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  74. )
  75. try:
  76. channel = Channels.update_channel_by_id(id, form_data)
  77. return ChannelModel(**channel.model_dump())
  78. except Exception as e:
  79. log.exception(e)
  80. raise HTTPException(
  81. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  82. )
  83. ############################
  84. # DeleteChannelById
  85. ############################
  86. @router.delete("/{id}/delete", response_model=bool)
  87. async def delete_channel_by_id(id: str, user=Depends(get_admin_user)):
  88. channel = Channels.get_channel_by_id(id)
  89. if not channel:
  90. raise HTTPException(
  91. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  92. )
  93. try:
  94. Channels.delete_channel_by_id(id)
  95. return True
  96. except Exception as e:
  97. log.exception(e)
  98. raise HTTPException(
  99. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  100. )
  101. ############################
  102. # GetChannelMessages
  103. ############################
  104. class MessageUserResponse(MessageResponse):
  105. user: UserNameResponse
  106. @router.get("/{id}/messages", response_model=list[MessageUserResponse])
  107. async def get_channel_messages(
  108. id: str, skip: int = 0, limit: int = 50, user=Depends(get_verified_user)
  109. ):
  110. channel = Channels.get_channel_by_id(id)
  111. if not channel:
  112. raise HTTPException(
  113. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  114. )
  115. if user.role != "admin" and not has_access(
  116. user.id, type="read", access_control=channel.access_control
  117. ):
  118. raise HTTPException(
  119. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  120. )
  121. message_list = Messages.get_messages_by_channel_id(id, skip, limit)
  122. users = {}
  123. messages = []
  124. for message in message_list:
  125. if message.user_id not in users:
  126. user = Users.get_user_by_id(message.user_id)
  127. users[message.user_id] = user
  128. messages.append(
  129. MessageUserResponse(
  130. **{
  131. **message.model_dump(),
  132. "reactions": Messages.get_reactions_by_message_id(message.id),
  133. "user": UserNameResponse(**users[message.user_id].model_dump()),
  134. }
  135. )
  136. )
  137. return messages
  138. ############################
  139. # PostNewMessage
  140. ############################
  141. async def send_notification(webui_url, channel, message, active_user_ids):
  142. users = get_users_with_access("read", channel.access_control)
  143. for user in users:
  144. if user.id in active_user_ids:
  145. continue
  146. else:
  147. if user.settings:
  148. webhook_url = user.settings.ui.get("notifications", {}).get(
  149. "webhook_url", None
  150. )
  151. if webhook_url:
  152. post_webhook(
  153. webhook_url,
  154. f"#{channel.name} - {webui_url}/channels/{channel.id}\n\n{message.content}",
  155. {
  156. "action": "channel",
  157. "message": message.content,
  158. "title": channel.name,
  159. "url": f"{webui_url}/channels/{channel.id}",
  160. },
  161. )
  162. @router.post("/{id}/messages/post", response_model=Optional[MessageModel])
  163. async def post_new_message(
  164. request: Request,
  165. id: str,
  166. form_data: MessageForm,
  167. background_tasks: BackgroundTasks,
  168. user=Depends(get_verified_user),
  169. ):
  170. channel = Channels.get_channel_by_id(id)
  171. if not channel:
  172. raise HTTPException(
  173. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  174. )
  175. if user.role != "admin" and not has_access(
  176. user.id, type="read", access_control=channel.access_control
  177. ):
  178. raise HTTPException(
  179. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  180. )
  181. try:
  182. message = Messages.insert_new_message(form_data, channel.id, user.id)
  183. if message:
  184. event_data = {
  185. "channel_id": channel.id,
  186. "message_id": message.id,
  187. "data": {
  188. "type": "message",
  189. "data": {
  190. **message.model_dump(),
  191. "user": UserNameResponse(**user.model_dump()).model_dump(),
  192. },
  193. },
  194. "user": UserNameResponse(**user.model_dump()).model_dump(),
  195. "channel": channel.model_dump(),
  196. }
  197. await sio.emit(
  198. "channel-events",
  199. event_data,
  200. to=f"channel:{channel.id}",
  201. )
  202. active_user_ids = get_user_ids_from_room(f"channel:{channel.id}")
  203. background_tasks.add_task(
  204. send_notification,
  205. request.app.state.config.WEBUI_URL,
  206. channel,
  207. message,
  208. active_user_ids,
  209. )
  210. return MessageModel(**message.model_dump())
  211. except Exception as e:
  212. log.exception(e)
  213. raise HTTPException(
  214. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  215. )
  216. ############################
  217. # UpdateMessageById
  218. ############################
  219. @router.post(
  220. "/{id}/messages/{message_id}/update", response_model=Optional[MessageModel]
  221. )
  222. async def update_message_by_id(
  223. id: str, message_id: str, form_data: MessageForm, user=Depends(get_verified_user)
  224. ):
  225. channel = Channels.get_channel_by_id(id)
  226. if not channel:
  227. raise HTTPException(
  228. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  229. )
  230. if user.role != "admin" and not has_access(
  231. user.id, type="read", access_control=channel.access_control
  232. ):
  233. raise HTTPException(
  234. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  235. )
  236. message = Messages.get_message_by_id(message_id)
  237. if not message:
  238. raise HTTPException(
  239. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  240. )
  241. if message.channel_id != id:
  242. raise HTTPException(
  243. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  244. )
  245. try:
  246. message = Messages.update_message_by_id(message_id, form_data)
  247. if message:
  248. await sio.emit(
  249. "channel-events",
  250. {
  251. "channel_id": channel.id,
  252. "message_id": message.id,
  253. "data": {
  254. "type": "message:update",
  255. "data": {
  256. **message.model_dump(),
  257. "user": UserNameResponse(**user.model_dump()).model_dump(),
  258. },
  259. },
  260. "user": UserNameResponse(**user.model_dump()).model_dump(),
  261. "channel": channel.model_dump(),
  262. },
  263. to=f"channel:{channel.id}",
  264. )
  265. return MessageModel(**message.model_dump())
  266. except Exception as e:
  267. log.exception(e)
  268. raise HTTPException(
  269. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  270. )
  271. ############################
  272. # AddReactionToMessage
  273. ############################
  274. class ReactionForm(BaseModel):
  275. name: str
  276. @router.post("/{id}/messages/{message_id}/reactions/add", response_model=bool)
  277. async def add_reaction_to_message(
  278. id: str, message_id: str, form_data: ReactionForm, user=Depends(get_verified_user)
  279. ):
  280. channel = Channels.get_channel_by_id(id)
  281. if not channel:
  282. raise HTTPException(
  283. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  284. )
  285. if user.role != "admin" and not has_access(
  286. user.id, type="read", access_control=channel.access_control
  287. ):
  288. raise HTTPException(
  289. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  290. )
  291. message = Messages.get_message_by_id(message_id)
  292. if not message:
  293. raise HTTPException(
  294. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  295. )
  296. if message.channel_id != id:
  297. raise HTTPException(
  298. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  299. )
  300. try:
  301. Messages.add_reaction_to_message(message_id, user.id, form_data.name)
  302. message = Messages.get_message_by_id(message_id)
  303. await sio.emit(
  304. "channel-events",
  305. {
  306. "channel_id": channel.id,
  307. "message_id": message.id,
  308. "data": {
  309. "type": "message:reaction",
  310. "data": {
  311. **message.model_dump(),
  312. "user": UserNameResponse(**user.model_dump()).model_dump(),
  313. "name": form_data.name,
  314. },
  315. },
  316. "user": UserNameResponse(**user.model_dump()).model_dump(),
  317. "channel": channel.model_dump(),
  318. },
  319. to=f"channel:{channel.id}",
  320. )
  321. return True
  322. except Exception as e:
  323. log.exception(e)
  324. raise HTTPException(
  325. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  326. )
  327. ############################
  328. # RemoveReactionById
  329. ############################
  330. @router.post("/{id}/messages/{message_id}/reactions/remove", response_model=bool)
  331. async def remove_reaction_by_id_and_user_id_and_name(
  332. id: str, message_id: str, form_data: ReactionForm, user=Depends(get_verified_user)
  333. ):
  334. channel = Channels.get_channel_by_id(id)
  335. if not channel:
  336. raise HTTPException(
  337. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  338. )
  339. if user.role != "admin" and not has_access(
  340. user.id, type="read", access_control=channel.access_control
  341. ):
  342. raise HTTPException(
  343. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  344. )
  345. message = Messages.get_message_by_id(message_id)
  346. if not message:
  347. raise HTTPException(
  348. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  349. )
  350. if message.channel_id != id:
  351. raise HTTPException(
  352. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  353. )
  354. try:
  355. Messages.remove_reaction_by_id_and_user_id_and_name(
  356. message_id, user.id, form_data.name
  357. )
  358. message = Messages.get_message_by_id(message_id)
  359. await sio.emit(
  360. "channel-events",
  361. {
  362. "channel_id": channel.id,
  363. "message_id": message.id,
  364. "data": {
  365. "type": "message:reaction",
  366. "data": {
  367. **message.model_dump(),
  368. "user": UserNameResponse(**user.model_dump()).model_dump(),
  369. "name": form_data.name,
  370. },
  371. },
  372. "user": UserNameResponse(**user.model_dump()).model_dump(),
  373. "channel": channel.model_dump(),
  374. },
  375. to=f"channel:{channel.id}",
  376. )
  377. return True
  378. except Exception as e:
  379. log.exception(e)
  380. raise HTTPException(
  381. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  382. )
  383. ############################
  384. # DeleteMessageById
  385. ############################
  386. @router.delete("/{id}/messages/{message_id}/delete", response_model=bool)
  387. async def delete_message_by_id(
  388. id: str, message_id: str, user=Depends(get_verified_user)
  389. ):
  390. channel = Channels.get_channel_by_id(id)
  391. if not channel:
  392. raise HTTPException(
  393. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  394. )
  395. if user.role != "admin" and not has_access(
  396. user.id, type="read", access_control=channel.access_control
  397. ):
  398. raise HTTPException(
  399. status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
  400. )
  401. message = Messages.get_message_by_id(message_id)
  402. if not message:
  403. raise HTTPException(
  404. status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
  405. )
  406. if message.channel_id != id:
  407. raise HTTPException(
  408. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  409. )
  410. try:
  411. Messages.delete_message_by_id(message_id)
  412. await sio.emit(
  413. "channel-events",
  414. {
  415. "channel_id": channel.id,
  416. "message_id": message.id,
  417. "data": {
  418. "type": "message:delete",
  419. "data": {
  420. **message.model_dump(),
  421. "user": UserNameResponse(**user.model_dump()).model_dump(),
  422. },
  423. },
  424. "user": UserNameResponse(**user.model_dump()).model_dump(),
  425. "channel": channel.model_dump(),
  426. },
  427. to=f"channel:{channel.id}",
  428. )
  429. return True
  430. except Exception as e:
  431. log.exception(e)
  432. raise HTTPException(
  433. status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
  434. )