forked from open-webui/open-webui
		
	Merge branch 'dev' into feature/support_auth_by_api_key
This commit is contained in:
		
						commit
						56369fea3a
					
				
					 35 changed files with 1231 additions and 325 deletions
				
			
		|  | @ -1,4 +1,5 @@ | |||
| from peewee import * | ||||
| from peewee_migrate import Router | ||||
| from config import SRC_LOG_LEVELS, DATA_DIR | ||||
| import os | ||||
| import logging | ||||
|  | @ -16,4 +17,6 @@ else: | |||
| 
 | ||||
| 
 | ||||
| DB = SqliteDatabase(f"{DATA_DIR}/webui.db") | ||||
| DB.connect() | ||||
| router = Router(DB, migrate_dir="apps/web/internal/migrations", logger=log) | ||||
| router.run() | ||||
| DB.connect(reuse_if_open=True) | ||||
|  |  | |||
							
								
								
									
										149
									
								
								backend/apps/web/internal/migrations/001_initial_schema.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								backend/apps/web/internal/migrations/001_initial_schema.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,149 @@ | |||
| """Peewee migrations -- 001_initial_schema.py. | ||||
| 
 | ||||
| Some examples (model - class or model name):: | ||||
| 
 | ||||
|     > Model = migrator.orm['table_name']            # Return model in current state by name | ||||
|     > Model = migrator.ModelClass                   # Return model in current state by name | ||||
| 
 | ||||
|     > migrator.sql(sql)                             # Run custom SQL | ||||
|     > migrator.run(func, *args, **kwargs)           # Run python function with the given args | ||||
|     > migrator.create_model(Model)                  # Create a model (could be used as decorator) | ||||
|     > migrator.remove_model(model, cascade=True)    # Remove a model | ||||
|     > migrator.add_fields(model, **fields)          # Add fields to a model | ||||
|     > migrator.change_fields(model, **fields)       # Change fields | ||||
|     > migrator.remove_fields(model, *field_names, cascade=True) | ||||
|     > migrator.rename_field(model, old_field_name, new_field_name) | ||||
|     > migrator.rename_table(model, new_table_name) | ||||
|     > migrator.add_index(model, *col_names, unique=False) | ||||
|     > migrator.add_not_null(model, *field_names) | ||||
|     > migrator.add_default(model, field_name, default) | ||||
|     > migrator.add_constraint(model, name, sql) | ||||
|     > migrator.drop_index(model, *col_names) | ||||
|     > migrator.drop_not_null(model, *field_names) | ||||
|     > migrator.drop_constraints(model, *constraints) | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from contextlib import suppress | ||||
| 
 | ||||
| import peewee as pw | ||||
| from peewee_migrate import Migrator | ||||
| 
 | ||||
| 
 | ||||
| with suppress(ImportError): | ||||
|     import playhouse.postgres_ext as pw_pext | ||||
| 
 | ||||
| 
 | ||||
| def migrate(migrator: Migrator, database: pw.Database, *, fake=False): | ||||
|     """Write your migrations here.""" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Auth(pw.Model): | ||||
|         id = pw.CharField(max_length=255, unique=True) | ||||
|         email = pw.CharField(max_length=255) | ||||
|         password = pw.CharField(max_length=255) | ||||
|         active = pw.BooleanField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "auth" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Chat(pw.Model): | ||||
|         id = pw.CharField(max_length=255, unique=True) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         title = pw.CharField() | ||||
|         chat = pw.TextField() | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "chat" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class ChatIdTag(pw.Model): | ||||
|         id = pw.CharField(max_length=255, unique=True) | ||||
|         tag_name = pw.CharField(max_length=255) | ||||
|         chat_id = pw.CharField(max_length=255) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "chatidtag" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Document(pw.Model): | ||||
|         id = pw.AutoField() | ||||
|         collection_name = pw.CharField(max_length=255, unique=True) | ||||
|         name = pw.CharField(max_length=255, unique=True) | ||||
|         title = pw.CharField() | ||||
|         filename = pw.CharField() | ||||
|         content = pw.TextField(null=True) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "document" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Modelfile(pw.Model): | ||||
|         id = pw.AutoField() | ||||
|         tag_name = pw.CharField(max_length=255, unique=True) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         modelfile = pw.TextField() | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "modelfile" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Prompt(pw.Model): | ||||
|         id = pw.AutoField() | ||||
|         command = pw.CharField(max_length=255, unique=True) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         title = pw.CharField() | ||||
|         content = pw.TextField() | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "prompt" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class Tag(pw.Model): | ||||
|         id = pw.CharField(max_length=255, unique=True) | ||||
|         name = pw.CharField(max_length=255) | ||||
|         user_id = pw.CharField(max_length=255) | ||||
|         data = pw.TextField(null=True) | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "tag" | ||||
| 
 | ||||
|     @migrator.create_model | ||||
|     class User(pw.Model): | ||||
|         id = pw.CharField(max_length=255, unique=True) | ||||
|         name = pw.CharField(max_length=255) | ||||
|         email = pw.CharField(max_length=255) | ||||
|         role = pw.CharField(max_length=255) | ||||
|         profile_image_url = pw.CharField(max_length=255) | ||||
|         timestamp = pw.DateField() | ||||
| 
 | ||||
|         class Meta: | ||||
|             table_name = "user" | ||||
| 
 | ||||
| 
 | ||||
| def rollback(migrator: Migrator, database: pw.Database, *, fake=False): | ||||
|     """Write your rollback migrations here.""" | ||||
| 
 | ||||
|     migrator.remove_model("user") | ||||
| 
 | ||||
|     migrator.remove_model("tag") | ||||
| 
 | ||||
|     migrator.remove_model("prompt") | ||||
| 
 | ||||
|     migrator.remove_model("modelfile") | ||||
| 
 | ||||
|     migrator.remove_model("document") | ||||
| 
 | ||||
|     migrator.remove_model("chatidtag") | ||||
| 
 | ||||
|     migrator.remove_model("chat") | ||||
| 
 | ||||
|     migrator.remove_model("auth") | ||||
|  | @ -0,0 +1,48 @@ | |||
| """Peewee migrations -- 002_add_local_sharing.py. | ||||
| 
 | ||||
| Some examples (model - class or model name):: | ||||
| 
 | ||||
|     > Model = migrator.orm['table_name']            # Return model in current state by name | ||||
|     > Model = migrator.ModelClass                   # Return model in current state by name | ||||
| 
 | ||||
|     > migrator.sql(sql)                             # Run custom SQL | ||||
|     > migrator.run(func, *args, **kwargs)           # Run python function with the given args | ||||
|     > migrator.create_model(Model)                  # Create a model (could be used as decorator) | ||||
|     > migrator.remove_model(model, cascade=True)    # Remove a model | ||||
|     > migrator.add_fields(model, **fields)          # Add fields to a model | ||||
|     > migrator.change_fields(model, **fields)       # Change fields | ||||
|     > migrator.remove_fields(model, *field_names, cascade=True) | ||||
|     > migrator.rename_field(model, old_field_name, new_field_name) | ||||
|     > migrator.rename_table(model, new_table_name) | ||||
|     > migrator.add_index(model, *col_names, unique=False) | ||||
|     > migrator.add_not_null(model, *field_names) | ||||
|     > migrator.add_default(model, field_name, default) | ||||
|     > migrator.add_constraint(model, name, sql) | ||||
|     > migrator.drop_index(model, *col_names) | ||||
|     > migrator.drop_not_null(model, *field_names) | ||||
|     > migrator.drop_constraints(model, *constraints) | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from contextlib import suppress | ||||
| 
 | ||||
| import peewee as pw | ||||
| from peewee_migrate import Migrator | ||||
| 
 | ||||
| 
 | ||||
| with suppress(ImportError): | ||||
|     import playhouse.postgres_ext as pw_pext | ||||
| 
 | ||||
| 
 | ||||
| def migrate(migrator: Migrator, database: pw.Database, *, fake=False): | ||||
|     """Write your migrations here.""" | ||||
| 
 | ||||
|     migrator.add_fields( | ||||
|         "chat", share_id=pw.CharField(max_length=255, null=True, unique=True) | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def rollback(migrator: Migrator, database: pw.Database, *, fake=False): | ||||
|     """Write your rollback migrations here.""" | ||||
| 
 | ||||
|     migrator.remove_fields("chat", "share_id") | ||||
							
								
								
									
										21
									
								
								backend/apps/web/internal/migrations/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								backend/apps/web/internal/migrations/README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| # Database Migrations | ||||
| 
 | ||||
| This directory contains all the database migrations for the web app. | ||||
| Migrations are done using the [`peewee-migrate`](https://github.com/klen/peewee_migrate) library. | ||||
| 
 | ||||
| Migrations are automatically ran at app startup. | ||||
| 
 | ||||
| ## Creating a migration | ||||
| 
 | ||||
| Have you made a change to the schema of an existing model? | ||||
| You will need to create a migration file to ensure that existing databases are updated for backwards compatibility. | ||||
| 
 | ||||
| 1. Have a database file (`webui.db`) that has the old schema prior to any of your changes. | ||||
| 2. Make your changes to the models. | ||||
| 3. From the `backend` directory, run the following command: | ||||
|    ```bash | ||||
|    pw_migrate create --auto --auto-source apps.web.models --database sqlite:///${SQLITE_DB} --directory apps/web/internal/migrations ${MIGRATION_NAME} | ||||
|    ``` | ||||
|    - `$SQLITE_DB` should be the path to the database file. | ||||
|    - `$MIGRATION_NAME` should be a descriptive name for the migration. | ||||
| 4. The migration file will be created in the `apps/web/internal/migrations` directory. | ||||
|  | @ -20,6 +20,7 @@ from config import ( | |||
|     ENABLE_SIGNUP, | ||||
|     USER_PERMISSIONS, | ||||
|     WEBHOOK_URL, | ||||
|     WEBUI_AUTH_TRUSTED_EMAIL_HEADER, | ||||
| ) | ||||
| 
 | ||||
| app = FastAPI() | ||||
|  | @ -34,7 +35,7 @@ app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS | |||
| app.state.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE | ||||
| app.state.USER_PERMISSIONS = USER_PERMISSIONS | ||||
| app.state.WEBHOOK_URL = WEBHOOK_URL | ||||
| 
 | ||||
| app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER | ||||
| 
 | ||||
| app.add_middleware( | ||||
|     CORSMiddleware, | ||||
|  |  | |||
|  | @ -139,6 +139,14 @@ class AuthsTable: | |||
|                 return user | ||||
|             else: | ||||
|                 return None | ||||
| 
 | ||||
|     def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]: | ||||
|         log.info(f"authenticate_user_by_trusted_header: {email}") | ||||
|         try: | ||||
|             auth = Auth.get(Auth.email == email, Auth.active == True) | ||||
|             if auth: | ||||
|                 user = Users.get_user_by_id(auth.id) | ||||
|                 return user | ||||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ class Chat(Model): | |||
|     title = CharField() | ||||
|     chat = TextField()  # Save Chat JSON as Text | ||||
|     timestamp = DateField() | ||||
|     share_id = CharField(null=True, unique=True) | ||||
| 
 | ||||
|     class Meta: | ||||
|         database = DB | ||||
|  | @ -31,6 +32,7 @@ class ChatModel(BaseModel): | |||
|     title: str | ||||
|     chat: str | ||||
|     timestamp: int  # timestamp in epoch | ||||
|     share_id: Optional[str] = None | ||||
| 
 | ||||
| 
 | ||||
| #################### | ||||
|  | @ -52,6 +54,7 @@ class ChatResponse(BaseModel): | |||
|     title: str | ||||
|     chat: dict | ||||
|     timestamp: int  # timestamp in epoch | ||||
|     share_id: Optional[str] = None  # id of the chat to be shared | ||||
| 
 | ||||
| 
 | ||||
| class ChatTitleIdResponse(BaseModel): | ||||
|  | @ -95,6 +98,71 @@ class ChatTable: | |||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|     def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]: | ||||
|         # Get the existing chat to share | ||||
|         chat = Chat.get(Chat.id == chat_id) | ||||
|         # Check if the chat is already shared | ||||
|         if chat.share_id: | ||||
|             return self.get_chat_by_id_and_user_id(chat.share_id, "shared") | ||||
|         # Create a new chat with the same data, but with a new ID | ||||
|         shared_chat = ChatModel( | ||||
|             **{ | ||||
|                 "id": str(uuid.uuid4()), | ||||
|                 "user_id": f"shared-{chat_id}", | ||||
|                 "title": chat.title, | ||||
|                 "chat": chat.chat, | ||||
|                 "timestamp": int(time.time()), | ||||
|             } | ||||
|         ) | ||||
|         shared_result = Chat.create(**shared_chat.model_dump()) | ||||
|         # Update the original chat with the share_id | ||||
|         result = ( | ||||
|             Chat.update(share_id=shared_chat.id).where(Chat.id == chat_id).execute() | ||||
|         ) | ||||
| 
 | ||||
|         return shared_chat if (shared_result and result) else None | ||||
| 
 | ||||
|     def update_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]: | ||||
|         try: | ||||
|             print("update_shared_chat_by_id") | ||||
|             chat = Chat.get(Chat.id == chat_id) | ||||
|             print(chat) | ||||
| 
 | ||||
|             query = Chat.update( | ||||
|                 title=chat.title, | ||||
|                 chat=chat.chat, | ||||
|             ).where(Chat.id == chat.share_id) | ||||
| 
 | ||||
|             query.execute() | ||||
| 
 | ||||
|             chat = Chat.get(Chat.id == chat.share_id) | ||||
|             return ChatModel(**model_to_dict(chat)) | ||||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|     def delete_shared_chat_by_chat_id(self, chat_id: str) -> bool: | ||||
|         try: | ||||
|             query = Chat.delete().where(Chat.user_id == f"shared-{chat_id}") | ||||
|             query.execute()  # Remove the rows, return number of rows removed. | ||||
| 
 | ||||
|             return True | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def update_chat_share_id_by_id( | ||||
|         self, id: str, share_id: Optional[str] | ||||
|     ) -> Optional[ChatModel]: | ||||
|         try: | ||||
|             query = Chat.update( | ||||
|                 share_id=share_id, | ||||
|             ).where(Chat.id == id) | ||||
|             query.execute() | ||||
| 
 | ||||
|             chat = Chat.get(Chat.id == id) | ||||
|             return ChatModel(**model_to_dict(chat)) | ||||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|     def get_chat_lists_by_user_id( | ||||
|         self, user_id: str, skip: int = 0, limit: int = 50 | ||||
|     ) -> List[ChatModel]: | ||||
|  | @ -131,6 +199,13 @@ class ChatTable: | |||
|             .order_by(Chat.timestamp.desc()) | ||||
|         ] | ||||
| 
 | ||||
|     def get_chat_by_id(self, id: str) -> Optional[ChatModel]: | ||||
|         try: | ||||
|             chat = Chat.get(Chat.id == id) | ||||
|             return ChatModel(**model_to_dict(chat)) | ||||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|     def get_chat_by_id_and_user_id(self, id: str, user_id: str) -> Optional[ChatModel]: | ||||
|         try: | ||||
|             chat = Chat.get(Chat.id == id, Chat.user_id == user_id) | ||||
|  | @ -149,12 +224,15 @@ class ChatTable: | |||
|             query = Chat.delete().where((Chat.id == id) & (Chat.user_id == user_id)) | ||||
|             query.execute()  # Remove the rows, return number of rows removed. | ||||
| 
 | ||||
|             return True | ||||
|             return True and self.delete_shared_chat_by_chat_id(id) | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def delete_chats_by_user_id(self, user_id: str) -> bool: | ||||
|         try: | ||||
| 
 | ||||
|             self.delete_shared_chats_by_user_id(user_id) | ||||
| 
 | ||||
|             query = Chat.delete().where(Chat.user_id == user_id) | ||||
|             query.execute()  # Remove the rows, return number of rows removed. | ||||
| 
 | ||||
|  | @ -162,5 +240,19 @@ class ChatTable: | |||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def delete_shared_chats_by_user_id(self, user_id: str) -> bool: | ||||
|         try: | ||||
|             shared_chat_ids = [ | ||||
|                 f"shared-{chat.id}" | ||||
|                 for chat in Chat.select().where(Chat.user_id == user_id) | ||||
|             ] | ||||
| 
 | ||||
|             query = Chat.delete().where(Chat.user_id << shared_chat_ids) | ||||
|             query.execute()  # Remove the rows, return number of rows removed. | ||||
| 
 | ||||
|             return True | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
| 
 | ||||
| Chats = ChatTable(DB) | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ from utils.utils import ( | |||
| from utils.misc import parse_duration, validate_email_format | ||||
| from utils.webhook import post_webhook | ||||
| from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES | ||||
| from config import WEBUI_AUTH_TRUSTED_EMAIL_HEADER | ||||
| 
 | ||||
| router = APIRouter() | ||||
| 
 | ||||
|  | @ -77,6 +78,8 @@ async def update_profile( | |||
| async def update_password( | ||||
|     form_data: UpdatePasswordForm, session_user=Depends(get_current_user) | ||||
| ): | ||||
|     if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: | ||||
|         raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED) | ||||
|     if session_user: | ||||
|         user = Auths.authenticate_user(session_user.email, form_data.password) | ||||
| 
 | ||||
|  | @ -96,7 +99,22 @@ async def update_password( | |||
| 
 | ||||
| @router.post("/signin", response_model=SigninResponse) | ||||
| async def signin(request: Request, form_data: SigninForm): | ||||
|     user = Auths.authenticate_user(form_data.email.lower(), form_data.password) | ||||
|     if WEBUI_AUTH_TRUSTED_EMAIL_HEADER: | ||||
|         if WEBUI_AUTH_TRUSTED_EMAIL_HEADER not in request.headers: | ||||
|             raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER) | ||||
| 
 | ||||
|         trusted_email = request.headers[WEBUI_AUTH_TRUSTED_EMAIL_HEADER].lower() | ||||
|         if not Users.get_user_by_email(trusted_email.lower()): | ||||
|             await signup( | ||||
|                 request, | ||||
|                 SignupForm( | ||||
|                     email=trusted_email, password=str(uuid.uuid4()), name=trusted_email | ||||
|                 ), | ||||
|             ) | ||||
|         user = Auths.authenticate_user_by_trusted_header(trusted_email) | ||||
|     else: | ||||
|         user = Auths.authenticate_user(form_data.email.lower(), form_data.password) | ||||
| 
 | ||||
|     if user: | ||||
|         token = create_token( | ||||
|             data={"id": user.id}, | ||||
|  |  | |||
|  | @ -189,6 +189,78 @@ async def delete_chat_by_id(request: Request, id: str, user=Depends(get_current_ | |||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| ############################ | ||||
| # ShareChatById | ||||
| ############################ | ||||
| 
 | ||||
| 
 | ||||
| @router.post("/{id}/share", response_model=Optional[ChatResponse]) | ||||
| async def share_chat_by_id(id: str, user=Depends(get_current_user)): | ||||
|     chat = Chats.get_chat_by_id_and_user_id(id, user.id) | ||||
|     if chat: | ||||
|         if chat.share_id: | ||||
|             shared_chat = Chats.update_shared_chat_by_chat_id(chat.id) | ||||
|             return ChatResponse( | ||||
|                 **{**shared_chat.model_dump(), "chat": json.loads(shared_chat.chat)} | ||||
|             ) | ||||
| 
 | ||||
|         shared_chat = Chats.insert_shared_chat_by_chat_id(chat.id) | ||||
|         if not shared_chat: | ||||
|             raise HTTPException( | ||||
|                 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||||
|                 detail=ERROR_MESSAGES.DEFAULT(), | ||||
|             ) | ||||
| 
 | ||||
|         return ChatResponse( | ||||
|             **{**shared_chat.model_dump(), "chat": json.loads(shared_chat.chat)} | ||||
|         ) | ||||
|     else: | ||||
|         raise HTTPException( | ||||
|             status_code=status.HTTP_401_UNAUTHORIZED, | ||||
|             detail=ERROR_MESSAGES.ACCESS_PROHIBITED, | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| ############################ | ||||
| # DeletedSharedChatById | ||||
| ############################ | ||||
| 
 | ||||
| 
 | ||||
| @router.delete("/{id}/share", response_model=Optional[bool]) | ||||
| async def delete_shared_chat_by_id(id: str, user=Depends(get_current_user)): | ||||
|     chat = Chats.get_chat_by_id_and_user_id(id, user.id) | ||||
|     if chat: | ||||
|         if not chat.share_id: | ||||
|             return False | ||||
| 
 | ||||
|         result = Chats.delete_shared_chat_by_chat_id(id) | ||||
|         update_result = Chats.update_chat_share_id_by_id(id, None) | ||||
| 
 | ||||
|         return result and update_result != None | ||||
|     else: | ||||
|         raise HTTPException( | ||||
|             status_code=status.HTTP_401_UNAUTHORIZED, | ||||
|             detail=ERROR_MESSAGES.ACCESS_PROHIBITED, | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| ############################ | ||||
| # GetSharedChatById | ||||
| ############################ | ||||
| 
 | ||||
| 
 | ||||
| @router.get("/share/{share_id}", response_model=Optional[ChatResponse]) | ||||
| async def get_shared_chat_by_id(share_id: str, user=Depends(get_current_user)): | ||||
|     chat = Chats.get_chat_by_id(share_id) | ||||
| 
 | ||||
|     if chat: | ||||
|         return ChatResponse(**{**chat.model_dump(), "chat": json.loads(chat.chat)}) | ||||
|     else: | ||||
|         raise HTTPException( | ||||
|             status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| ############################ | ||||
| # GetChatTagsById | ||||
| ############################ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy Jaeryang Baek
						Timothy Jaeryang Baek