merge dev
This commit is contained in:
commit
20014f4937
79 changed files with 887 additions and 582 deletions
0
.githooks/pre-commit
Normal file → Executable file
0
.githooks/pre-commit
Normal file → Executable file
2
Makefile
2
Makefile
|
|
@ -27,7 +27,7 @@ format:
|
||||||
cd src/frontend && npm run format
|
cd src/frontend && npm run format
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
poetry run mypy --exclude .venv .
|
poetry run mypy src/backend/langflow
|
||||||
poetry run black . --check
|
poetry run black . --check
|
||||||
poetry run ruff . --fix
|
poetry run ruff . --fix
|
||||||
|
|
||||||
|
|
|
||||||
21
poetry.lock
generated
21
poetry.lock
generated
|
|
@ -3047,13 +3047,13 @@ server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loguru"
|
name = "loguru"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
description = "Python logging made (stupidly) simple"
|
description = "Python logging made (stupidly) simple"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
files = [
|
files = [
|
||||||
{file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"},
|
{file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"},
|
||||||
{file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"},
|
{file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
|
@ -3061,7 +3061,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||||
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"]
|
dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "pre-commit (==3.3.1)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lxml"
|
name = "lxml"
|
||||||
|
|
@ -7041,6 +7041,17 @@ files = [
|
||||||
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
|
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "types-pywin32"
|
||||||
|
version = "306.0.0.4"
|
||||||
|
description = "Typing stubs for pywin32"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "types-pywin32-306.0.0.4.tar.gz", hash = "sha256:ae4bbec80d535053236d4bebedf55f58dee89cf5883d277f0fa89e857f3ff337"},
|
||||||
|
{file = "types_pywin32-306.0.0.4-py3-none-any.whl", hash = "sha256:f76a343ed6933008af85e158063963f923e54f2f461e697b2929b4178c7b77a1"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "types-pyyaml"
|
name = "types-pyyaml"
|
||||||
version = "6.0.12.11"
|
version = "6.0.12.11"
|
||||||
|
|
@ -7773,4 +7784,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.9,<3.11"
|
python-versions = ">=3.9,<3.11"
|
||||||
content-hash = "c877b4d713eef71815d858d30976ab21c42e5eadcc2df8159e940e03323681ee"
|
content-hash = "6523f2e35458c6d0b8d281e20dd2233180128805ea07199c073224b0d6f75ee7"
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ python-jose = "^3.3.0"
|
||||||
metaphor-python = "^0.1.11"
|
metaphor-python = "^0.1.11"
|
||||||
markupsafe = "^2.1.3"
|
markupsafe = "^2.1.3"
|
||||||
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
|
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
|
||||||
|
loguru = "^0.7.1"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
black = "^23.1.0"
|
black = "^23.1.0"
|
||||||
|
|
@ -103,6 +104,7 @@ types-python-jose = "^3.3.4.8"
|
||||||
types-passlib = "^1.7.7.13"
|
types-passlib = "^1.7.7.13"
|
||||||
pytest-mock = "^3.11.1"
|
pytest-mock = "^3.11.1"
|
||||||
pytest-xdist = "^3.3.1"
|
pytest-xdist = "^3.3.1"
|
||||||
|
types-pywin32 = "^306.0.0.4"
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
|
|
|
||||||
|
|
@ -356,7 +356,7 @@ def superuser(
|
||||||
with session_getter(db_manager) as session:
|
with session_getter(db_manager) as session:
|
||||||
from langflow.services.auth.utils import create_super_user
|
from langflow.services.auth.utils import create_super_user
|
||||||
|
|
||||||
if create_super_user(session, username, password):
|
if create_super_user(db=session, username=username, password=password):
|
||||||
# Verify that the superuser was created
|
# Verify that the superuser was created
|
||||||
from langflow.services.database.models.user.user import User
|
from langflow.services.database.models.user.user import User
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ def upgrade() -> None:
|
||||||
# and other related indices
|
# and other related indices
|
||||||
if "flowstyle" in existing_tables:
|
if "flowstyle" in existing_tables:
|
||||||
op.drop_table("flowstyle")
|
op.drop_table("flowstyle")
|
||||||
if "ix_flowstyle_flow_id" in [
|
if "ix_flowstyle_flow_id" in [
|
||||||
index["name"] for index in inspector.get_indexes("flowstyle")
|
index["name"] for index in inspector.get_indexes("flowstyle")
|
||||||
]:
|
]:
|
||||||
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle")
|
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle")
|
||||||
|
|
||||||
existing_indices_flow = []
|
existing_indices_flow = []
|
||||||
existing_fks_flow = []
|
existing_fks_flow = []
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ class FrontendNodeRequest(FrontendNode):
|
||||||
class ValidatePromptRequest(BaseModel):
|
class ValidatePromptRequest(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
template: str
|
template: str
|
||||||
#optional for tweak call
|
# optional for tweak call
|
||||||
frontend_node: Optional[FrontendNodeRequest]
|
frontend_node: Optional[FrontendNodeRequest] = None
|
||||||
|
|
||||||
|
|
||||||
# Build ValidationResponse class for {"imports": {"errors": []}, "function": {"errors": []}}
|
# Build ValidationResponse class for {"imports": {"errors": []}, "function": {"errors": []}}
|
||||||
|
|
@ -41,8 +41,8 @@ class CodeValidationResponse(BaseModel):
|
||||||
|
|
||||||
class PromptValidationResponse(BaseModel):
|
class PromptValidationResponse(BaseModel):
|
||||||
input_variables: list
|
input_variables: list
|
||||||
#object return for tweak call
|
# object return for tweak call
|
||||||
frontend_node: FrontendNodeRequest | object
|
frontend_node: Optional[FrontendNodeRequest] = None
|
||||||
|
|
||||||
|
|
||||||
INVALID_CHARACTERS = {
|
INVALID_CHARACTERS = {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from fastapi import WebSocket
|
||||||
|
|
||||||
|
|
||||||
from langchain.schema import AgentAction, LLMResult, AgentFinish
|
from langchain.schema import AgentAction, LLMResult, AgentFinish
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,15 @@ from fastapi.responses import StreamingResponse
|
||||||
from langflow.api.utils import build_input_keys_response
|
from langflow.api.utils import build_input_keys_response
|
||||||
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
|
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
|
||||||
|
|
||||||
from langflow.services import service_manager, ServiceType
|
|
||||||
from langflow.graph.graph.base import Graph
|
from langflow.graph.graph.base import Graph
|
||||||
from langflow.services.auth.utils import get_current_active_user, get_current_user
|
from langflow.services.auth.utils import get_current_active_user, get_current_user
|
||||||
from langflow.services.utils import get_session
|
from langflow.services.utils import get_session
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
from langflow.services.utils import get_chat_manager, get_session
|
||||||
from cachetools import LRUCache
|
from cachetools import LRUCache
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
from typing import TYPE_CHECKING
|
from langflow.services.chat.manager import ChatManager
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from langflow.services.chat.manager import ChatManager
|
|
||||||
|
|
||||||
router = APIRouter(tags=["Chat"])
|
router = APIRouter(tags=["Chat"])
|
||||||
|
|
||||||
|
|
@ -34,6 +32,7 @@ async def chat(
|
||||||
websocket: WebSocket,
|
websocket: WebSocket,
|
||||||
token: str = Query(...),
|
token: str = Query(...),
|
||||||
db: Session = Depends(get_session),
|
db: Session = Depends(get_session),
|
||||||
|
chat_manager: "ChatManager" = Depends(get_chat_manager),
|
||||||
):
|
):
|
||||||
"""Websocket endpoint for chat."""
|
"""Websocket endpoint for chat."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -48,7 +47,6 @@ async def chat(
|
||||||
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
|
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
|
||||||
)
|
)
|
||||||
|
|
||||||
chat_manager: "ChatManager" = service_manager.get(ServiceType.CHAT_MANAGER)
|
|
||||||
if client_id in chat_manager.in_memory_cache:
|
if client_id in chat_manager.in_memory_cache:
|
||||||
await chat_manager.handle_websocket(client_id, websocket)
|
await chat_manager.handle_websocket(client_id, websocket)
|
||||||
else:
|
else:
|
||||||
|
|
@ -61,19 +59,21 @@ async def chat(
|
||||||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Error in chat websocket: {exc}")
|
logger.error(f"Error in chat websocket: {exc}")
|
||||||
if isinstance(exc, HTTPException):
|
messsage = exc.detail if isinstance(exc, HTTPException) else str(exc)
|
||||||
exc = exc.detail
|
|
||||||
if "Could not validate credentials" in str(exc):
|
if "Could not validate credentials" in str(exc):
|
||||||
await websocket.close(
|
await websocket.close(
|
||||||
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
|
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=messsage)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
|
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
|
||||||
async def init_build(
|
async def init_build(
|
||||||
graph_data: dict, flow_id: str, current_user=Depends(get_current_active_user)
|
graph_data: dict,
|
||||||
|
flow_id: str,
|
||||||
|
current_user=Depends(get_current_active_user),
|
||||||
|
chat_manager: "ChatManager" = Depends(get_chat_manager),
|
||||||
):
|
):
|
||||||
"""Initialize the build by storing graph data and returning a unique session ID."""
|
"""Initialize the build by storing graph data and returning a unique session ID."""
|
||||||
|
|
||||||
|
|
@ -88,7 +88,6 @@ async def init_build(
|
||||||
return InitResponse(flowId=flow_id)
|
return InitResponse(flowId=flow_id)
|
||||||
|
|
||||||
# Delete from cache if already exists
|
# Delete from cache if already exists
|
||||||
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
|
|
||||||
if flow_id in chat_manager.in_memory_cache:
|
if flow_id in chat_manager.in_memory_cache:
|
||||||
with chat_manager.in_memory_cache._lock:
|
with chat_manager.in_memory_cache._lock:
|
||||||
chat_manager.in_memory_cache.delete(flow_id)
|
chat_manager.in_memory_cache.delete(flow_id)
|
||||||
|
|
@ -124,7 +123,9 @@ async def build_status(flow_id: str):
|
||||||
|
|
||||||
|
|
||||||
@router.get("/build/stream/{flow_id}", response_class=StreamingResponse)
|
@router.get("/build/stream/{flow_id}", response_class=StreamingResponse)
|
||||||
async def stream_build(flow_id: str):
|
async def stream_build(
|
||||||
|
flow_id: str, chat_manager: "ChatManager" = Depends(get_chat_manager)
|
||||||
|
):
|
||||||
"""Stream the build process based on stored flow data."""
|
"""Stream the build process based on stored flow data."""
|
||||||
|
|
||||||
async def event_stream(flow_id):
|
async def event_stream(flow_id):
|
||||||
|
|
@ -203,7 +204,6 @@ async def stream_build(flow_id: str):
|
||||||
"handle_keys": [],
|
"handle_keys": [],
|
||||||
}
|
}
|
||||||
yield str(StreamData(event="message", data=input_keys_response))
|
yield str(StreamData(event="message", data=input_keys_response))
|
||||||
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
|
|
||||||
chat_manager.set_cache(flow_id, langchain_object)
|
chat_manager.set_cache(flow_id, langchain_object)
|
||||||
# We need to reset the chat history
|
# We need to reset the chat history
|
||||||
chat_manager.chat_history.empty_history(flow_id)
|
chat_manager.chat_history.empty_history(flow_id)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from langflow.services.database.models.flow import Flow
|
||||||
from langflow.processing.process import process_graph_cached, process_tweaks
|
from langflow.processing.process import process_graph_cached, process_tweaks
|
||||||
from langflow.services.database.models.user.user import User
|
from langflow.services.database.models.user.user import User
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status
|
from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from langflow.interface.custom.custom_component import CustomComponent
|
from langflow.interface.custom.custom_component import CustomComponent
|
||||||
|
|
@ -34,14 +34,15 @@ from sqlmodel import Session
|
||||||
router = APIRouter(tags=["Base"])
|
router = APIRouter(tags=["Base"])
|
||||||
|
|
||||||
|
|
||||||
@router.get("/all")
|
@router.get("/all", dependencies=[Depends(get_current_active_user)])
|
||||||
def get_all(current_user: User = Depends(get_current_active_user)):
|
def get_all(
|
||||||
|
settings_manager=Depends(get_settings_manager),
|
||||||
|
):
|
||||||
logger.debug("Building langchain types dict")
|
logger.debug("Building langchain types dict")
|
||||||
native_components = build_langchain_types_dict()
|
native_components = build_langchain_types_dict()
|
||||||
# custom_components is a list of dicts
|
# custom_components is a list of dicts
|
||||||
# need to merge all the keys into one dict
|
# need to merge all the keys into one dict
|
||||||
custom_components_from_file: dict[str, Any] = {}
|
custom_components_from_file: dict[str, Any] = {}
|
||||||
settings_manager = get_settings_manager()
|
|
||||||
if settings_manager.settings.COMPONENTS_PATH:
|
if settings_manager.settings.COMPONENTS_PATH:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}"
|
f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}"
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ def update_flow(
|
||||||
flow_id: UUID,
|
flow_id: UUID,
|
||||||
flow: FlowUpdate,
|
flow: FlowUpdate,
|
||||||
current_user: User = Depends(get_current_active_user),
|
current_user: User = Depends(get_current_active_user),
|
||||||
|
settings_manager=Depends(get_settings_manager),
|
||||||
):
|
):
|
||||||
"""Update a flow."""
|
"""Update a flow."""
|
||||||
|
|
||||||
|
|
@ -90,7 +91,6 @@ def update_flow(
|
||||||
if not db_flow:
|
if not db_flow:
|
||||||
raise HTTPException(status_code=404, detail="Flow not found")
|
raise HTTPException(status_code=404, detail="Flow not found")
|
||||||
flow_data = flow.dict(exclude_unset=True)
|
flow_data = flow.dict(exclude_unset=True)
|
||||||
settings_manager = get_settings_manager()
|
|
||||||
if settings_manager.settings.REMOVE_API_KEYS:
|
if settings_manager.settings.REMOVE_API_KEYS:
|
||||||
flow_data = remove_api_keys(flow_data)
|
flow_data = remove_api_keys(flow_data)
|
||||||
for key, value in flow_data.items():
|
for key, value in flow_data.items():
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,9 @@ async def login_to_get_access_token(
|
||||||
|
|
||||||
|
|
||||||
@router.get("/auto_login")
|
@router.get("/auto_login")
|
||||||
async def auto_login(db: Session = Depends(get_session)):
|
async def auto_login(
|
||||||
settings_manager = get_settings_manager()
|
db: Session = Depends(get_session), settings_manager=Depends(get_settings_manager)
|
||||||
|
):
|
||||||
if settings_manager.auth_settings.AUTO_LOGIN:
|
if settings_manager.auth_settings.AUTO_LOGIN:
|
||||||
return create_user_longterm_token(db)
|
return create_user_longterm_token(db)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ router = APIRouter(tags=["Users"])
|
||||||
@router.post("/user", response_model=UserRead, status_code=201)
|
@router.post("/user", response_model=UserRead, status_code=201)
|
||||||
def add_user(
|
def add_user(
|
||||||
user: UserCreate,
|
user: UserCreate,
|
||||||
db: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
) -> User:
|
) -> User:
|
||||||
"""
|
"""
|
||||||
Add a new user to the database.
|
Add a new user to the database.
|
||||||
|
|
@ -38,12 +38,14 @@ def add_user(
|
||||||
try:
|
try:
|
||||||
new_user.password = get_password_hash(user.password)
|
new_user.password = get_password_hash(user.password)
|
||||||
|
|
||||||
db.add(new_user)
|
session.add(new_user)
|
||||||
db.commit()
|
session.commit()
|
||||||
db.refresh(new_user)
|
session.refresh(new_user)
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
db.rollback()
|
session.rollback()
|
||||||
raise HTTPException(status_code=400, detail="This username is unavailable.") from e
|
raise HTTPException(
|
||||||
|
status_code=400, detail="This username is unavailable."
|
||||||
|
) from e
|
||||||
|
|
||||||
return new_user
|
return new_user
|
||||||
|
|
||||||
|
|
@ -63,16 +65,16 @@ def read_all_users(
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
current_user: Session = Depends(get_current_active_superuser),
|
current_user: Session = Depends(get_current_active_superuser),
|
||||||
db: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
) -> UsersResponse:
|
) -> UsersResponse:
|
||||||
"""
|
"""
|
||||||
Retrieve a list of users from the database with pagination.
|
Retrieve a list of users from the database with pagination.
|
||||||
"""
|
"""
|
||||||
query = select(User).offset(skip).limit(limit)
|
query = select(User).offset(skip).limit(limit)
|
||||||
users = db.execute(query).fetchall()
|
users = session.execute(query).fetchall()
|
||||||
|
|
||||||
count_query = select(func.count()).select_from(User) # type: ignore
|
count_query = select(func.count()).select_from(User) # type: ignore
|
||||||
total_count = db.execute(count_query).scalar()
|
total_count = session.execute(count_query).scalar()
|
||||||
|
|
||||||
return UsersResponse(
|
return UsersResponse(
|
||||||
total_count=total_count, # type: ignore
|
total_count=total_count, # type: ignore
|
||||||
|
|
@ -85,19 +87,19 @@ def patch_user(
|
||||||
user_id: UUID,
|
user_id: UUID,
|
||||||
user: UserUpdate,
|
user: UserUpdate,
|
||||||
_: Session = Depends(get_current_active_user),
|
_: Session = Depends(get_current_active_user),
|
||||||
db: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
) -> User:
|
) -> User:
|
||||||
"""
|
"""
|
||||||
Update an existing user's data.
|
Update an existing user's data.
|
||||||
"""
|
"""
|
||||||
return update_user(user_id, user, db)
|
return update_user(user_id, user, session)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/user/{user_id}")
|
@router.delete("/user/{user_id}")
|
||||||
def delete_user(
|
def delete_user(
|
||||||
user_id: UUID,
|
user_id: UUID,
|
||||||
current_user: User = Depends(get_current_active_superuser),
|
current_user: User = Depends(get_current_active_superuser),
|
||||||
db: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Delete a user from the database.
|
Delete a user from the database.
|
||||||
|
|
@ -111,12 +113,12 @@ def delete_user(
|
||||||
status_code=403, detail="You don't have the permission to delete this user"
|
status_code=403, detail="You don't have the permission to delete this user"
|
||||||
)
|
)
|
||||||
|
|
||||||
user_db = db.query(User).filter(User.id == user_id).first()
|
user_db = session.query(User).filter(User.id == user_id).first()
|
||||||
if not user_db:
|
if not user_db:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
db.delete(user_db)
|
session.delete(user_db)
|
||||||
db.commit()
|
session.commit()
|
||||||
|
|
||||||
return {"detail": "User deleted"}
|
return {"detail": "User deleted"}
|
||||||
|
|
||||||
|
|
@ -124,7 +126,7 @@ def delete_user(
|
||||||
# TODO: REMOVE - Just for testing purposes
|
# TODO: REMOVE - Just for testing purposes
|
||||||
@router.post("/super_user", response_model=User)
|
@router.post("/super_user", response_model=User)
|
||||||
def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
|
def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
|
||||||
db: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
) -> User:
|
) -> User:
|
||||||
"""
|
"""
|
||||||
Add a superuser for testing purposes.
|
Add a superuser for testing purposes.
|
||||||
|
|
@ -139,11 +141,11 @@ def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.add(new_user)
|
session.add(new_user)
|
||||||
db.commit()
|
session.commit()
|
||||||
db.refresh(new_user)
|
session.refresh(new_user)
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
db.rollback()
|
session.rollback()
|
||||||
raise HTTPException(status_code=400, detail="User exists") from e
|
raise HTTPException(status_code=400, detail="User exists") from e
|
||||||
|
|
||||||
return new_user
|
return new_user
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from langflow.api.v1.base import (
|
||||||
validate_prompt,
|
validate_prompt,
|
||||||
)
|
)
|
||||||
from langflow.template.field.base import TemplateField
|
from langflow.template.field.base import TemplateField
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.validate import validate_code
|
from langflow.utils.validate import validate_code
|
||||||
|
|
||||||
# build router
|
# build router
|
||||||
|
|
@ -35,7 +35,7 @@ def post_validate_prompt(prompt_request: ValidatePromptRequest):
|
||||||
if prompt_request.frontend_node is None:
|
if prompt_request.frontend_node is None:
|
||||||
return PromptValidationResponse(
|
return PromptValidationResponse(
|
||||||
input_variables=input_variables,
|
input_variables=input_variables,
|
||||||
frontend_node={},
|
frontend_node=None,
|
||||||
)
|
)
|
||||||
old_custom_fields = get_old_custom_fields(prompt_request)
|
old_custom_fields = get_old_custom_fields(prompt_request)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from langflow.graph.vertex.types import (
|
||||||
)
|
)
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from langflow.graph.utils import UnbuiltObject
|
||||||
from langflow.interface.initialize import loading
|
from langflow.interface.initialize import loading
|
||||||
from langflow.interface.listing import lazy_load_dict
|
from langflow.interface.listing import lazy_load_dict
|
||||||
from langflow.utils.constants import DIRECT_TYPES
|
from langflow.utils.constants import DIRECT_TYPES
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import sync_to_async
|
from langflow.utils.util import sync_to_async
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from langflow.interface.base import LangChainTypeCreator
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.agents import AgentFrontendNode
|
from langflow.template.frontend_node.agents import AgentFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from pydantic import BaseModel
|
||||||
from langflow.template.field.base import TemplateField
|
from langflow.template.field.base import TemplateField
|
||||||
from langflow.template.frontend_node.base import FrontendNode
|
from langflow.template.frontend_node.base import FrontendNode
|
||||||
from langflow.template.template.base import Template
|
from langflow.template.template.base import Template
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.chains import ChainFrontendNode
|
from langflow.template.frontend_node.chains import ChainFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
from langchain import chains
|
from langchain import chains
|
||||||
from langchain_experimental.sql import SQLDatabaseChain # type: ignore
|
from langchain_experimental.sql import SQLDatabaseChain # type: ignore
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from langflow.interface.custom.custom_component import CustomComponent
|
||||||
from langflow.template.frontend_node.custom_components import (
|
from langflow.template.frontend_node.custom_components import (
|
||||||
CustomComponentFrontendNode,
|
CustomComponentFrontendNode,
|
||||||
)
|
)
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import ast
|
import ast
|
||||||
import zlib
|
import zlib
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
class CustomComponentPathValueError(ValueError):
|
class CustomComponentPathValueError(ValueError):
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
|
||||||
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
|
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
|
||||||
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
|
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
|
||||||
|
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.base import FrontendNode
|
from langflow.template.frontend_node.base import FrontendNode
|
||||||
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode
|
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ from langflow.utils import validate
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
from langchain.vectorstores.base import VectorStore
|
from langchain.vectorstores.base import VectorStore
|
||||||
from langchain.document_loaders.base import BaseLoader
|
from langchain.document_loaders.base import BaseLoader
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from langflow.interface.custom_lists import llm_type_to_cls_dict
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.llms import LLMFrontendNode
|
from langflow.template.frontend_node.llms import LLMFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.base import FrontendNode
|
from langflow.template.frontend_node.base import FrontendNode
|
||||||
from langflow.template.frontend_node.memories import MemoryFrontendNode
|
from langflow.template.frontend_node.memories import MemoryFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
from langflow.custom.customs import get_custom_nodes
|
from langflow.custom.customs import get_custom_nodes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
|
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.prompts import PromptFrontendNode
|
from langflow.template.frontend_node.prompts import PromptFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode
|
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_method, build_template_from_class
|
from langflow.utils.util import build_template_from_method, build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import Any, Dict, Tuple
|
from typing import Any, Dict, Tuple
|
||||||
from langflow.services.cache.utils import memoize_dict
|
from langflow.services.cache.utils import memoize_dict
|
||||||
from langflow.graph import Graph
|
from langflow.graph import Graph
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
@memoize_dict(maxsize=10)
|
@memoize_dict(maxsize=10)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
|
||||||
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
|
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
|
||||||
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
|
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
|
||||||
|
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langflow.interface.base import LangChainTypeCreator
|
||||||
from langflow.interface.importing.utils import import_class, import_module
|
from langflow.interface.importing.utils import import_class, import_module
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import inspect
|
||||||
from typing import Dict, Union
|
from typing import Dict, Union
|
||||||
|
|
||||||
from langchain.agents.tools import Tool
|
from langchain.agents.tools import Tool
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:
|
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ from langflow.template.frontend_node.custom_components import (
|
||||||
from langflow.interface.retrievers.base import retriever_creator
|
from langflow.interface.retrievers.base import retriever_creator
|
||||||
|
|
||||||
from langflow.interface.custom.directory_reader import DirectoryReader
|
from langflow.interface.custom.directory_reader import DirectoryReader
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import get_base_classes
|
from langflow.utils.util import get_base_classes
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
|
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class
|
from langflow.utils.util import build_template_from_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import re
|
||||||
import yaml
|
import yaml
|
||||||
from langchain.base_language import BaseLanguageModel
|
from langchain.base_language import BaseLanguageModel
|
||||||
from PIL.Image import Image
|
from PIL.Image import Image
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.services.chat.config import ChatConfig
|
from langflow.services.chat.config import ChatConfig
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
|
|
||||||
from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode
|
from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_method
|
from langflow.utils.util import build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from typing import Dict, List, Optional
|
||||||
from langchain import requests, sql_database
|
from langchain import requests, sql_database
|
||||||
|
|
||||||
from langflow.interface.base import LangChainTypeCreator
|
from langflow.interface.base import LangChainTypeCreator
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from langflow.api import router
|
||||||
|
|
||||||
from langflow.interface.utils import setup_llm_caching
|
from langflow.interface.utils import setup_llm_caching
|
||||||
from langflow.services.database.utils import initialize_database
|
from langflow.services.database.utils import initialize_database
|
||||||
from langflow.services.manager import initialize_services
|
from langflow.services.manager import initialize_services, teardown_services
|
||||||
from langflow.utils.logger import configure
|
from langflow.utils.logger import configure
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,6 +40,7 @@ def create_app():
|
||||||
app.on_event("startup")(initialize_services)
|
app.on_event("startup")(initialize_services)
|
||||||
app.on_event("startup")(initialize_database)
|
app.on_event("startup")(initialize_database)
|
||||||
app.on_event("startup")(setup_llm_caching)
|
app.on_event("startup")(setup_llm_caching)
|
||||||
|
app.on_event("shutdown")(teardown_services)
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from langflow.api.v1.callback import (
|
||||||
StreamingLLMCallbackHandler,
|
StreamingLLMCallbackHandler,
|
||||||
)
|
)
|
||||||
from langflow.processing.process import fix_memory_inputs, format_actions
|
from langflow.processing.process import fix_memory_inputs, format_actions
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langchain.agents.agent import AgentExecutor
|
from langchain.agents.agent import AgentExecutor
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from langflow.interface.run import (
|
||||||
get_memory_key,
|
get_memory_key,
|
||||||
update_memory_keys,
|
update_memory_keys,
|
||||||
)
|
)
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from langflow.graph import Graph
|
from langflow.graph import Graph
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
from langchain.vectorstores.base import VectorStore
|
from langchain.vectorstores.base import VectorStore
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,12 @@ async def api_key_security(
|
||||||
result: Optional[Union[ApiKey, User]] = None
|
result: Optional[Union[ApiKey, User]] = None
|
||||||
if settings_manager.auth_settings.AUTO_LOGIN:
|
if settings_manager.auth_settings.AUTO_LOGIN:
|
||||||
# Get the first user
|
# Get the first user
|
||||||
settings_manager.auth_settings.FIRST_SUPERUSER
|
if not settings_manager.auth_settings.FIRST_SUPERUSER:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Missing first superuser credentials",
|
||||||
|
)
|
||||||
|
|
||||||
result = get_user_by_username(
|
result = get_user_by_username(
|
||||||
db, settings_manager.auth_settings.FIRST_SUPERUSER
|
db, settings_manager.auth_settings.FIRST_SUPERUSER
|
||||||
)
|
)
|
||||||
|
|
@ -80,6 +85,9 @@ async def get_current_user(
|
||||||
if isinstance(token, Coroutine):
|
if isinstance(token, Coroutine):
|
||||||
token = await token
|
token = await token
|
||||||
|
|
||||||
|
if settings_manager.auth_settings.SECRET_KEY is None:
|
||||||
|
raise credentials_exception
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token,
|
token,
|
||||||
|
|
@ -150,22 +158,16 @@ def create_token(data: dict, expires_delta: timedelta):
|
||||||
|
|
||||||
|
|
||||||
def create_super_user(
|
def create_super_user(
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
db: Session = Depends(get_session),
|
db: Session = Depends(get_session),
|
||||||
username: Optional[str] = None,
|
|
||||||
password: Optional[str] = None,
|
|
||||||
) -> User:
|
) -> User:
|
||||||
settings_manager = get_settings_manager()
|
super_user = get_user_by_username(db, username)
|
||||||
|
|
||||||
super_user = get_user_by_username(
|
|
||||||
db, username or settings_manager.auth_settings.FIRST_SUPERUSER
|
|
||||||
)
|
|
||||||
|
|
||||||
if not super_user:
|
if not super_user:
|
||||||
super_user = User(
|
super_user = User(
|
||||||
username=username or settings_manager.auth_settings.FIRST_SUPERUSER,
|
username=username,
|
||||||
password=get_password_hash(
|
password=get_password_hash(password),
|
||||||
password or settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
|
|
||||||
),
|
|
||||||
is_superuser=True,
|
is_superuser=True,
|
||||||
is_active=True,
|
is_active=True,
|
||||||
last_login_at=None,
|
last_login_at=None,
|
||||||
|
|
@ -179,7 +181,15 @@ def create_super_user(
|
||||||
|
|
||||||
|
|
||||||
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
|
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
|
||||||
super_user = create_super_user(db)
|
settings_manager = get_settings_manager()
|
||||||
|
username = settings_manager.auth_settings.FIRST_SUPERUSER
|
||||||
|
password = settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
|
||||||
|
if not username or not password:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Missing first superuser credentials",
|
||||||
|
)
|
||||||
|
super_user = create_super_user(db=db, username=username, password=password)
|
||||||
|
|
||||||
access_token_expires_longterm = timedelta(days=365)
|
access_token_expires_longterm = timedelta(days=365)
|
||||||
access_token = create_token(
|
access_token = create_token(
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,8 @@
|
||||||
class Service:
|
from abc import ABC
|
||||||
|
|
||||||
|
|
||||||
|
class Service(ABC):
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
pass
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from langflow.services.cache.manager import Subject
|
||||||
from langflow.services.chat.utils import process_graph
|
from langflow.services.chat.utils import process_graph
|
||||||
from langflow.interface.utils import pil_to_base64
|
from langflow.interface.utils import pil_to_base64
|
||||||
from langflow.services.schema import ServiceType
|
from langflow.services.schema import ServiceType
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from fastapi import WebSocket
|
||||||
from langflow.api.v1.schemas import ChatMessage
|
from langflow.api.v1.schemas import ChatMessage
|
||||||
from langflow.processing.base import get_result_and_steps
|
from langflow.processing.base import get_result_and_steps
|
||||||
from langflow.interface.utils import try_setting_streaming_options
|
from langflow.interface.utils import try_setting_streaming_options
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
async def process_graph(
|
async def process_graph(
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from langflow.services.base import Service
|
from langflow.services.base import Service
|
||||||
|
from langflow.services.database.models.user.crud import get_user_by_username
|
||||||
from langflow.services.database.utils import Result, TableResults
|
from langflow.services.database.utils import Result, TableResults
|
||||||
from langflow.services.utils import get_settings_manager
|
from langflow.services.utils import get_settings_manager
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlmodel import SQLModel, Session, create_engine
|
from sqlmodel import SQLModel, Session, create_engine
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from alembic.config import Config
|
from alembic.config import Config
|
||||||
from alembic import command
|
from alembic import command
|
||||||
from langflow.services.database import models # noqa
|
from langflow.services.database import models # noqa
|
||||||
|
|
@ -88,7 +89,7 @@ class DatabaseManager(Service):
|
||||||
|
|
||||||
for table in legacy_tables:
|
for table in legacy_tables:
|
||||||
if table in inspector.get_table_names():
|
if table in inspector.get_table_names():
|
||||||
logger.warn(f"Legacy table exists: {table}")
|
logger.warning(f"Legacy table exists: {table}")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -159,3 +160,23 @@ class DatabaseManager(Service):
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("Database and tables created successfully")
|
logger.debug("Database and tables created successfully")
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
logger.debug("Tearing down database")
|
||||||
|
try:
|
||||||
|
settings_manager = get_settings_manager()
|
||||||
|
# remove the default superuser if auto_login is enabled
|
||||||
|
# using the FIRST_SUPERUSER to get the user
|
||||||
|
if settings_manager.auth_settings.AUTO_LOGIN:
|
||||||
|
logger.debug("Removing default superuser")
|
||||||
|
username = settings_manager.auth_settings.FIRST_SUPERUSER
|
||||||
|
with Session(self.engine) as session:
|
||||||
|
user = get_user_by_username(session, username)
|
||||||
|
session.delete(user)
|
||||||
|
session.commit()
|
||||||
|
logger.debug("Default superuser removed")
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(f"Error tearing down database: {exc}")
|
||||||
|
|
||||||
|
self.engine.dispose()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from alembic.util.exc import CommandError
|
from alembic.util.exc import CommandError
|
||||||
from sqlmodel import Session
|
from sqlmodel import Session
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from langflow.services.schema import ServiceType
|
from langflow.services.schema import ServiceType
|
||||||
from typing import TYPE_CHECKING, List, Optional
|
from typing import TYPE_CHECKING, List, Optional
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.services.factory import ServiceFactory
|
from langflow.services.factory import ServiceFactory
|
||||||
|
|
@ -42,6 +43,7 @@ class ServiceManager:
|
||||||
"""
|
"""
|
||||||
Create a new service given its name, handling dependencies.
|
Create a new service given its name, handling dependencies.
|
||||||
"""
|
"""
|
||||||
|
logger.debug(f"Create service {service_name}")
|
||||||
self._validate_service_creation(service_name)
|
self._validate_service_creation(service_name)
|
||||||
|
|
||||||
# Create dependencies first
|
# Create dependencies first
|
||||||
|
|
@ -74,9 +76,21 @@ class ServiceManager:
|
||||||
Update a service by its name.
|
Update a service by its name.
|
||||||
"""
|
"""
|
||||||
if service_name in self.services:
|
if service_name in self.services:
|
||||||
|
logger.debug(f"Update service {service_name}")
|
||||||
self.services.pop(service_name, None)
|
self.services.pop(service_name, None)
|
||||||
self.get(service_name)
|
self.get(service_name)
|
||||||
|
|
||||||
|
def teardown(self):
|
||||||
|
"""
|
||||||
|
Teardown all the services.
|
||||||
|
"""
|
||||||
|
for service in self.services.values():
|
||||||
|
logger.debug(f"Teardown service {service.name}")
|
||||||
|
service.teardown()
|
||||||
|
self.services = {}
|
||||||
|
self.factories = {}
|
||||||
|
self.dependencies = {}
|
||||||
|
|
||||||
|
|
||||||
service_manager = ServiceManager()
|
service_manager = ServiceManager()
|
||||||
|
|
||||||
|
|
@ -121,7 +135,7 @@ def initialize_session_manager():
|
||||||
"""
|
"""
|
||||||
Initialize the session manager.
|
Initialize the session manager.
|
||||||
"""
|
"""
|
||||||
from langflow.services.session import factory as session_manager_factory
|
from langflow.services.session import factory as session_manager_factory # type: ignore
|
||||||
from langflow.services.cache import factory as cache_factory
|
from langflow.services.cache import factory as cache_factory
|
||||||
|
|
||||||
initialize_settings_manager()
|
initialize_settings_manager()
|
||||||
|
|
@ -134,3 +148,10 @@ def initialize_session_manager():
|
||||||
session_manager_factory.SessionManagerFactory(),
|
session_manager_factory.SessionManagerFactory(),
|
||||||
dependencies=[ServiceType.CACHE_MANAGER],
|
dependencies=[ServiceType.CACHE_MANAGER],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_services():
|
||||||
|
"""
|
||||||
|
Teardown all the services.
|
||||||
|
"""
|
||||||
|
service_manager.teardown()
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,17 @@ from langflow.services.settings.utils import read_secret_from_file, write_secret
|
||||||
|
|
||||||
from pydantic import BaseSettings, Field, validator
|
from pydantic import BaseSettings, Field, validator
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
class AuthSettings(BaseSettings):
|
class AuthSettings(BaseSettings):
|
||||||
# Login settings
|
# Login settings
|
||||||
CONFIG_DIR: str
|
CONFIG_DIR: str
|
||||||
SECRET_KEY: Optional[str] = Field(
|
SECRET_KEY: str = Field(
|
||||||
None,
|
default="",
|
||||||
description="Secret key for JWT. If not provided, a random one will be generated.",
|
description="Secret key for JWT. If not provided, a random one will be generated.",
|
||||||
env="LANGFLOW_SECRET_KEY",
|
env="LANGFLOW_SECRET_KEY",
|
||||||
|
allow_mutation=False,
|
||||||
)
|
)
|
||||||
ALGORITHM: str = "HS256"
|
ALGORITHM: str = "HS256"
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from pydantic import BaseSettings, root_validator, validator
|
from pydantic import BaseSettings, root_validator, validator
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
|
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
|
||||||
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
|
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from langflow.services.base import Service
|
from langflow.services.base import Service
|
||||||
from langflow.services.settings.auth import AuthSettings
|
from langflow.services.settings.auth import AuthSettings
|
||||||
from langflow.services.settings.base import Settings
|
from langflow.services.settings.base import Settings
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
@ -35,5 +35,10 @@ class SettingsManager(Service):
|
||||||
)
|
)
|
||||||
|
|
||||||
settings = Settings(**settings_dict)
|
settings = Settings(**settings_dict)
|
||||||
auth_settings = AuthSettings(CONFIG_DIR=settings.CONFIG_DIR)
|
if not settings.CONFIG_DIR:
|
||||||
|
raise ValueError("CONFIG_DIR must be set in settings")
|
||||||
|
|
||||||
|
auth_settings = AuthSettings(
|
||||||
|
CONFIG_DIR=settings.CONFIG_DIR,
|
||||||
|
)
|
||||||
return cls(settings, auth_settings)
|
return cls(settings, auth_settings)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
from langflow.utils.logger import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
def set_secure_permissions(file_path):
|
def set_secure_permissions(file_path):
|
||||||
|
|
@ -43,5 +43,5 @@ def write_secret_to_file(path: Path, value: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def read_secret_from_file(path: Path) -> str:
|
def read_secret_from_file(path: Path) -> str:
|
||||||
with path.open("rb") as f:
|
with path.open("r") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
from langflow.services import ServiceType, service_manager
|
from langflow.services import ServiceType, service_manager
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Generator
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.services.database.manager import DatabaseManager
|
from langflow.services.database.manager import DatabaseManager
|
||||||
from langflow.services.settings.manager import SettingsManager
|
from langflow.services.settings.manager import SettingsManager
|
||||||
|
from langflow.services.chat.manager import ChatManager
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
|
|
||||||
def get_settings_manager() -> "SettingsManager":
|
def get_settings_manager() -> "SettingsManager":
|
||||||
|
|
@ -15,6 +17,10 @@ def get_db_manager() -> "DatabaseManager":
|
||||||
return service_manager.get(ServiceType.DATABASE_MANAGER)
|
return service_manager.get(ServiceType.DATABASE_MANAGER)
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
def get_session() -> Generator["Session", None, None]:
|
||||||
db_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
|
db_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
|
||||||
yield from db_manager.get_session()
|
yield from db_manager.get_session()
|
||||||
|
|
||||||
|
|
||||||
|
def get_chat_manager() -> "ChatManager":
|
||||||
|
return service_manager.get(ServiceType.CHAT_MANAGER)
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,35 @@
|
||||||
import logging
|
from typing import Optional
|
||||||
|
from loguru import logger
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
|
|
||||||
logger = logging.getLogger("langflow")
|
|
||||||
|
|
||||||
|
def configure(log_level: str = "DEBUG", log_file: Optional[Path] = None):
|
||||||
|
log_format = "<green>{time:HH:mm:ss}</green> - <level>{level: <8}</level> - <level>{message}</level>"
|
||||||
|
logger.remove() # Remove default handlers
|
||||||
|
|
||||||
def configure(log_level: str = "DEBUG", log_file: Path = None): # type: ignore
|
# Configure loguru to use RichHandler
|
||||||
log_format = "%(asctime)s - %(levelname)s - %(message)s"
|
logger.configure(
|
||||||
log_level_value = getattr(logging, log_level.upper(), logging.INFO)
|
handlers=[
|
||||||
|
{
|
||||||
logging.basicConfig(
|
"sink": RichHandler(rich_tracebacks=True, markup=True),
|
||||||
level=log_level_value,
|
"format": log_format,
|
||||||
format=log_format,
|
"level": log_level.upper(),
|
||||||
datefmt="[%X]",
|
}
|
||||||
handlers=[RichHandler(rich_tracebacks=True)],
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
if log_file:
|
if log_file:
|
||||||
log_file = Path(log_file)
|
log_file = Path(log_file)
|
||||||
log_file.parent.mkdir(parents=True, exist_ok=True)
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
file_handler = logging.FileHandler(log_file)
|
logger.add(
|
||||||
file_handler.setFormatter(logging.Formatter(log_format))
|
sink=str(log_file),
|
||||||
logger.addHandler(file_handler)
|
level=log_level.upper(),
|
||||||
|
format=log_format,
|
||||||
|
rotation="10 MB", # Log rotation based on file size
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(f"Logger set up with log level: {log_level_value}({log_level})")
|
logger.info(f"Logger set up with log level: {log_level}")
|
||||||
if log_file:
|
if log_file:
|
||||||
logger.info(f"Log file: {log_file}")
|
logger.info(f"Log file: {log_file}")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { useState } from "react";
|
||||||
|
import { dropdownButtonPropsType } from "../../types/components";
|
||||||
|
import IconComponent from "../genericIconComponent";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../ui/dropdown-menu";
|
||||||
|
|
||||||
|
export default function DropdownButton({
|
||||||
|
firstButtonName,
|
||||||
|
onFirstBtnClick,
|
||||||
|
options,
|
||||||
|
}: dropdownButtonPropsType): JSX.Element {
|
||||||
|
const [showOptions, setShowOptions] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DropdownMenu open={showOptions}>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
className="relative pr-10"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
onFirstBtnClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{firstButtonName}
|
||||||
|
<div
|
||||||
|
className="absolute right-2 items-center text-muted-foreground"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
setShowOptions(!showOptions);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!showOptions ? (
|
||||||
|
<IconComponent name="ChevronDown" />
|
||||||
|
) : (
|
||||||
|
<IconComponent name="ChevronUp" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent
|
||||||
|
onPointerDownOutside={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
setShowOptions(!showOptions);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{options.map(({ name, onBtnClick }, index) => (
|
||||||
|
<DropdownMenuItem onClick={onBtnClick} key={index}>
|
||||||
|
{name}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,6 @@ import {
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "../../components/ui/tabs";
|
} from "../../components/ui/tabs";
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { typesContext } from "../../contexts/typesContext";
|
import { typesContext } from "../../contexts/typesContext";
|
||||||
import { codeTabsPropsType } from "../../types/components";
|
import { codeTabsPropsType } from "../../types/components";
|
||||||
|
|
@ -57,7 +56,7 @@ export default function CodeTabsComponent({
|
||||||
}, [flow]);
|
}, [flow]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(tweaks){
|
if (tweaks) {
|
||||||
unselectAllNodes({
|
unselectAllNodes({
|
||||||
data,
|
data,
|
||||||
updateNodes: (nodes) => {
|
updateNodes: (nodes) => {
|
||||||
|
|
@ -181,7 +180,7 @@ export default function CodeTabsComponent({
|
||||||
key={idx} // Remember to add a unique key prop
|
key={idx} // Remember to add a unique key prop
|
||||||
>
|
>
|
||||||
{idx < 4 ? (
|
{idx < 4 ? (
|
||||||
<>
|
<div className="w-full h-full flex flex-col">
|
||||||
{tab.description && (
|
{tab.description && (
|
||||||
<div
|
<div
|
||||||
className="mb-2 w-full text-left text-sm"
|
className="mb-2 w-full text-left text-sm"
|
||||||
|
|
@ -195,7 +194,7 @@ export default function CodeTabsComponent({
|
||||||
>
|
>
|
||||||
{tab.code}
|
{tab.code}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
</>
|
</div>
|
||||||
) : idx === 4 ? (
|
) : idx === 4 ? (
|
||||||
<>
|
<>
|
||||||
<div className="api-modal-according-display">
|
<div className="api-modal-according-display">
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,17 @@ import { alertContext } from "../../contexts/alertContext";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
import { gradients } from "../../utils/styleUtils";
|
||||||
import IconComponent from "../genericIconComponent";
|
import IconComponent from "../genericIconComponent";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../ui/dropdown-menu";
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import MenuBar from "./components/menuBar";
|
import MenuBar from "./components/menuBar";
|
||||||
|
|
||||||
|
|
@ -17,7 +26,7 @@ export default function Header(): JSX.Element {
|
||||||
const { dark, setDark } = useContext(darkContext);
|
const { dark, setDark } = useContext(darkContext);
|
||||||
const { notificationCenter } = useContext(alertContext);
|
const { notificationCenter } = useContext(alertContext);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { logout, autoLogin, isAdmin } = useContext(AuthContext);
|
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
|
||||||
const { stars } = useContext(darkContext);
|
const { stars } = useContext(darkContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -31,40 +40,6 @@ export default function Header(): JSX.Element {
|
||||||
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
||||||
<MenuBar flows={flows} tabId={tabId} />
|
<MenuBar flows={flows} tabId={tabId} />
|
||||||
)}
|
)}
|
||||||
{!autoLogin && location.pathname !== `/flow/${tabId}` && (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
logout();
|
|
||||||
navigate("/login");
|
|
||||||
}}
|
|
||||||
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer mx-5"
|
|
||||||
>
|
|
||||||
Sign out
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{location.pathname === "/admin" && (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
navigate("/");
|
|
||||||
}}
|
|
||||||
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer"
|
|
||||||
>
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isAdmin &&
|
|
||||||
!autoLogin &&
|
|
||||||
location.pathname !== "/admin" &&
|
|
||||||
location.pathname !== `/flow/${tabId}` && (
|
|
||||||
<a
|
|
||||||
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer"
|
|
||||||
onClick={() => navigate("/admin")}
|
|
||||||
>
|
|
||||||
Admin page
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="round-button-div">
|
<div className="round-button-div">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
|
|
@ -156,6 +131,44 @@ export default function Header(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{!autoLogin && (
|
||||||
|
<>
|
||||||
|
<Separator orientation="vertical" />
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||||
|
gradients[
|
||||||
|
parseInt(userData?.id ?? "", 10) % gradients.length
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
{isAdmin && (
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => navigate("/admin")}
|
||||||
|
>
|
||||||
|
Admin Page
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
<DropdownMenuItem
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
logout();
|
||||||
|
navigate("/login");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign Out
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export default function InputComponent({
|
||||||
{isForm ? (
|
{isForm ? (
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type={password && !pwdVisible ? "password" : "text"}
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
@ -53,6 +54,7 @@ export default function InputComponent({
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
|
type="text"
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
@ -76,6 +78,8 @@ export default function InputComponent({
|
||||||
)}
|
)}
|
||||||
{password && (
|
{password && (
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={-1}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
editNode
|
editNode
|
||||||
? "input-component-true-button"
|
? "input-component-true-button"
|
||||||
|
|
|
||||||
16
src/frontend/src/components/skeletonCardComponent/index.tsx
Normal file
16
src/frontend/src/components/skeletonCardComponent/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
|
export const SkeletonCardComponent = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="skeleton-card">
|
||||||
|
<div className="skeleton-card-wrapper">
|
||||||
|
<Skeleton className="h-8 w-8 rounded-full" />
|
||||||
|
<Skeleton className="h-4 w-[120px]" />
|
||||||
|
</div>
|
||||||
|
<div className="skeleton-card-text">
|
||||||
|
<Skeleton className="h-3 w-[250px]" />
|
||||||
|
<Skeleton className="h-3 w-[200px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef<
|
||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-blur-shared backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
|
"fixed top-0 right-0 left-0 bottom-0 overflow-auto inset-0 z-50 bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"noundo nocopy fixed z-50 flex w-full flex-col gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0",
|
"noundo nocopy flex text-start overflow-auto z-50 justify-center items-left flex-col w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-bottom-[48%] data-[state=open]:slide-in-from-bottom-[48%] sm:rounded-lg md:w-full",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
15
src/frontend/src/components/ui/skeleton.tsx
Normal file
15
src/frontend/src/components/ui/skeleton.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { cn } from "../../utils/utils";
|
||||||
|
|
||||||
|
function Skeleton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("animate-pulse rounded-md bg-border", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton };
|
||||||
|
|
@ -25,7 +25,7 @@ const initialValue: alertContextType = {
|
||||||
notificationList: [],
|
notificationList: [],
|
||||||
pushNotificationList: () => {},
|
pushNotificationList: () => {},
|
||||||
clearNotificationList: () => {},
|
clearNotificationList: () => {},
|
||||||
removeFromNotificationList: () => {}
|
removeFromNotificationList: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const alertContext = createContext<alertContextType>(initialValue);
|
export const alertContext = createContext<alertContextType>(initialValue);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ const TabsContextInitialValue: TabsContextType = {
|
||||||
save: () => {},
|
save: () => {},
|
||||||
tabId: "",
|
tabId: "",
|
||||||
setTabId: (index: string) => {},
|
setTabId: (index: string) => {},
|
||||||
|
isLoading: true,
|
||||||
flows: [],
|
flows: [],
|
||||||
removeFlow: (id: string) => {},
|
removeFlow: (id: string) => {},
|
||||||
addFlow: async (flowData?: any) => "",
|
addFlow: async (flowData?: any) => "",
|
||||||
|
|
@ -47,7 +48,7 @@ const TabsContextInitialValue: TabsContextType = {
|
||||||
downloadFlow: (flow: FlowType) => {},
|
downloadFlow: (flow: FlowType) => {},
|
||||||
downloadFlows: () => {},
|
downloadFlows: () => {},
|
||||||
uploadFlows: () => {},
|
uploadFlows: () => {},
|
||||||
uploadFlow: () => {},
|
uploadFlow: async () => "",
|
||||||
isBuilt: false,
|
isBuilt: false,
|
||||||
setIsBuilt: (state: boolean) => {},
|
setIsBuilt: (state: boolean) => {},
|
||||||
hardReset: () => {},
|
hardReset: () => {},
|
||||||
|
|
@ -72,10 +73,12 @@ export const TabsContext = createContext<TabsContextType>(
|
||||||
export function TabsProvider({ children }: { children: ReactNode }) {
|
export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const { setErrorData, setNoticeData, setSuccessData } =
|
const { setErrorData, setNoticeData, setSuccessData } =
|
||||||
useContext(alertContext);
|
useContext(alertContext);
|
||||||
const { getAuthentication } = useContext(AuthContext);
|
const { getAuthentication, isAuthenticated } = useContext(AuthContext);
|
||||||
|
|
||||||
const [tabId, setTabId] = useState("");
|
const [tabId, setTabId] = useState("");
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||||
const [id, setId] = useState(uid());
|
const [id, setId] = useState(uid());
|
||||||
const { templates, reactFlowInstance } = useContext(typesContext);
|
const { templates, reactFlowInstance } = useContext(typesContext);
|
||||||
|
|
@ -86,6 +89,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const [tabsState, setTabsState] = useState<TabsState>({});
|
const [tabsState, setTabsState] = useState<TabsState>({});
|
||||||
const [getTweak, setTweak] = useState<tweakType>([]);
|
const [getTweak, setTweak] = useState<tweakType>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
hardReset();
|
||||||
|
}
|
||||||
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
const newNodeId = useRef(uid());
|
const newNodeId = useRef(uid());
|
||||||
function incrementNodeId() {
|
function incrementNodeId() {
|
||||||
newNodeId.current = uid();
|
newNodeId.current = uid();
|
||||||
|
|
@ -116,11 +125,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshFlows() {
|
function refreshFlows() {
|
||||||
|
setIsLoading(true);
|
||||||
getTabsDataFromDB().then((DbData) => {
|
getTabsDataFromDB().then((DbData) => {
|
||||||
if (DbData && Object.keys(templates).length > 0) {
|
if (DbData && Object.keys(templates).length > 0) {
|
||||||
try {
|
try {
|
||||||
processDBData(DbData);
|
processDBData(DbData);
|
||||||
updateStateWithDbData(DbData);
|
updateStateWithDbData(DbData);
|
||||||
|
setIsLoading(false);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -229,6 +240,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
setTabId("");
|
setTabId("");
|
||||||
|
|
||||||
setFlows([]);
|
setFlows([]);
|
||||||
|
setIsLoading(true);
|
||||||
setId(uid());
|
setId(uid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,39 +298,40 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
* If the file type is application/json, the file is read and parsed into a JSON object.
|
* If the file type is application/json, the file is read and parsed into a JSON object.
|
||||||
* The resulting JSON object is passed to the addFlow function.
|
* The resulting JSON object is passed to the addFlow function.
|
||||||
*/
|
*/
|
||||||
function uploadFlow(newProject?: boolean, file?: File) {
|
async function uploadFlow(
|
||||||
|
newProject?: boolean,
|
||||||
|
file?: File
|
||||||
|
): Promise<String | undefined> {
|
||||||
|
let id;
|
||||||
if (file) {
|
if (file) {
|
||||||
file.text().then((text) => {
|
let text = await file.text();
|
||||||
// parse the text into a JSON object
|
// parse the text into a JSON object
|
||||||
let flow: FlowType = JSON.parse(text);
|
let flow: FlowType = JSON.parse(text);
|
||||||
|
|
||||||
addFlow(flow, newProject);
|
id = await addFlow(flow, newProject);
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// create a file input
|
// create a file input
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
input.accept = ".json";
|
input.accept = ".json";
|
||||||
// add a change event listener to the file input
|
// add a change event listener to the file input
|
||||||
input.onchange = (e: Event) => {
|
id = await new Promise((resolve) => {
|
||||||
// check if the file type is application/json
|
input.onchange = async (e: Event) => {
|
||||||
if (
|
if (
|
||||||
(e.target as HTMLInputElement).files![0].type === "application/json"
|
(e.target as HTMLInputElement).files![0].type === "application/json"
|
||||||
) {
|
) {
|
||||||
// get the file from the file input
|
const currentfile = (e.target as HTMLInputElement).files![0];
|
||||||
const currentfile = (e.target as HTMLInputElement).files![0];
|
let text = await currentfile.text();
|
||||||
// read the file as text
|
|
||||||
currentfile.text().then((text) => {
|
|
||||||
// parse the text into a JSON object
|
|
||||||
let flow: FlowType = JSON.parse(text);
|
let flow: FlowType = JSON.parse(text);
|
||||||
|
const flowId = await addFlow(flow, newProject);
|
||||||
addFlow(flow, newProject);
|
resolve(flowId);
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
};
|
// trigger the file input click event to open the file dialog
|
||||||
// trigger the file input click event to open the file dialog
|
input.click();
|
||||||
input.click();
|
});
|
||||||
}
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFlows() {
|
function uploadFlows() {
|
||||||
|
|
@ -641,6 +654,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
paste,
|
paste,
|
||||||
getTweak,
|
getTweak,
|
||||||
setTweak,
|
setTweak,
|
||||||
|
isLoading,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,7 @@ export async function readFlowsFromDatabase() {
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`${BASE_URL_API}flows/`);
|
const response = await api.get(`${BASE_URL_API}flows/`);
|
||||||
if (response?.status !== 200) {
|
if (response?.status !== 200) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -177,7 +177,7 @@ export async function downloadFlowsFromDatabase() {
|
||||||
try {
|
try {
|
||||||
const response = await api.get(`${BASE_URL_API}flows/download/`);
|
const response = await api.get(`${BASE_URL_API}flows/download/`);
|
||||||
if (response?.status !== 200) {
|
if (response?.status !== 200) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -190,8 +190,8 @@ export async function uploadFlowsToDatabase(flows: FormData) {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(`${BASE_URL_API}flows/upload/`, flows);
|
const response = await api.post(`${BASE_URL_API}flows/upload/`, flows);
|
||||||
|
|
||||||
if (response.status !== 201) {
|
if (response?.status !== 201) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
}
|
}
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -468,7 +468,7 @@ export async function updateUser(user_id: string, user: Users) {
|
||||||
|
|
||||||
export async function getApiKey() {
|
export async function getApiKey() {
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`${BASE_URL_API}api_key`);
|
const res = await api.get(`${BASE_URL_API}api_key/`);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
@ -480,7 +480,7 @@ export async function getApiKey() {
|
||||||
|
|
||||||
export async function createApiKey(name: string) {
|
export async function createApiKey(name: string) {
|
||||||
try {
|
try {
|
||||||
const res = await api.post(`${BASE_URL_API}api_key`, { name });
|
const res = await api.post(`${BASE_URL_API}api_key/`, { name });
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { classNames } from "../../utils/utils";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
import ChatMessage from "./chatMessage";
|
import ChatMessage from "./chatMessage";
|
||||||
|
|
||||||
import _, { set } from "lodash";
|
import _ from "lodash";
|
||||||
import AccordionComponent from "../../components/AccordionComponent";
|
import AccordionComponent from "../../components/AccordionComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||||
|
|
@ -25,9 +25,9 @@ import { Textarea } from "../../components/ui/textarea";
|
||||||
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
import { getBuildStatus } from "../../controllers/API";
|
||||||
import { TabsState } from "../../types/tabs";
|
import { TabsState } from "../../types/tabs";
|
||||||
import { validateNodes } from "../../utils/reactflowUtils";
|
import { validateNodes } from "../../utils/reactflowUtils";
|
||||||
import { getBuildStatus } from "../../controllers/API";
|
|
||||||
|
|
||||||
export default function FormModal({
|
export default function FormModal({
|
||||||
flow,
|
flow,
|
||||||
|
|
@ -156,19 +156,21 @@ export default function FormModal({
|
||||||
|
|
||||||
function handleOnClose(event: CloseEvent): void {
|
function handleOnClose(event: CloseEvent): void {
|
||||||
if (isOpen.current) {
|
if (isOpen.current) {
|
||||||
getBuildStatus(flow.id).then((response) => {
|
getBuildStatus(flow.id)
|
||||||
if (response.data.built) {
|
.then((response) => {
|
||||||
connectWS();
|
if (response.data.built) {
|
||||||
}
|
connectWS();
|
||||||
else {
|
} else {
|
||||||
|
setErrorData({
|
||||||
|
title: "Please build the flow again before using the chat.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Please build the flow again before using the chat."
|
title: error.data?.detail ? error.data.detail : error.message,
|
||||||
})
|
});
|
||||||
}
|
});
|
||||||
}).catch((error) => {
|
|
||||||
setErrorData({title:error.data?.detail?error.data.detail:error.message})
|
|
||||||
|
|
||||||
});
|
|
||||||
setErrorData({ title: event.reason });
|
setErrorData({ title: event.reason });
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLockChat(false);
|
setLockChat(false);
|
||||||
|
|
@ -186,8 +188,9 @@ export default function FormModal({
|
||||||
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
||||||
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
||||||
|
|
||||||
return `${isDevelopment ? "ws" : webSocketProtocol
|
return `${
|
||||||
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
isDevelopment ? "ws" : webSocketProtocol
|
||||||
|
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleWsMessage(data: any) {
|
function handleWsMessage(data: any) {
|
||||||
|
|
@ -209,20 +212,20 @@ export default function FormModal({
|
||||||
newChatHistory.push(
|
newChatHistory.push(
|
||||||
chatItem.files
|
chatItem.files
|
||||||
? {
|
? {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: chatItem.message,
|
message: chatItem.message,
|
||||||
template: chatItem.template,
|
template: chatItem.template,
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
files: chatItem.files,
|
files: chatItem.files,
|
||||||
chatKey: chatItem.chatKey,
|
chatKey: chatItem.chatKey,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: chatItem.message,
|
message: chatItem.message,
|
||||||
template: chatItem.template,
|
template: chatItem.template,
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
chatKey: chatItem.chatKey,
|
chatKey: chatItem.chatKey,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -442,73 +445,73 @@ export default function FormModal({
|
||||||
|
|
||||||
{tabsState[id.current]?.formKeysData?.input_keys
|
{tabsState[id.current]?.formKeysData?.input_keys
|
||||||
? Object.keys(
|
? Object.keys(
|
||||||
tabsState[id.current].formKeysData.input_keys!
|
tabsState[id.current].formKeysData.input_keys!
|
||||||
).map((key, index) => (
|
).map((key, index) => (
|
||||||
<div className="file-component-accordion-div" key={index}>
|
<div className="file-component-accordion-div" key={index}>
|
||||||
<AccordionComponent
|
<AccordionComponent
|
||||||
trigger={
|
trigger={
|
||||||
<div className="file-component-badge-div">
|
<div className="file-component-badge-div">
|
||||||
<Badge variant="gray" size="md">
|
<Badge variant="gray" size="md">
|
||||||
{key}
|
{key}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="-mb-1"
|
className="-mb-1"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ToggleShadComponent
|
<ToggleShadComponent
|
||||||
enabled={chatKey === key}
|
enabled={chatKey === key}
|
||||||
setEnabled={(value) =>
|
setEnabled={(value) =>
|
||||||
handleOnCheckedChange(value, key)
|
handleOnCheckedChange(value, key)
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
disabled={tabsState[
|
disabled={tabsState[
|
||||||
id.current
|
id.current
|
||||||
].formKeysData.handle_keys!.some(
|
].formKeysData.handle_keys!.some(
|
||||||
(t) => t === key
|
(t) => t === key
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
key={index}
|
||||||
key={index}
|
keyValue={key}
|
||||||
keyValue={key}
|
>
|
||||||
>
|
<div className="file-component-tab-column">
|
||||||
<div className="file-component-tab-column">
|
{tabsState[id.current].formKeysData.handle_keys!.some(
|
||||||
{tabsState[id.current].formKeysData.handle_keys!.some(
|
(t) => t === key
|
||||||
(t) => t === key
|
) && (
|
||||||
) && (
|
|
||||||
<div className="font-normal text-muted-foreground ">
|
<div className="font-normal text-muted-foreground ">
|
||||||
Source: Component
|
Source: Component
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Textarea
|
<Textarea
|
||||||
className="custom-scroll"
|
className="custom-scroll"
|
||||||
value={
|
value={
|
||||||
tabsState[id.current].formKeysData.input_keys![
|
tabsState[id.current].formKeysData.input_keys![
|
||||||
key
|
key
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
setTabsState((old: TabsState) => {
|
setTabsState((old: TabsState) => {
|
||||||
let newTabsState = _.cloneDeep(old);
|
let newTabsState = _.cloneDeep(old);
|
||||||
newTabsState[
|
newTabsState[
|
||||||
id.current
|
id.current
|
||||||
].formKeysData.input_keys![key] =
|
].formKeysData.input_keys![key] =
|
||||||
e.target.value;
|
e.target.value;
|
||||||
return newTabsState;
|
return newTabsState;
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
disabled={chatKey === key}
|
disabled={chatKey === key}
|
||||||
placeholder="Enter text..."
|
placeholder="Enter text..."
|
||||||
></Textarea>
|
></Textarea>
|
||||||
</div>
|
</div>
|
||||||
</AccordionComponent>
|
</AccordionComponent>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
: null}
|
: null}
|
||||||
{tabsState[id.current].formKeysData.memory_keys!.map(
|
{tabsState[id.current].formKeysData.memory_keys!.map(
|
||||||
(key, index) => (
|
(key, index) => (
|
||||||
|
|
@ -522,7 +525,7 @@ export default function FormModal({
|
||||||
<div className="-mb-1">
|
<div className="-mb-1">
|
||||||
<ToggleShadComponent
|
<ToggleShadComponent
|
||||||
enabled={chatKey === key}
|
enabled={chatKey === key}
|
||||||
setEnabled={() => { }}
|
setEnabled={() => {}}
|
||||||
size="small"
|
size="small"
|
||||||
disabled={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,11 @@ export default function GenericModal({
|
||||||
setSuccessData({
|
setSuccessData({
|
||||||
title: "Prompt is ready",
|
title: "Prompt is ready",
|
||||||
});
|
});
|
||||||
if(JSON.stringify(apiReturn.data?.frontend_node)!==JSON.stringify({})) setNodeClass!(apiReturn.data?.frontend_node);
|
if (
|
||||||
|
JSON.stringify(apiReturn.data?.frontend_node) !==
|
||||||
|
JSON.stringify({})
|
||||||
|
)
|
||||||
|
setNodeClass!(apiReturn.data?.frontend_node);
|
||||||
setModalOpen(closeModal);
|
setModalOpen(closeModal);
|
||||||
setValue(inputValue);
|
setValue(inputValue);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import PaginatorComponent from "../../components/PaginatorComponent";
|
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
import Header from "../../components/headerComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { Checkbox } from "../../components/ui/checkbox";
|
import { Checkbox } from "../../components/ui/checkbox";
|
||||||
import { Input } from "../../components/ui/input";
|
import { Input } from "../../components/ui/input";
|
||||||
|
|
@ -25,9 +26,8 @@ import {
|
||||||
} from "../../controllers/API";
|
} from "../../controllers/API";
|
||||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||||
import UserManagementModal from "../../modals/UserManagementModal";
|
import UserManagementModal from "../../modals/UserManagementModal";
|
||||||
import { UserInputType } from "../../types/components";
|
|
||||||
import Header from "../../components/headerComponent";
|
|
||||||
import { Users } from "../../types/api";
|
import { Users } from "../../types/api";
|
||||||
|
import { UserInputType } from "../../types/components";
|
||||||
|
|
||||||
export default function AdminPage() {
|
export default function AdminPage() {
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
|
@ -89,7 +89,7 @@ export default function AdminPage() {
|
||||||
if (input === "") {
|
if (input === "") {
|
||||||
setFilterUserList(userList.current);
|
setFilterUserList(userList.current);
|
||||||
} else {
|
} else {
|
||||||
const filteredList = userList.current.filter((user:Users) =>
|
const filteredList = userList.current.filter((user: Users) =>
|
||||||
user.username.toLowerCase().includes(input.toLowerCase())
|
user.username.toLowerCase().includes(input.toLowerCase())
|
||||||
);
|
);
|
||||||
setFilterUserList(filteredList);
|
setFilterUserList(filteredList);
|
||||||
|
|
@ -183,249 +183,254 @@ export default function AdminPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
{userData && (
|
{userData && (
|
||||||
<div className="main-page-panel">
|
<div className="main-page-panel">
|
||||||
<div className="m-auto flex h-full flex-row justify-center">
|
<div className="m-auto flex h-full flex-row justify-center">
|
||||||
<div className="basis-5/6">
|
<div className="basis-5/6">
|
||||||
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
||||||
<div className="flex items-center justify-between space-y-2">
|
<div className="flex items-center justify-between space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold tracking-tight">
|
<h2 className="text-2xl font-bold tracking-tight">
|
||||||
Welcome back!
|
Welcome back!
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Navigate through this section to efficiently oversee all
|
Navigate through this section to efficiently oversee all
|
||||||
application users. From here, you can seamlessly manage
|
application users. From here, you can seamlessly manage
|
||||||
user accounts.
|
user accounts.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{userList.current.length === 0 && !loadingUsers && (
|
{userList.current.length === 0 && !loadingUsers && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<h2>There's no users registered :)</h2>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h2>There's no users registered :)</h2>
|
<div className="flex flex-1 items-center space-x-2">
|
||||||
</div>
|
<Input
|
||||||
</>
|
value={inputValue}
|
||||||
)}
|
placeholder="Filter users..."
|
||||||
<>
|
className="h-8 w-[150px] lg:w-[250px]"
|
||||||
<div className="flex items-center justify-between">
|
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||||
<div className="flex flex-1 items-center space-x-2">
|
/>
|
||||||
<Input
|
{inputValue.length > 0 && (
|
||||||
value={inputValue}
|
<Button
|
||||||
placeholder="Filter users..."
|
onClick={() => {
|
||||||
className="h-8 w-[150px] lg:w-[250px]"
|
setInputValue("");
|
||||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
setFilterUserList(userList.current);
|
||||||
/>
|
}}
|
||||||
{inputValue.length > 0 && (
|
variant="ghost"
|
||||||
<Button
|
className="h-8 px-2 lg:px-3"
|
||||||
onClick={() => {
|
>
|
||||||
setInputValue("");
|
Reset
|
||||||
setFilterUserList(userList.current);
|
<X className="ml-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<UserManagementModal
|
||||||
|
title="New User"
|
||||||
|
titleHeader={"Add a new user"}
|
||||||
|
cancelText="Cancel"
|
||||||
|
confirmationText="Save"
|
||||||
|
icon={"UserPlus2"}
|
||||||
|
onConfirm={(index, user) => {
|
||||||
|
handleNewUser(user);
|
||||||
}}
|
}}
|
||||||
variant="ghost"
|
|
||||||
className="h-8 px-2 lg:px-3"
|
|
||||||
>
|
>
|
||||||
Reset
|
<Button>New User</Button>
|
||||||
<X className="ml-2 h-4 w-4" />
|
</UserManagementModal>
|
||||||
</Button>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{loadingUsers && (
|
||||||
<UserManagementModal
|
<div>
|
||||||
title="New User"
|
<strong>Loading...</strong>
|
||||||
titleHeader={"Add a new user"}
|
</div>
|
||||||
cancelText="Cancel"
|
)}
|
||||||
confirmationText="Save"
|
<div
|
||||||
icon={"UserPlus2"}
|
className={
|
||||||
onConfirm={(index, user) => {
|
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||||
handleNewUser(user);
|
(loadingUsers ? " border-0" : "")
|
||||||
}}
|
}
|
||||||
>
|
>
|
||||||
<Button>New User</Button>
|
<Table className={"table-fixed bg-muted outline-1"}>
|
||||||
</UserManagementModal>
|
<TableHeader
|
||||||
</div>
|
className={
|
||||||
</div>
|
loadingUsers
|
||||||
{loadingUsers && (
|
? "hidden"
|
||||||
<div>
|
: "table-fixed bg-muted outline-1"
|
||||||
<strong>Loading...</strong>
|
}
|
||||||
</div>
|
>
|
||||||
)}
|
<TableRow>
|
||||||
<div
|
<TableHead className="h-10">Id</TableHead>
|
||||||
className={
|
<TableHead className="h-10">Username</TableHead>
|
||||||
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
<TableHead className="h-10">Active</TableHead>
|
||||||
(loadingUsers ? " border-0" : "")
|
<TableHead className="h-10">Superuser</TableHead>
|
||||||
}
|
<TableHead className="h-10">Created At</TableHead>
|
||||||
>
|
<TableHead className="h-10">Updated At</TableHead>
|
||||||
<Table className={"table-fixed bg-muted outline-1"}>
|
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||||
<TableHeader
|
</TableRow>
|
||||||
className={
|
</TableHeader>
|
||||||
loadingUsers
|
{!loadingUsers && (
|
||||||
? "hidden"
|
<TableBody>
|
||||||
: "table-fixed bg-muted outline-1"
|
{filterUserList.map(
|
||||||
}
|
(user: UserInputType, index) => (
|
||||||
>
|
<TableRow key={index}>
|
||||||
<TableRow>
|
<TableCell className="truncate py-2 font-medium">
|
||||||
<TableHead className="h-10">Id</TableHead>
|
<ShadTooltip content={user.id}>
|
||||||
<TableHead className="h-10">Username</TableHead>
|
<span className="cursor-default">
|
||||||
<TableHead className="h-10">Active</TableHead>
|
{user.id}
|
||||||
<TableHead className="h-10">Superuser</TableHead>
|
</span>
|
||||||
<TableHead className="h-10">Created At</TableHead>
|
|
||||||
<TableHead className="h-10">Updated At</TableHead>
|
|
||||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
{!loadingUsers && (
|
|
||||||
<TableBody>
|
|
||||||
{filterUserList.map((user:UserInputType, index) => (
|
|
||||||
<TableRow key={index}>
|
|
||||||
<TableCell className="truncate py-2 font-medium">
|
|
||||||
<ShadTooltip content={user.id}>
|
|
||||||
<span className="cursor-default">
|
|
||||||
{user.id}
|
|
||||||
</span>
|
|
||||||
</ShadTooltip>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="truncate py-2">
|
|
||||||
<ShadTooltip content={user.username}>
|
|
||||||
<span className="cursor-default">
|
|
||||||
{user.username}
|
|
||||||
</span>
|
|
||||||
</ShadTooltip>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
|
||||||
<ConfirmationModal
|
|
||||||
title="Edit"
|
|
||||||
titleHeader={`${user.username}`}
|
|
||||||
modalContentTitle="Attention!"
|
|
||||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
|
||||||
cancelText="Cancel"
|
|
||||||
confirmationText="Confirm"
|
|
||||||
icon={"UserCog2"}
|
|
||||||
data={user}
|
|
||||||
index={index}
|
|
||||||
onConfirm={(index, user) => {
|
|
||||||
handleDisableUser(
|
|
||||||
user.is_active,
|
|
||||||
user.id,
|
|
||||||
user
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
id="is_active"
|
|
||||||
checked={user.is_active}
|
|
||||||
/>
|
|
||||||
</ConfirmationModal>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
|
||||||
<ConfirmationModal
|
|
||||||
title="Edit"
|
|
||||||
titleHeader={`${user.username}`}
|
|
||||||
modalContentTitle="Attention!"
|
|
||||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
|
||||||
cancelText="Cancel"
|
|
||||||
confirmationText="Confirm"
|
|
||||||
icon={"UserCog2"}
|
|
||||||
data={user}
|
|
||||||
index={index}
|
|
||||||
onConfirm={(index, user) => {
|
|
||||||
handleSuperUserEdit(
|
|
||||||
user.is_superuser,
|
|
||||||
user.id,
|
|
||||||
user
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
id="is_superuser"
|
|
||||||
checked={user.is_superuser}
|
|
||||||
/>
|
|
||||||
</ConfirmationModal>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="truncate py-2 ">
|
|
||||||
{
|
|
||||||
new Date(user.create_at!)
|
|
||||||
.toISOString()
|
|
||||||
.split("T")[0]
|
|
||||||
}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="truncate py-2">
|
|
||||||
{
|
|
||||||
new Date(user.updated_at!)
|
|
||||||
.toISOString()
|
|
||||||
.split("T")[0]
|
|
||||||
}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="flex w-[100px] py-2 text-right">
|
|
||||||
<div className="flex">
|
|
||||||
<UserManagementModal
|
|
||||||
title="Edit"
|
|
||||||
titleHeader={`${user.id}`}
|
|
||||||
cancelText="Cancel"
|
|
||||||
confirmationText="Save"
|
|
||||||
icon={"UserPlus2"}
|
|
||||||
data={user}
|
|
||||||
index={index}
|
|
||||||
onConfirm={(index, editUser) => {
|
|
||||||
handleEditUser(user.id, editUser);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ShadTooltip content="Edit" side="top">
|
|
||||||
<IconComponent
|
|
||||||
name="Pencil"
|
|
||||||
className="h-4 w-4 cursor-pointer"
|
|
||||||
/>
|
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</UserManagementModal>
|
</TableCell>
|
||||||
|
<TableCell className="truncate py-2">
|
||||||
<ConfirmationModal
|
<ShadTooltip content={user.username}>
|
||||||
title="Delete"
|
<span className="cursor-default">
|
||||||
titleHeader="Delete User"
|
{user.username}
|
||||||
modalContentTitle="Attention!"
|
</span>
|
||||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
|
||||||
cancelText="Cancel"
|
|
||||||
confirmationText="Delete"
|
|
||||||
icon={"UserMinus2"}
|
|
||||||
data={user}
|
|
||||||
index={index}
|
|
||||||
onConfirm={(index, user) => {
|
|
||||||
handleDeleteUser(user);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ShadTooltip content="Delete" side="top">
|
|
||||||
<IconComponent
|
|
||||||
name="Trash2"
|
|
||||||
className="ml-2 h-4 w-4 cursor-pointer"
|
|
||||||
/>
|
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
</ConfirmationModal>
|
</TableCell>
|
||||||
</div>
|
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||||
</TableCell>
|
<ConfirmationModal
|
||||||
</TableRow>
|
title="Edit"
|
||||||
))}
|
titleHeader={`${user.username}`}
|
||||||
</TableBody>
|
modalContentTitle="Attention!"
|
||||||
)}
|
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||||
</Table>
|
cancelText="Cancel"
|
||||||
</div>
|
confirmationText="Confirm"
|
||||||
|
icon={"UserCog2"}
|
||||||
|
data={user}
|
||||||
|
index={index}
|
||||||
|
onConfirm={(index, user) => {
|
||||||
|
handleDisableUser(
|
||||||
|
user.is_active,
|
||||||
|
user.id,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id="is_active"
|
||||||
|
checked={user.is_active}
|
||||||
|
/>
|
||||||
|
</ConfirmationModal>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||||
|
<ConfirmationModal
|
||||||
|
title="Edit"
|
||||||
|
titleHeader={`${user.username}`}
|
||||||
|
modalContentTitle="Attention!"
|
||||||
|
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||||
|
cancelText="Cancel"
|
||||||
|
confirmationText="Confirm"
|
||||||
|
icon={"UserCog2"}
|
||||||
|
data={user}
|
||||||
|
index={index}
|
||||||
|
onConfirm={(index, user) => {
|
||||||
|
handleSuperUserEdit(
|
||||||
|
user.is_superuser,
|
||||||
|
user.id,
|
||||||
|
user
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
id="is_superuser"
|
||||||
|
checked={user.is_superuser}
|
||||||
|
/>
|
||||||
|
</ConfirmationModal>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="truncate py-2 ">
|
||||||
|
{
|
||||||
|
new Date(user.create_at!)
|
||||||
|
.toISOString()
|
||||||
|
.split("T")[0]
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="truncate py-2">
|
||||||
|
{
|
||||||
|
new Date(user.updated_at!)
|
||||||
|
.toISOString()
|
||||||
|
.split("T")[0]
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex w-[100px] py-2 text-right">
|
||||||
|
<div className="flex">
|
||||||
|
<UserManagementModal
|
||||||
|
title="Edit"
|
||||||
|
titleHeader={`${user.id}`}
|
||||||
|
cancelText="Cancel"
|
||||||
|
confirmationText="Save"
|
||||||
|
icon={"UserPlus2"}
|
||||||
|
data={user}
|
||||||
|
index={index}
|
||||||
|
onConfirm={(index, editUser) => {
|
||||||
|
handleEditUser(user.id, editUser);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ShadTooltip content="Edit" side="top">
|
||||||
|
<IconComponent
|
||||||
|
name="Pencil"
|
||||||
|
className="h-4 w-4 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</ShadTooltip>
|
||||||
|
</UserManagementModal>
|
||||||
|
|
||||||
<PaginatorComponent
|
<ConfirmationModal
|
||||||
pageIndex={index}
|
title="Delete"
|
||||||
pageSize={size}
|
titleHeader="Delete User"
|
||||||
totalRowsCount={totalRowsCount}
|
modalContentTitle="Attention!"
|
||||||
paginate={(pageIndex, pageSize) => {
|
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||||
handleChangePagination(pageSize, pageIndex);
|
cancelText="Cancel"
|
||||||
}}
|
confirmationText="Delete"
|
||||||
></PaginatorComponent>
|
icon={"UserMinus2"}
|
||||||
</>
|
data={user}
|
||||||
|
index={index}
|
||||||
|
onConfirm={(index, user) => {
|
||||||
|
handleDeleteUser(user);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ShadTooltip
|
||||||
|
content="Delete"
|
||||||
|
side="top"
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="Trash2"
|
||||||
|
className="ml-2 h-4 w-4 cursor-pointer"
|
||||||
|
/>
|
||||||
|
</ShadTooltip>
|
||||||
|
</ConfirmationModal>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PaginatorComponent
|
||||||
|
pageIndex={index}
|
||||||
|
pageSize={size}
|
||||||
|
totalRowsCount={totalRowsCount}
|
||||||
|
paginate={(pageIndex, pageSize) => {
|
||||||
|
handleChangePagination(pageSize, pageIndex);
|
||||||
|
}}
|
||||||
|
></PaginatorComponent>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ export default function Page({
|
||||||
zoomOnScroll={!view}
|
zoomOnScroll={!view}
|
||||||
zoomOnPinch={!view}
|
zoomOnPinch={!view}
|
||||||
panOnDrag={!view}
|
panOnDrag={!view}
|
||||||
proOptions={{hideAttribution: true}}
|
proOptions={{ hideAttribution: true }}
|
||||||
>
|
>
|
||||||
<Background className="" />
|
<Background className="" />
|
||||||
{!view && (
|
{!view && (
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
const { data, templates } = useContext(typesContext);
|
const { data, templates } = useContext(typesContext);
|
||||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||||
useContext(TabsContext);
|
useContext(TabsContext);
|
||||||
const { setSuccessData, setErrorData } =
|
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||||
useContext(alertContext);
|
|
||||||
const [dataFilter, setFilterData] = useState(data);
|
const [dataFilter, setFilterData] = useState(data);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const isPending = tabsState[tabId]?.isPending;
|
const isPending = tabsState[tabId]?.isPending;
|
||||||
|
|
@ -101,9 +100,7 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
<div className="side-bar-button">
|
<div className="side-bar-button">
|
||||||
{flow && flow.data && (
|
{flow && flow.data && (
|
||||||
<ApiModal flow={flow} disable={!isBuilt}>
|
<ApiModal flow={flow} disable={!isBuilt}>
|
||||||
<div
|
<div className={classNames("extra-side-bar-buttons")}>
|
||||||
className={classNames("extra-side-bar-buttons")}
|
|
||||||
>
|
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Code2"
|
name="Code2"
|
||||||
className={
|
className={
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,33 @@
|
||||||
import { useContext, useEffect } from "react";
|
import { useContext, useEffect } from "react";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
|
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||||
import { CardComponent } from "../../components/cardComponent";
|
import { CardComponent } from "../../components/cardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import Header from "../../components/headerComponent";
|
import Header from "../../components/headerComponent";
|
||||||
|
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
export default function HomePage(): JSX.Element {
|
export default function HomePage(): JSX.Element {
|
||||||
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } =
|
const {
|
||||||
useContext(TabsContext);
|
flows,
|
||||||
|
setTabId,
|
||||||
|
downloadFlows,
|
||||||
|
uploadFlows,
|
||||||
|
addFlow,
|
||||||
|
removeFlow,
|
||||||
|
uploadFlow,
|
||||||
|
isLoading,
|
||||||
|
} = useContext(TabsContext);
|
||||||
|
const dropdownOptions = [
|
||||||
|
{
|
||||||
|
name: "Import from JSON",
|
||||||
|
onBtnClick: () =>
|
||||||
|
uploadFlow(true).then((id) => {
|
||||||
|
navigate("/flow/" + id);
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// Set a null id
|
// Set a null id
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -16,6 +35,10 @@ export default function HomePage(): JSX.Element {
|
||||||
}, []);
|
}, []);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(isLoading);
|
||||||
|
}, [isLoading]);
|
||||||
|
|
||||||
// Personal flows display
|
// Personal flows display
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -45,48 +68,55 @@ export default function HomePage(): JSX.Element {
|
||||||
<IconComponent name="Upload" className="main-page-nav-button" />
|
<IconComponent name="Upload" className="main-page-nav-button" />
|
||||||
Upload Collection
|
Upload Collection
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<DropdownButton
|
||||||
variant="primary"
|
firstButtonName="New Project"
|
||||||
onClick={() => {
|
onFirstBtnClick={() => {
|
||||||
addFlow(null!, true).then((id) => {
|
addFlow(null!, true).then((id) => {
|
||||||
navigate("/flow/" + id);
|
navigate("/flow/" + id);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
options={dropdownOptions}
|
||||||
<IconComponent name="Plus" className="main-page-nav-button" />
|
/>
|
||||||
New Project
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="main-page-description-text">
|
<span className="main-page-description-text">
|
||||||
Manage your personal projects. Download or upload your collection.
|
Manage your personal projects. Download or upload your collection.
|
||||||
</span>
|
</span>
|
||||||
<div className="main-page-flows-display">
|
<div className="main-page-flows-display">
|
||||||
{flows.map((flow, idx) => (
|
{isLoading && flows.length == 0 ? (
|
||||||
<CardComponent
|
<>
|
||||||
key={idx}
|
<SkeletonCardComponent />
|
||||||
flow={flow}
|
<SkeletonCardComponent />
|
||||||
id={flow.id}
|
<SkeletonCardComponent />
|
||||||
button={
|
<SkeletonCardComponent />
|
||||||
<Link to={"/flow/" + flow.id}>
|
</>
|
||||||
<Button
|
) : (
|
||||||
variant="outline"
|
flows.map((flow, idx) => (
|
||||||
size="sm"
|
<CardComponent
|
||||||
className="whitespace-nowrap "
|
key={idx}
|
||||||
>
|
flow={flow}
|
||||||
<IconComponent
|
id={flow.id}
|
||||||
name="ExternalLink"
|
button={
|
||||||
className="main-page-nav-button"
|
<Link to={"/flow/" + flow.id}>
|
||||||
/>
|
<Button
|
||||||
Edit Flow
|
variant="outline"
|
||||||
</Button>
|
size="sm"
|
||||||
</Link>
|
className="whitespace-nowrap "
|
||||||
}
|
>
|
||||||
onDelete={() => {
|
<IconComponent
|
||||||
removeFlow(flow.id);
|
name="ExternalLink"
|
||||||
}}
|
className="main-page-nav-button"
|
||||||
/>
|
/>
|
||||||
))}
|
Edit Flow
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
onDelete={() => {
|
||||||
|
removeFlow(flow.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,8 @@ export default function LoginPage(): JSX.Element {
|
||||||
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
|
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
|
||||||
|
|
||||||
const { password, username } = inputState;
|
const { password, username } = inputState;
|
||||||
const { login, getAuthentication, setUserData, setIsAdmin } = useContext(AuthContext);
|
const { login, getAuthentication, setUserData, setIsAdmin } =
|
||||||
|
useContext(AuthContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setErrorData } = useContext(alertContext);
|
const { setErrorData } = useContext(alertContext);
|
||||||
|
|
||||||
|
|
@ -31,8 +32,8 @@ export default function LoginPage(): JSX.Element {
|
||||||
|
|
||||||
function signIn() {
|
function signIn() {
|
||||||
const user: LoginType = {
|
const user: LoginType = {
|
||||||
username: username,
|
username: username.trim(),
|
||||||
password: password,
|
password: password.trim(),
|
||||||
};
|
};
|
||||||
onLogin(user)
|
onLogin(user)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
|
|
@ -89,6 +90,7 @@ export default function LoginPage(): JSX.Element {
|
||||||
|
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type="username"
|
||||||
onChange={({ target: { value } }) => {
|
onChange={({ target: { value } }) => {
|
||||||
handleInput({ target: { name: "username", value } });
|
handleInput({ target: { name: "username", value } });
|
||||||
}}
|
}}
|
||||||
|
|
@ -129,12 +131,14 @@ export default function LoginPage(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Form.Submit asChild>
|
<Form.Submit asChild>
|
||||||
<Button className="mr-3 mt-6 w-full">Sign in</Button>
|
<Button className="mr-3 mt-6 w-full" type="submit">
|
||||||
|
Sign in
|
||||||
|
</Button>
|
||||||
</Form.Submit>
|
</Form.Submit>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Link to="/signup">
|
<Link to="/signup">
|
||||||
<Button className="w-full" variant="outline">
|
<Button className="w-full" variant="outline" type="button">
|
||||||
Don't have an account? <b>Sign Up</b>
|
Don't have an account? <b>Sign Up</b>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ export default function SignUp(): JSX.Element {
|
||||||
function handleSignup(): void {
|
function handleSignup(): void {
|
||||||
const { username, password } = inputState;
|
const { username, password } = inputState;
|
||||||
const newUser: UserInputType = {
|
const newUser: UserInputType = {
|
||||||
username,
|
username: username.trim(),
|
||||||
password,
|
password: password.trim(),
|
||||||
};
|
};
|
||||||
addUser(newUser)
|
addUser(newUser)
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
|
|
@ -84,6 +84,7 @@ export default function SignUp(): JSX.Element {
|
||||||
|
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type="username"
|
||||||
onChange={({ target: { value } }) => {
|
onChange={({ target: { value } }) => {
|
||||||
handleInput({ target: { name: "username", value } });
|
handleInput({ target: { name: "username", value } });
|
||||||
}}
|
}}
|
||||||
|
|
@ -157,6 +158,7 @@ export default function SignUp(): JSX.Element {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Form.Submit asChild>
|
<Form.Submit asChild>
|
||||||
<Button
|
<Button
|
||||||
|
type="submit"
|
||||||
className="mr-3 mt-6 w-full"
|
className="mr-3 mt-6 w-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSignup();
|
handleSignup();
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,18 @@
|
||||||
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton-card {
|
||||||
|
@apply bg-background h-48 p-4 border rounded-lg flex flex-col gap-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-card-wrapper {
|
||||||
|
@apply flex items-center space-x-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-card-text {
|
||||||
|
@apply flex flex-col gap-3;
|
||||||
|
}
|
||||||
|
|
||||||
/* The same as primary-input but no-truncate */
|
/* The same as primary-input but no-truncate */
|
||||||
.textarea-primary {
|
.textarea-primary {
|
||||||
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,18 @@ pre {
|
||||||
.gradient-start {
|
.gradient-start {
|
||||||
animation: gradient-motion-start 4s infinite forwards;
|
animation: gradient-motion-start 4s infinite forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
textarea:-webkit-autofill,
|
||||||
|
textarea:-webkit-autofill:hover,
|
||||||
|
textarea:-webkit-autofill:focus,
|
||||||
|
select:-webkit-autofill,
|
||||||
|
select:-webkit-autofill:hover,
|
||||||
|
select:-webkit-autofill:focus {
|
||||||
|
-webkit-text-fill-color: black;
|
||||||
|
-webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||||
|
box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
@ -283,7 +283,7 @@ export type UserInputType = {
|
||||||
is_superuser?: boolean;
|
is_superuser?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
create_at?: string;
|
create_at?: string;
|
||||||
updated_at?:string;
|
updated_at?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiKeyType = {
|
export type ApiKeyType = {
|
||||||
|
|
@ -559,3 +559,9 @@ export type fetchErrorComponentType = {
|
||||||
message: string;
|
message: string;
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type dropdownButtonPropsType = {
|
||||||
|
firstButtonName: string;
|
||||||
|
onFirstBtnClick: () => void;
|
||||||
|
options: Array<{ name: string; onBtnClick: () => void }>;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export type TabsContextType = {
|
||||||
saveFlow: (flow: FlowType) => Promise<void>;
|
saveFlow: (flow: FlowType) => Promise<void>;
|
||||||
save: () => void;
|
save: () => void;
|
||||||
tabId: string;
|
tabId: string;
|
||||||
|
isLoading: boolean;
|
||||||
setTabId: (index: string) => void;
|
setTabId: (index: string) => void;
|
||||||
flows: Array<FlowType>;
|
flows: Array<FlowType>;
|
||||||
removeFlow: (id: string) => void;
|
removeFlow: (id: string) => void;
|
||||||
|
|
@ -23,7 +24,7 @@ export type TabsContextType = {
|
||||||
uploadFlows: () => void;
|
uploadFlows: () => void;
|
||||||
isBuilt: boolean;
|
isBuilt: boolean;
|
||||||
setIsBuilt: (state: boolean) => void;
|
setIsBuilt: (state: boolean) => void;
|
||||||
uploadFlow: (newFlow?: boolean, file?: File) => void;
|
uploadFlow: (newFlow?: boolean, file?: File) => Promise<String | undefined>;
|
||||||
hardReset: () => void;
|
hardReset: () => void;
|
||||||
getNodeId: (nodeType: string) => string;
|
getNodeId: (nodeType: string) => string;
|
||||||
tabsState: TabsState;
|
tabsState: TabsState;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
ChevronUp,
|
||||||
ChevronsLeft,
|
ChevronsLeft,
|
||||||
ChevronsRight,
|
ChevronsRight,
|
||||||
ChevronsUpDown,
|
ChevronsUpDown,
|
||||||
|
|
@ -301,5 +302,6 @@ export const nodeIconsLucide: iconsType = {
|
||||||
UserCog2,
|
UserCog2,
|
||||||
Key,
|
Key,
|
||||||
Unplug,
|
Unplug,
|
||||||
BookMarked
|
BookMarked,
|
||||||
|
ChevronUp
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,13 @@ from langflow.services.database.models.user import UserUpdate
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def super_user(client, session):
|
def super_user(client, session):
|
||||||
return create_super_user(session)
|
settings_manager = get_settings_manager()
|
||||||
|
auth_settings = settings_manager.auth_settings
|
||||||
|
return create_super_user(
|
||||||
|
db=session,
|
||||||
|
username=auth_settings.FIRST_SUPERUSER,
|
||||||
|
password=auth_settings.FIRST_SUPERUSER_PASSWORD,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue