Merge remote-tracking branch 'origin/dev' into fix/dev_bugs
This commit is contained in:
commit
b0eed97964
26 changed files with 234 additions and 122 deletions
20
.github/workflows/docker-build.yml
vendored
20
.github/workflows/docker-build.yml
vendored
|
|
@ -26,18 +26,24 @@ jobs:
|
|||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
base_tags: ${{ steps.set-vars.outputs.base_tags }}
|
||||
main_tags: ${{ steps.set-vars.outputs.main_tags }}
|
||||
tags: ${{ steps.set-vars.outputs.tags }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Dockerfile and Tags
|
||||
id: set-vars
|
||||
run: |
|
||||
echo "base_tags=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_OUTPUT
|
||||
echo "main_tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ inputs.release_type }}" == "base" ]]; then
|
||||
echo "tags=langflowai/langflow:base-${{ inputs.version }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "tags=langflowai/langflow:${{ inputs.version }},langflowai/langflow:1.0-alpha" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
build_base:
|
||||
runs-on: ubuntu-latest
|
||||
needs: setup
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux/amd64, linux/arm64/v8]
|
||||
file: [./docker/build_and_push.Dockerfile, ./docker/build_and_push_base.Dockerfile]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
|
|
@ -52,9 +58,9 @@ jobs:
|
|||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: "linux/amd64,linux/arm64/v8"
|
||||
file: ./docker/build_and_push_base.Dockerfile
|
||||
tags: ${{ needs.setup.outputs.base_tags }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
file: ${{ matrix.file }}
|
||||
tags: ${{ needs.setup.outputs.tags }}
|
||||
|
||||
build_components:
|
||||
if: ${{ inputs.release_type == 'main' }}
|
||||
|
|
|
|||
2
.github/workflows/lint-js.yml
vendored
2
.github/workflows/lint-js.yml
vendored
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
if: ${{ steps.setup-node.outputs.cache-hit != 'true' }}
|
||||
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@v1
|
||||
uses: wearerequired/lint-action@v2
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
auto_fix: true
|
||||
|
|
|
|||
2
.github/workflows/lint-py.yml
vendored
2
.github/workflows/lint-py.yml
vendored
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
./.mypy_cache
|
||||
key: ${{ runner.os }}-mypy-${{ hashFiles('**/pyproject.toml') }}
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@v1
|
||||
uses: wearerequired/lint-action@v2
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
# Enable linters
|
||||
|
|
|
|||
|
|
@ -159,11 +159,11 @@ class CustomComponent(Component):
|
|||
if self.repr_value == "":
|
||||
self.repr_value = self.status
|
||||
if isinstance(self.repr_value, dict):
|
||||
return yaml.dump(self.repr_value)
|
||||
if isinstance(self.repr_value, str):
|
||||
return self.repr_value
|
||||
if isinstance(self.repr_value, BaseModel) and not isinstance(self.repr_value, Record):
|
||||
return str(self.repr_value)
|
||||
self.repr_value = yaml.dump(self.repr_value)
|
||||
if isinstance(self.repr_value, BaseModel) and not isinstance(self.repr_value, Data):
|
||||
self.repr_value = str(self.repr_value)
|
||||
elif hasattr(self.repr_value, "to_json"):
|
||||
self.repr_value = self.repr_value.to_json()
|
||||
return self.repr_value
|
||||
|
||||
def build_config(self):
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from langflow.initial_setup.setup import (
|
|||
load_flows_from_directory,
|
||||
)
|
||||
from langflow.interface.utils import setup_llm_caching
|
||||
from langflow.services.deps import get_settings_service
|
||||
from langflow.services.plugins.langfuse_plugin import LangfuseInstance
|
||||
from langflow.services.utils import initialize_services, teardown_services
|
||||
from langflow.utils.logger import configure
|
||||
|
|
@ -78,6 +79,7 @@ def create_app():
|
|||
socketio_server = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*", logger=True)
|
||||
lifespan = get_lifespan(socketio_server=socketio_server, version=__version__)
|
||||
app = FastAPI(lifespan=lifespan, title="Langflow", version=__version__)
|
||||
setup_sentry(app)
|
||||
origins = ["*"]
|
||||
|
||||
app.add_middleware(
|
||||
|
|
@ -115,6 +117,20 @@ def mount_socketio(app: FastAPI, socketio_server: socketio.AsyncServer):
|
|||
return app
|
||||
|
||||
|
||||
def setup_sentry(app: FastAPI):
|
||||
settings = get_settings_service().settings
|
||||
if settings.sentry_dsn:
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn=settings.sentry_dsn,
|
||||
traces_sample_rate=settings.sentry_traces_sample_rate,
|
||||
profiles_sample_rate=settings.sentry_profiles_sample_rate,
|
||||
)
|
||||
app.add_middleware(SentryAsgiMiddleware)
|
||||
|
||||
|
||||
def setup_static_files(app: FastAPI, static_files_dir: Path):
|
||||
"""
|
||||
Setup the static files directory.
|
||||
|
|
|
|||
|
|
@ -85,6 +85,11 @@ class Settings(BaseSettings):
|
|||
redis_url: Optional[str] = None
|
||||
redis_cache_expire: int = 3600
|
||||
|
||||
# Sentry
|
||||
sentry_dsn: Optional[str] = None
|
||||
sentry_traces_sample_rate: Optional[float] = 1.0
|
||||
sentry_profiles_sample_rate: Optional[float] = 1.0
|
||||
|
||||
# PLUGIN_DIR: Optional[str] = None
|
||||
|
||||
langfuse_secret_key: Optional[str] = None
|
||||
|
|
|
|||
53
src/backend/base/poetry.lock
generated
53
src/backend/base/poetry.lock
generated
|
|
@ -2411,7 +2411,6 @@ files = [
|
|||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
|
|
@ -2499,6 +2498,56 @@ files = [
|
|||
[package.dependencies]
|
||||
pyasn1 = ">=0.1.3"
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.5.1"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "sentry_sdk-2.5.1-py2.py3-none-any.whl", hash = "sha256:1f87acdce4a43a523ae5aa21a3fc37522d73ebd9ec04b1dbf01aa3d173852def"},
|
||||
{file = "sentry_sdk-2.5.1.tar.gz", hash = "sha256:fbc40a78a8a9c6675133031116144f0d0940376fa6e4e1acd5624c90b0aaf58b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
urllib3 = ">=1.26.11"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.5)"]
|
||||
anthropic = ["anthropic (>=0.16)"]
|
||||
arq = ["arq (>=0.23)"]
|
||||
asyncpg = ["asyncpg (>=0.23)"]
|
||||
beam = ["apache-beam (>=2.12)"]
|
||||
bottle = ["bottle (>=0.12.13)"]
|
||||
celery = ["celery (>=3)"]
|
||||
celery-redbeat = ["celery-redbeat (>=2)"]
|
||||
chalice = ["chalice (>=1.16.0)"]
|
||||
clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
|
||||
django = ["django (>=1.8)"]
|
||||
falcon = ["falcon (>=1.4)"]
|
||||
fastapi = ["fastapi (>=0.79.0)"]
|
||||
flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
|
||||
grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"]
|
||||
httpx = ["httpx (>=0.16.0)"]
|
||||
huey = ["huey (>=2)"]
|
||||
huggingface-hub = ["huggingface-hub (>=0.22)"]
|
||||
langchain = ["langchain (>=0.0.210)"]
|
||||
loguru = ["loguru (>=0.5)"]
|
||||
openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
|
||||
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
|
||||
opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
|
||||
pure-eval = ["asttokens", "executing", "pure-eval"]
|
||||
pymongo = ["pymongo (>=3.1)"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
|
||||
rq = ["rq (>=0.6)"]
|
||||
sanic = ["sanic (>=0.8)"]
|
||||
sqlalchemy = ["sqlalchemy (>=1.2)"]
|
||||
starlette = ["starlette (>=0.19.1)"]
|
||||
starlite = ["starlite (>=1.48)"]
|
||||
tornado = ["tornado (>=5)"]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
version = "1.5.4"
|
||||
|
|
@ -3247,4 +3296,4 @@ local = []
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "3c83d086a178cebd83473758459c4f026907268dbf8c9ce45bc4bfb1f340e9a5"
|
||||
content-hash = "72f05330f1e734596d160b45cb68ab2ebf7d0824314bec0566bddb5b2043f4e6"
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ cryptography = "^42.0.5"
|
|||
asyncer = "^0.0.5"
|
||||
pyperclip = "^1.8.2"
|
||||
uncurl = "^0.0.11"
|
||||
sentry-sdk = "^2.5.1"
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ import { autoLogin, getGlobalVariables, getHealth } from "./controllers/API";
|
|||
import { setupAxiosDefaults } from "./controllers/API/utils";
|
||||
import useTrackLastVisitedPath from "./hooks/use-track-last-visited-path";
|
||||
import Router from "./routes";
|
||||
import { Case } from "./shared/components/caseComponent";
|
||||
import useAlertStore from "./stores/alertStore";
|
||||
import { useDarkStore } from "./stores/darkStore";
|
||||
import useFlowsManagerStore from "./stores/flowsManagerStore";
|
||||
import { useFolderStore } from "./stores/foldersStore";
|
||||
import { useGlobalVariablesStore } from "./stores/globalVariablesStore/globalVariables";
|
||||
import { useStoreStore } from "./stores/storeStore";
|
||||
import { useTypesStore } from "./stores/typesStore";
|
||||
export default function App() {
|
||||
useTrackLastVisitedPath();
|
||||
|
||||
|
|
@ -43,10 +43,8 @@ export default function App() {
|
|||
|
||||
const { isAuthenticated, login, setUserData, setAutoLogin, getUser } =
|
||||
useContext(AuthContext);
|
||||
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
|
||||
const setLoading = useAlertStore((state) => state.setLoading);
|
||||
const fetchApiData = useStoreStore((state) => state.fetchApiData);
|
||||
const getTypes = useTypesStore((state) => state.getTypes);
|
||||
const refreshVersion = useDarkStore((state) => state.refreshVersion);
|
||||
const refreshStars = useDarkStore((state) => state.refreshStars);
|
||||
const setGlobalVariables = useGlobalVariablesStore(
|
||||
|
|
@ -56,8 +54,7 @@ export default function App() {
|
|||
const navigate = useNavigate();
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
const loadingFolders = useFolderStore((state) => state.loading);
|
||||
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
|
||||
|
||||
const [isLoadingHealth, setIsLoadingHealth] = useState(false);
|
||||
|
||||
|
|
@ -115,13 +112,13 @@ export default function App() {
|
|||
if (isAuthenticated) {
|
||||
try {
|
||||
await setupAxiosDefaults();
|
||||
await getFoldersApi();
|
||||
await getTypes();
|
||||
await refreshFlows();
|
||||
|
||||
const res = await getGlobalVariables();
|
||||
setGlobalVariables(res);
|
||||
|
||||
checkHasStore();
|
||||
fetchApiData();
|
||||
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch data:", error);
|
||||
|
|
@ -174,6 +171,8 @@ export default function App() {
|
|||
}
|
||||
};
|
||||
|
||||
const isLoadingApplication = isLoading || isLoadingFolders;
|
||||
|
||||
return (
|
||||
//need parent component with width and height
|
||||
<div className="flex h-full flex-col">
|
||||
|
|
@ -196,15 +195,15 @@ export default function App() {
|
|||
></FetchErrorComponent>
|
||||
}
|
||||
|
||||
{isLoading || loadingFolders ? (
|
||||
<Case condition={isLoadingApplication}>
|
||||
<div className="loading-page-panel">
|
||||
<LoadingComponent remSize={50} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Router />
|
||||
</>
|
||||
)}
|
||||
</Case>
|
||||
|
||||
<Case condition={!isLoadingApplication}>
|
||||
<Router />
|
||||
</Case>
|
||||
</>
|
||||
</ErrorBoundary>
|
||||
<div></div>
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const currentFolder = pathname.split("/");
|
||||
const urlWithoutPath = pathname.split("/").length < 4;
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
const folderIdDragging = useFolderStore((state) => state.folderIdDragging);
|
||||
const refreshFolders = useFolderStore((state) => state.refreshFolders);
|
||||
|
||||
const checkPathName = (itemId: string) => {
|
||||
if (urlWithoutPath && itemId === myCollectionId) {
|
||||
|
|
@ -85,7 +85,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
function addNewFolder() {
|
||||
addFolder({ name: "New Folder", parent_id: null, description: "" }).then(
|
||||
(res) => {
|
||||
getFoldersApi(true);
|
||||
refreshFolders();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
);
|
||||
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
const refreshFolders = useFolderStore((state) => state.refreshFolders);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
|
||||
const triggerFolderChange = (folderId) => {
|
||||
|
|
@ -118,7 +118,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
setFolderIdDragging("");
|
||||
|
||||
updateFlowInDatabase(updatedFlow).then(() => {
|
||||
getFoldersApi(true);
|
||||
refreshFolders();
|
||||
triggerFolderChange(folderId);
|
||||
});
|
||||
};
|
||||
|
|
@ -129,7 +129,7 @@ const useFileDrop = (folderId, folderChangeCallback) => {
|
|||
setFolderDragging(false);
|
||||
setFolderIdDragging("");
|
||||
uploadFlowsFromFolders(formData).then(() => {
|
||||
getFoldersApi(true);
|
||||
refreshFolders();
|
||||
triggerFolderChange(folderId);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default function SidebarNav({
|
|||
}: SidebarNavProps) {
|
||||
const location = useLocation();
|
||||
const pathname = location.pathname;
|
||||
const loadingFolders = useFolderStore((state) => state.loading);
|
||||
const loadingFolders = useFolderStore((state) => state.isLoadingFolders);
|
||||
const folders = useFolderStore((state) => state.folders);
|
||||
|
||||
const pathValues = ["folder", "components", "flows", "all"];
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ const TableComponent = forwardRef<
|
|||
}
|
||||
}, 50);
|
||||
setTimeout(() => {
|
||||
realRef.current.api.hideOverlay();
|
||||
realRef?.current?.api?.hideOverlay();
|
||||
}, 1000);
|
||||
if (props.onGridReady) props.onGridReady(params);
|
||||
};
|
||||
|
|
@ -143,6 +143,7 @@ const TableComponent = forwardRef<
|
|||
minWidth: 100,
|
||||
autoHeight: true,
|
||||
}}
|
||||
animateRows={false}
|
||||
columnDefs={colDef}
|
||||
ref={realRef}
|
||||
onGridReady={onGridReady}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
|
|||
import Cookies from "universal-cookie";
|
||||
import { getLoggedUser, requestLogout } from "../controllers/API";
|
||||
import useAlertStore from "../stores/alertStore";
|
||||
import { useFolderStore } from "../stores/foldersStore";
|
||||
import { Users } from "../types/api";
|
||||
import { AuthContextType } from "../types/contexts/auth";
|
||||
|
||||
|
|
@ -42,7 +43,8 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
const [apiKey, setApiKey] = useState<string | null>(
|
||||
cookies.get("apikey_tkn_lflw")
|
||||
);
|
||||
// const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
|
||||
useEffect(() => {
|
||||
const storedAccessToken = cookies.get("access_token_lf");
|
||||
|
|
@ -64,7 +66,8 @@ export function AuthProvider({ children }): React.ReactElement {
|
|||
setUserData(user);
|
||||
const isSuperUser = user!.is_superuser;
|
||||
setIsAdmin(isSuperUser);
|
||||
// await getFoldersApi(true);
|
||||
|
||||
getFoldersApi(true, true);
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoading(false);
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ function ApiInterceptor() {
|
|||
|
||||
if (!checkRequest) {
|
||||
controller.abort("Duplicate Request");
|
||||
console.error("Duplicate Request");
|
||||
}
|
||||
|
||||
const accessToken = cookies.get("access_token_lf");
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ export function checkDuplicateRequestAndStoreRequest(config) {
|
|||
const lastUrl = localStorage.getItem("lastUrlCalled");
|
||||
const lastMethodCalled = localStorage.getItem("lastMethodCalled");
|
||||
const lastRequestTime = localStorage.getItem("lastRequestTime");
|
||||
const lastCurrentUrl = localStorage.getItem("lastCurrentUrl");
|
||||
|
||||
const currentUrl = window.location.pathname;
|
||||
const currentTime = Date.now();
|
||||
|
||||
const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) =>
|
||||
config?.url!.includes(request)
|
||||
);
|
||||
|
|
@ -17,7 +18,8 @@ export function checkDuplicateRequestAndStoreRequest(config) {
|
|||
lastMethodCalled === config.method &&
|
||||
lastMethodCalled === "get" && // Assuming you want to check only for GET requests
|
||||
lastRequestTime &&
|
||||
currentTime - parseInt(lastRequestTime, 10) < 800
|
||||
currentTime - parseInt(lastRequestTime, 10) < 300 &&
|
||||
lastCurrentUrl === currentUrl
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -25,6 +27,7 @@ export function checkDuplicateRequestAndStoreRequest(config) {
|
|||
localStorage.setItem("lastUrlCalled", config.url ?? "");
|
||||
localStorage.setItem("lastMethodCalled", config.method ?? "");
|
||||
localStorage.setItem("lastRequestTime", currentTime.toString());
|
||||
localStorage.setItem("lastCurrentUrl", currentUrl);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1091,8 +1091,6 @@ export async function getMessagesTable(
|
|||
|
||||
const rowsOrganized = rows.data;
|
||||
|
||||
console.log(rowsOrganized);
|
||||
|
||||
const columns = extractColumnsFromRows(rowsOrganized, mode, excludedFields);
|
||||
const sessions = new Set<string>();
|
||||
rowsOrganized.forEach((row) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import TableComponent from "../../components/tableComponent";
|
||||
import { Tabs, TabsList, TabsTrigger } from "../../components/ui/tabs";
|
||||
|
|
@ -61,6 +61,21 @@ export default function FlowLogsModal({
|
|||
}
|
||||
}, [open, activeTab]);
|
||||
|
||||
const tableComponentRender = useMemo(() => {
|
||||
return (
|
||||
<TableComponent
|
||||
key={activeTab}
|
||||
readOnlyEdit
|
||||
className="h-max-full h-full w-full"
|
||||
pagination={rows.length === 0 ? false : true}
|
||||
columnDefs={columns}
|
||||
autoSizeStrategy={{ type: "fitGridWidth" }}
|
||||
rowData={rows}
|
||||
headerHeight={rows.length === 0 ? 0 : undefined}
|
||||
/>
|
||||
);
|
||||
}, [activeTab]);
|
||||
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} size="large">
|
||||
<BaseModal.Header description="Inspect component executions and monitor sent messages in the playground.">
|
||||
|
|
@ -85,16 +100,7 @@ export default function FlowLogsModal({
|
|||
<TabsTrigger value={"Messages"}>Messages</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
<TableComponent
|
||||
key={activeTab}
|
||||
readOnlyEdit
|
||||
className="h-max-full h-full w-full"
|
||||
pagination={rows.length === 0 ? false : true}
|
||||
columnDefs={columns}
|
||||
autoSizeStrategy={{ type: "fitGridWidth" }}
|
||||
rowData={rows}
|
||||
headerHeight={rows.length === 0 ? 0 : undefined}
|
||||
></TableComponent>
|
||||
{tableComponentRender}
|
||||
</BaseModal.Content>
|
||||
</BaseModal>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export default function LoginAdminPage() {
|
|||
const [inputState, setInputState] =
|
||||
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
|
||||
const { login, isAuthenticated, setUserData } = useContext(AuthContext);
|
||||
const setLoading = useAlertStore((state) => state.setLoading);
|
||||
|
||||
const { password, username } = inputState;
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
|
@ -35,6 +36,10 @@ export default function LoginAdminPage() {
|
|||
};
|
||||
onLogin(user)
|
||||
.then((user) => {
|
||||
console.log("admin page");
|
||||
|
||||
setLoading(true);
|
||||
|
||||
login(user.access_token);
|
||||
navigate("/admin/");
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { CONTROL_LOGIN_STATE } from "../../constants/constants";
|
|||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { onLogin } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { LoginType } from "../../types/api";
|
||||
import {
|
||||
inputHandlerEventType,
|
||||
|
|
@ -23,6 +24,7 @@ export default function LoginPage(): JSX.Element {
|
|||
const { login } = useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const setLoading = useFlowsManagerStore((state) => state.setIsLoading);
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
|
|
@ -37,6 +39,9 @@ export default function LoginPage(): JSX.Element {
|
|||
};
|
||||
onLogin(user)
|
||||
.then((user) => {
|
||||
console.log("login page");
|
||||
|
||||
setLoading(true);
|
||||
login(user.access_token);
|
||||
navigate("/");
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,39 +1,19 @@
|
|||
import { useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import useAlertStore from "../../../../../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
|
||||
import { useFolderStore } from "../../../../../../stores/foldersStore";
|
||||
import { handleDownloadFolderFn } from "../../../../utils/handle-download-folder";
|
||||
import InputSearchComponent from "../inputSearchComponent";
|
||||
import TabsSearchComponent from "../tabsComponent";
|
||||
|
||||
type HeaderTabsSearchComponentProps = {};
|
||||
|
||||
const HeaderTabsSearchComponent = ({}: HeaderTabsSearchComponentProps) => {
|
||||
const location = useLocation();
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const folderId = location?.state?.folderId || myCollectionId;
|
||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||
const [tabActive, setTabActive] = useState("Flows");
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
||||
const setSearchFlowsComponents = useFlowsManagerStore(
|
||||
(state) => state.setSearchFlowsComponents
|
||||
);
|
||||
|
||||
const handleDownloadFolder = () => {
|
||||
if (allFlows.length === 0) {
|
||||
setErrorData({
|
||||
title: "Folder is empty",
|
||||
list: [],
|
||||
});
|
||||
return;
|
||||
}
|
||||
handleDownloadFolderFn(folderId);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex items-end gap-4">
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import useAlertStore from "../../../stores/alertStore";
|
|||
import { useFolderStore } from "../../../stores/foldersStore";
|
||||
import { deleteFolder, getFolderById } from "../services";
|
||||
|
||||
const useDeleteFolder = ({ navigate, getFoldersApi }) => {
|
||||
const useDeleteFolder = ({ navigate }) => {
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const folderToEdit = useFolderStore((state) => state.folderToEdit);
|
||||
const myCollectionId = useFolderStore((state) => state.myCollectionId);
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
|
||||
const handleDeleteFolder = () => {
|
||||
deleteFolder(folderToEdit?.id!)
|
||||
|
|
|
|||
|
|
@ -25,16 +25,9 @@ export default function HomePage(): JSX.Element {
|
|||
const [openFolderModal, setOpenFolderModal] = useState(false);
|
||||
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
|
||||
const is_component = pathname === "/components";
|
||||
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
|
||||
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
getFoldersApi();
|
||||
}, 300);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentFlowId("");
|
||||
}, [pathname]);
|
||||
|
|
@ -45,7 +38,7 @@ export default function HomePage(): JSX.Element {
|
|||
is_component,
|
||||
});
|
||||
|
||||
const { handleDeleteFolder } = useDeleteFolder({ navigate, getFoldersApi });
|
||||
const { handleDeleteFolder } = useDeleteFolder({ navigate });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -7,64 +7,100 @@ import {
|
|||
} from "../pages/MainPage/services";
|
||||
import { FoldersStoreType } from "../types/zustand/folders";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
import { useTypesStore } from "./typesStore";
|
||||
|
||||
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||
folders: [],
|
||||
getFoldersApi: (refetch = false) => {
|
||||
getFoldersApi: (refetch = false, startupApplication: boolean = false) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (get()?.folders.length === 0 || refetch === true) {
|
||||
get().setLoading(true);
|
||||
get().setIsLoadingFolders(true);
|
||||
getFolders().then(
|
||||
(res) => {
|
||||
async (res) => {
|
||||
const foldersWithoutStarterProjects = res?.filter(
|
||||
(folder) => folder.name !== STARTER_FOLDER_NAME,
|
||||
(folder) => folder.name !== STARTER_FOLDER_NAME
|
||||
);
|
||||
|
||||
const starterProjects = res?.find(
|
||||
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||
(folder) => folder.name === STARTER_FOLDER_NAME
|
||||
);
|
||||
|
||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
||||
set({ folders: foldersWithoutStarterProjects });
|
||||
|
||||
const myCollectionId = res?.find(
|
||||
(f) => f.name === DEFAULT_FOLDER,
|
||||
(f) => f.name === DEFAULT_FOLDER
|
||||
)?.id;
|
||||
|
||||
set({ myCollectionId });
|
||||
|
||||
if (refetch === true) {
|
||||
useFlowsManagerStore.getState().refreshFlows();
|
||||
useFlowsManagerStore.getState().setAllFlows;
|
||||
}
|
||||
const { refreshFlows } = useFlowsManagerStore.getState();
|
||||
const { getTypes } = useTypesStore.getState();
|
||||
const { setIsLoadingFolders } = get();
|
||||
|
||||
if (refetch) {
|
||||
if (startupApplication) {
|
||||
await refreshFlows();
|
||||
await getTypes();
|
||||
} else {
|
||||
refreshFlows();
|
||||
getTypes();
|
||||
}
|
||||
}
|
||||
setIsLoadingFolders(false);
|
||||
|
||||
get().setLoading(false);
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
set({ folders: [] });
|
||||
get().setLoading(false);
|
||||
get().setIsLoadingFolders(false);
|
||||
reject(error);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
refreshFolders: () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
getFolders().then(
|
||||
async (res) => {
|
||||
const foldersWithoutStarterProjects = res?.filter(
|
||||
(folder) => folder.name !== STARTER_FOLDER_NAME
|
||||
);
|
||||
|
||||
const starterProjects = res?.find(
|
||||
(folder) => folder.name === STARTER_FOLDER_NAME
|
||||
);
|
||||
|
||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
||||
set({ folders: foldersWithoutStarterProjects });
|
||||
|
||||
const myCollectionId = res?.find(
|
||||
(f) => f.name === DEFAULT_FOLDER
|
||||
)?.id;
|
||||
|
||||
set({ myCollectionId });
|
||||
|
||||
resolve();
|
||||
},
|
||||
(error) => {
|
||||
set({ folders: [] });
|
||||
get().setIsLoadingFolders(false);
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
setFolders: (folders) => set(() => ({ folders: folders })),
|
||||
loading: false,
|
||||
setLoading: (loading) => set(() => ({ loading: loading })),
|
||||
isLoadingFolders: false,
|
||||
setIsLoadingFolders: (isLoadingFolders) => set(() => ({ isLoadingFolders })),
|
||||
getFolderById: (id) => {
|
||||
if (id) {
|
||||
getFolderById(id).then(
|
||||
(res) => {
|
||||
const setAllFlows = useFlowsManagerStore.getState().setAllFlows;
|
||||
setAllFlows(res.flows);
|
||||
set({ selectedFolder: res });
|
||||
},
|
||||
() => {
|
||||
get().getFoldersApi(true);
|
||||
},
|
||||
);
|
||||
getFolderById(id).then((res) => {
|
||||
const setAllFlows = useFlowsManagerStore.getState().setAllFlows;
|
||||
setAllFlows(res.flows);
|
||||
set({ selectedFolder: res });
|
||||
});
|
||||
}
|
||||
},
|
||||
selectedFolder: null,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@ import { FolderType } from "../../../pages/MainPage/entities";
|
|||
|
||||
export type FoldersStoreType = {
|
||||
folders: FolderType[];
|
||||
getFoldersApi: (refetch?: boolean) => Promise<void>;
|
||||
getFoldersApi: (
|
||||
refetch?: boolean,
|
||||
startupApplication?: boolean
|
||||
) => Promise<void>;
|
||||
setFolders: (folders: FolderType[]) => void;
|
||||
loading: boolean;
|
||||
setLoading: (loading: boolean) => void;
|
||||
isLoadingFolders: boolean;
|
||||
setIsLoadingFolders: (isLoadingFolders: boolean) => void;
|
||||
selectedFolder: FolderType | null;
|
||||
getFolderById: (id: string) => void;
|
||||
getMyCollectionFolder: () => void;
|
||||
|
|
@ -23,4 +26,5 @@ export type FoldersStoreType = {
|
|||
setFolderIdDragging: (id: string) => void;
|
||||
starterProjectId: string;
|
||||
setStarterProjectId: (id: string) => void;
|
||||
refreshFolders: () => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export function normalCaseToSnakeCase(str: string): string {
|
|||
|
||||
export function toTitleCase(
|
||||
str: string | undefined,
|
||||
isNodeField?: boolean
|
||||
isNodeField?: boolean,
|
||||
): string {
|
||||
if (!str) return "";
|
||||
let result = str
|
||||
|
|
@ -65,7 +65,7 @@ export function toTitleCase(
|
|||
if (isNodeField) return word;
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase(),
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
|
|
@ -78,7 +78,7 @@ export function toTitleCase(
|
|||
if (isNodeField) return word;
|
||||
if (index === 0) {
|
||||
return checkUpperWords(
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||
word[0].toUpperCase() + word.slice(1).toLowerCase(),
|
||||
);
|
||||
}
|
||||
return checkUpperWords(word.toLowerCase());
|
||||
|
|
@ -182,7 +182,7 @@ export function checkLocalStorageKey(key: string): boolean {
|
|||
|
||||
export function IncrementObjectKey(
|
||||
object: object,
|
||||
key: string
|
||||
key: string,
|
||||
): { newKey: string; increment: number } {
|
||||
let count = 1;
|
||||
const type = removeCountFromString(key);
|
||||
|
|
@ -217,7 +217,7 @@ export function groupByFamily(
|
|||
data: APIDataType,
|
||||
baseClasses: string,
|
||||
left: boolean,
|
||||
flow?: NodeType[]
|
||||
flow?: NodeType[],
|
||||
): groupedObjType[] {
|
||||
const baseClassesSet = new Set(baseClasses.split("\n"));
|
||||
let arrOfPossibleInputs: Array<{
|
||||
|
|
@ -243,7 +243,7 @@ export function groupByFamily(
|
|||
baseClassesSet.has(template.type)) ||
|
||||
(template?.input_types &&
|
||||
template?.input_types.some((inputType) =>
|
||||
baseClassesSet.has(inputType)
|
||||
baseClassesSet.has(inputType),
|
||||
)))
|
||||
);
|
||||
};
|
||||
|
|
@ -263,7 +263,7 @@ export function groupByFamily(
|
|||
hasBaseClassInBaseClasses:
|
||||
foundNode?.hasBaseClassInBaseClasses ||
|
||||
nodeData.node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
baseClassesSet.has(baseClass),
|
||||
), //seta como anterior ou verifica se o node tem base class
|
||||
displayName: nodeData.node?.display_name,
|
||||
});
|
||||
|
|
@ -280,10 +280,10 @@ export function groupByFamily(
|
|||
if (!foundNode) {
|
||||
foundNode = {
|
||||
hasBaseClassInTemplate: Object.values(node!.template).some(
|
||||
checkBaseClass
|
||||
checkBaseClass,
|
||||
),
|
||||
hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass)
|
||||
hasBaseClassInBaseClasses: node!.base_classes?.some((baseClass) =>
|
||||
baseClassesSet.has(baseClass),
|
||||
),
|
||||
displayName: node?.display_name,
|
||||
};
|
||||
|
|
@ -355,7 +355,7 @@ export function isTimeStampString(str: string): boolean {
|
|||
export function extractColumnsFromRows(
|
||||
rows: object[],
|
||||
mode: "intersection" | "union",
|
||||
excludeColumns?: Array<string>
|
||||
excludeColumns?: Array<string>,
|
||||
): (ColDef<any> | ColGroupDef<any>)[] {
|
||||
let columnsKeys: { [key: string]: ColDef<any> | ColGroupDef<any> } = {};
|
||||
if (rows.length === 0) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue