From e2e0dd1c7f717c633b11d0ed11a95074a7675d20 Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Mon, 3 Mar 2025 16:17:48 +0530 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20Add=20environment=20configurati?= =?UTF-8?q?on,=20improve=20asset=20loading,=20and=20enhance=20error=20hand?= =?UTF-8?q?ling=20in=20the=20App=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 260392 -> 284280 bytes packages/frontend/.env-example | 2 + packages/frontend/.gitignore | 8 +- packages/frontend/package.json | 63 +-- packages/frontend/src/App.tsx | 36 +- packages/frontend/src/Layout.tsx | 28 +- packages/frontend/src/Root.tsx | 30 +- .../frontend/src/components/InjectedHtml.tsx | 14 +- packages/frontend/src/components/Metadata.tsx | 32 +- packages/frontend/src/hooks/assets.ts | 71 ++-- packages/frontend/src/styles.ts | 4 + packages/frontend/vite.config.ts | 26 +- uv.lock | 3 - webflow/modules/__init__.py | 1 - webflow/modules/routes.py | 365 ++++-------------- webflow/modules/serve.py | 8 - webflow/modules/types.py | 16 +- webflow/modules/webflow_api.py | 183 +++++++++ webflow/webflow.py | 8 +- 19 files changed, 419 insertions(+), 479 deletions(-) create mode 100644 packages/frontend/.env-example create mode 100644 packages/frontend/src/styles.ts delete mode 100644 webflow/modules/serve.py create mode 100644 webflow/modules/webflow_api.py diff --git a/bun.lockb b/bun.lockb index 2598051b7cf159d52de797426f272e88ac1461f9..9953289ad26c448081ece65bdfb62105ac4f65c3 100644 GIT binary patch delta 68346 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 delta 55356 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 - [], - ); + const [sidebarItems, setSidebarItems] = useState([]); const [sidebarVisible, setSidebarVisible] = useState(false); const [sidebarLabel, setSidebarLabel] = useState(''); const [defaultOpen, setDefaultOpen] = useState(false); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { const fetchSidebarItems = async () => { - const response: SidebarResponse = await getSidebarItems(); - setSidebarItems(response.items); - setSidebarVisible(response.visible); - setSidebarLabel(response.label); - setDefaultOpen(response.default_open); + try { + setLoading(true); + setError(null); + + const response: SidebarResponse = await getSidebarItems(); + setSidebarItems(response.items); + setSidebarVisible(response.visible); + setSidebarLabel(response.label); + setDefaultOpen(response.default_open); + } catch (err) { + console.error('Error fetching sidebar items:', err); + setError('Failed to load sidebar'); + } finally { + setLoading(false); + } }; fetchSidebarItems(); @@ -35,16 +45,20 @@ export function App() {
- {sidebarVisible && ( + {loading &&

Loading sidebar...

} + {error &&

{error}

} + + {!loading && !error && sidebarVisible && ( )} - {sidebarVisible && } + {!loading && !error && sidebarVisible && } +
- {/* Add the InjectedHtml component here */} +
diff --git a/packages/frontend/src/Layout.tsx b/packages/frontend/src/Layout.tsx index 7d09b47..c0ffff1 100644 --- a/packages/frontend/src/Layout.tsx +++ b/packages/frontend/src/Layout.tsx @@ -30,21 +30,29 @@ export default function Layout() { const { theme, setTheme, systemTheme } = useTheme(); const currentTheme = theme === 'system' ? systemTheme : theme; - useEffect(() => { - const loadInitialConfig = async () => { - try { - const fetchedConfig = await getConfig(); - if (fetchedConfig[0].colorMode) { +useEffect(() => { + const loadInitialConfig = async () => { + try { + const fetchedConfig = await getConfig(); + + if (Array.isArray(fetchedConfig) && fetchedConfig.length > 0) { + if (fetchedConfig[0]?.colorMode) { setTheme(fetchedConfig[0].colorMode); } setConfig(fetchedConfig[0]); - } catch (error) { - console.error('Error fetching initial config:', error); + } else { + setConfig({}); } - }; + } catch (error) { + console.error("Error fetching initial config:", error); + setConfig({}); // Fallback to empty object on error + } + }; + + loadInitialConfig(); +}, [setTheme]); + - loadInitialConfig(); - }, [setTheme]); useEffect(() => { const updateProgress = () => { diff --git a/packages/frontend/src/Root.tsx b/packages/frontend/src/Root.tsx index bcb5c19..ce2793e 100644 --- a/packages/frontend/src/Root.tsx +++ b/packages/frontend/src/Root.tsx @@ -1,3 +1,4 @@ +import './styles.ts'; import React, { Suspense, useEffect, useState } from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; @@ -5,18 +6,11 @@ import { MantineProvider } from '@mantine/core'; import { ThemeProvider } from 'next-themes'; import { Theme } from '@radix-ui/themes'; import { HelmetProvider } from 'react-helmet-async'; -import './styles/index.scss'; -import '@xyflow/react/dist/style.css'; -import '@radix-ui/themes/styles.css'; -import '@mantine/core/styles.css'; -import { loadAndInjectAssets } from './hooks/assets.ts'; import { getConfig } from '@pywebflow/api/src/config'; // Lazy load components with preloading const Layout = React.lazy(() => import(/* webpackPreload: true */ './Layout')); -const MetaData = React.lazy( - () => import(/* webpackPreload: true */ './components/Metadata'), -); +const MetaData = React.lazy(() => import(/* webpackPreload: true */ './components/Metadata')); // Preload critical components asynchronously const preloadAssets = () => { @@ -28,27 +22,20 @@ preloadAssets(); const Root = () => { const [mounted, setMounted] = useState(false); - const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>( - 'system', - ); + const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>('system'); useEffect(() => { - // Load assets dynamically - loadAndInjectAssets(); - // Fetch config data const fetchConfig = async () => { try { const fetchedConfig = await getConfig(); - const config = fetchedConfig.data[0]; - if (config.colorMode) { - setDefaultTheme(config.colorMode); + if (fetchedConfig?.data?.length > 0) { + setDefaultTheme(fetchedConfig.data[0].colorMode || 'system'); } } catch (error) { console.error('Error fetching config:', error); } }; - fetchConfig(); }, []); @@ -58,12 +45,7 @@ const Root = () => { return ( - + {mounted && ( diff --git a/packages/frontend/src/components/InjectedHtml.tsx b/packages/frontend/src/components/InjectedHtml.tsx index 3f4bb4c..2dc131f 100644 --- a/packages/frontend/src/components/InjectedHtml.tsx +++ b/packages/frontend/src/components/InjectedHtml.tsx @@ -1,5 +1,7 @@ import React, { useEffect, useState } from 'react'; import { fetchHtmlContent } from '@pywebflow/api/src/html'; +import DOMPurify from 'dompurify'; +import parse from 'html-react-parser'; const InjectedHtml: React.FC = () => { const [htmlContents, setHtmlContents] = useState([]); @@ -14,16 +16,14 @@ const InjectedHtml: React.FC = () => { }, []); return ( -
+
{htmlContents.map((htmlContent, index) => ( -
+
+ {parse(DOMPurify.sanitize(htmlContent))} +
))}
); }; -export default InjectedHtml; +export default InjectedHtml; \ No newline at end of file diff --git a/packages/frontend/src/components/Metadata.tsx b/packages/frontend/src/components/Metadata.tsx index 2fee2f1..872e7f4 100644 --- a/packages/frontend/src/components/Metadata.tsx +++ b/packages/frontend/src/components/Metadata.tsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from 'react'; import { Helmet } from 'react-helmet-async'; import getMetadata, { Metadata } from '@pywebflow/api/src/metadata.ts'; +import { injectAssets } from '../hooks/assets.ts'; + const MetaData: React.FC = () => { const [metadata, setMetadata] = useState({ title: 'PyWebflow', @@ -8,7 +10,9 @@ const MetaData: React.FC = () => { }); useEffect(() => { - const fetchMetadata = async () => { + const loadMetadataAndAssets = async () => { + await injectAssets(); // Load CSS & JS before fetching metadata + try { const data = await getMetadata(); setMetadata((prevMetadata) => ({ @@ -20,35 +24,23 @@ const MetaData: React.FC = () => { } }; - fetchMetadata(); + loadMetadataAndAssets(); }, []); return ( {metadata.title} - {metadata.description && ( - - )} - {metadata.keywords && ( - - )} + {metadata.description && } + {metadata.keywords && } {metadata.author && } - {metadata.viewport && ( - - )} + {metadata.viewport && } {metadata.charset && } {metadata.robots && } {metadata.canonical && } - {metadata.ogTitle && ( - - )} - {metadata.ogDescription && ( - - )} + {metadata.ogTitle && } + {metadata.ogDescription && } {metadata.ogUrl && } - {metadata.ogImage && ( - - )} + {metadata.ogImage && } ); }; diff --git a/packages/frontend/src/hooks/assets.ts b/packages/frontend/src/hooks/assets.ts index c16da2f..5c0a31a 100644 --- a/packages/frontend/src/hooks/assets.ts +++ b/packages/frontend/src/hooks/assets.ts @@ -1,56 +1,41 @@ import { loadAssets } from '@pywebflow/api/src/filepaths.ts'; -// Function to inject CSS dynamically -export const injectCSS = (href: string): void => { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = href; - document.head.appendChild(link); -}; - -// Function to inject JavaScript dynamically -export const injectJS = (src: string): void => { - const script = document.createElement('script'); - script.type = 'module'; - script.src = src; - document.body.appendChild(script); -}; - -// Function to prefetch files before loading -export const prefetchFile = ( - href: string, - asType: 'script' | 'style', -): void => { - const prefetchLink = document.createElement('link'); - prefetchLink.rel = 'prefetch'; - prefetchLink.href = href; - prefetchLink.as = asType; - document.head.appendChild(prefetchLink); -}; - -// Type definition for assets response -interface AssetsResponse { - css?: string[]; - js?: string[]; -} - -// Function to load and inject assets dynamically -export const loadAndInjectAssets = async (): Promise => { +// Function to inject assets dynamically +export const injectAssets = async (): Promise => { try { - const assets: AssetsResponse = (await loadAssets()) || {}; // Ensure a valid object is returned + const assets = await loadAssets(); - const cssFiles = assets.css ?? []; // Default to an empty array if undefined - const jsFiles = assets.js ?? []; // Default to an empty array if undefined + if (!assets || typeof assets !== 'object') { + console.error('Invalid assets data:', assets); + return; + } + const cssFiles = Array.isArray(assets.css) ? assets.css.filter(Boolean) : []; + const jsFiles = Array.isArray(assets.js) ? assets.js.filter(Boolean) : []; + + // Inject CSS files cssFiles.forEach((href: string) => { - prefetchFile(href, 'style'); - injectCSS(href); + if (!document.querySelector(`link[href="${href}"]`)) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = href; + document.head.appendChild(link); + console.log(`Injected CSS: ${href}`); + } }); + // Inject JS files jsFiles.forEach((src: string) => { - prefetchFile(src, 'script'); - injectJS(src); + if (!document.querySelector(`script[src="${src}"]`)) { + const script = document.createElement('script'); + script.type = 'module'; + script.src = src; + script.defer = true; // Prevent blocking page load + document.body.appendChild(script); + console.log(`Injected JS: ${src}`); + } }); + } catch (error) { console.error('Error loading assets:', error); } diff --git a/packages/frontend/src/styles.ts b/packages/frontend/src/styles.ts new file mode 100644 index 0000000..d0ee88c --- /dev/null +++ b/packages/frontend/src/styles.ts @@ -0,0 +1,4 @@ +import './styles/index.scss'; +import '@xyflow/react/dist/style.css'; +import '@radix-ui/themes/styles.css'; +import '@mantine/core/styles.css'; \ No newline at end of file diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index fb85473..8943208 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,9 +1,13 @@ import { defineConfig, PluginOption } from 'vite'; -import react from '@vitejs/plugin-react'; +import react from "@vitejs/plugin-react-swc"; import path from 'path'; import tailwindcss from '@tailwindcss/vite'; import { visualizer } from 'rollup-plugin-visualizer'; +const isProduction = process.env.NODE_ENV === 'production'; + +const API_BASE_URL = isProduction ? 'https://your-production-api.com' : 'http://127.0.0.1:8000'; + export default defineConfig({ plugins: [react(), tailwindcss(), visualizer() as PluginOption], resolve: { @@ -15,24 +19,24 @@ export default defineConfig({ outDir: path.resolve(__dirname, '../../webflow/frontend/dist'), emptyOutDir: true, minify: 'esbuild', - sourcemap: false, + sourcemap: !isProduction, cssCodeSplit: true, + cssMinify: 'esbuild', + assetsInlineLimit: 0, terserOptions: { compress: { - drop_console: true, - drop_debugger: true, + drop_console: isProduction, + drop_debugger: isProduction, }, }, }, server: { proxy: { - '/api/nodes': 'http://127.0.0.1:8000', - '/api/edges': 'http://127.0.0.1:8000', - '/api/status': 'http://127.0.0.1:8000', - '/api/filepaths': 'http://127.0.0.1:8000', - '/api/sidebar': 'http://127.0.0.1:8000', - '/api/config': 'http://127.0.0.1:8000', - '/api/html': 'http://127.0.0.1:8000', + '/api': { + target: API_BASE_URL, + changeOrigin: true, + secure: isProduction, + }, }, fs: { strict: false, diff --git a/uv.lock b/uv.lock index e419d8e..cc22837 100644 --- a/uv.lock +++ b/uv.lock @@ -284,12 +284,10 @@ wheels = [ [[package]] name = "pywebflow" -version = "0.0.1rc0" source = { editable = "." } dependencies = [ { name = "fastapi" }, { name = "logly" }, - { name = "rich" }, { name = "uvicorn" }, ] @@ -310,7 +308,6 @@ requires-dist = [ { name = "fastapi", specifier = ">=0.115.6,<0.116.0" }, { name = "logly", git = "https://github.com/muhammad-fiaz/logly.git?rev=9920c22e118facaa5f3145df0a9b4b9e9b9a8839" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, - { name = "rich", specifier = ">=13.9.4" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.5" }, { name = "uvicorn", specifier = ">=0.34.0,<0.35.0" }, ] diff --git a/webflow/modules/__init__.py b/webflow/modules/__init__.py index 072e66d..d3a3e78 100644 --- a/webflow/modules/__init__.py +++ b/webflow/modules/__init__.py @@ -1,4 +1,3 @@ from webflow.modules.arguments import parse_arguments from webflow.modules.mount import mount_static_files from webflow.modules.routes import WebFlow_API -from webflow.modules.serve import app diff --git a/webflow/modules/routes.py b/webflow/modules/routes.py index c2a24cf..78c4c1a 100644 --- a/webflow/modules/routes.py +++ b/webflow/modules/routes.py @@ -1,300 +1,69 @@ import datetime -from pathlib import Path -from fastapi import APIRouter, FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse, FileResponse -from typing import Dict, List, Optional +from fastapi import APIRouter +from fastapi.responses import JSONResponse +from typing import List + +from webflow.modules.types import NodeData, EdgeData, Metadata, SidebarResponse, HtmlContent, ReactFlowConfig +from webflow.modules.webflow_api import WebFlow_API + +router = APIRouter() + +@router.get("/api/status") +async def get_status(): + return { + "status": "online", + "message": "Server is running smoothly", + "timestamp": datetime.datetime.now().isoformat(), + } + +@router.get("/api/nodes", response_model=List[NodeData]) +async def get_nodes(): + return WebFlow_API.nodes + +@router.get("/api/edges", response_model=List[EdgeData]) +async def get_edges(): + return WebFlow_API.edges + +@router.get("/api/sidebar", response_model=SidebarResponse) +async def get_sidebar(): + items = WebFlow_API.sidebar if isinstance(WebFlow_API.sidebar, list) else [] + return SidebarResponse( + visible=WebFlow_API.sidebar_visible, + label=WebFlow_API.sidebar_label, + default_open=WebFlow_API.sidebar_default_open, + items=items + ) + + +@router.get("/api/metadata", response_model=Metadata) +async def get_metadata(): + return WebFlow_API.metadata + +@router.get("/api/config", response_model=List[ReactFlowConfig]) +async def get_config(): + return WebFlow_API.config + +@router.get("/api/html") +async def get_html(): + return {"content": WebFlow_API.get_html()} + +@router.post("/api/html") +async def set_html(content: HtmlContent): + WebFlow_API.add_html(content.content) + return {"status": "success"} + +@router.get("/api/filepaths") +async def get_file_paths(): + WebFlow_API.refresh_static_files() + return JSONResponse(content={ + "css": WebFlow_API.custom_css, + "js": WebFlow_API.custom_js, + "html": WebFlow_API.custom_html, + }) + +@router.get("/static/{filename:path}") +async def get_static_file(filename: str): + return WebFlow_API.serve_file(filename) + +WebFlow_API.app.include_router(router) -from pydantic import BaseModel - - -from webflow.logly import logly -from webflow.modules.mount import mount_static_files -from webflow.modules.types import ( - NodeData, - EdgeData, - Metadata, - SideBar, - SidebarResponse, - ReactFlowConfig, - HtmlContent, -) - - -def ensure_initialized(method): - def wrapper(cls, *args, **kwargs): - if not cls.initialized: - cls.initialize() - return method(cls, *args, **kwargs) - - return wrapper - - -class WebFlow_API: - app = FastAPI() - nodes: List[NodeData] = [] - edges: List[EdgeData] = [] - metadata: Metadata = Metadata(title="PyWebflow", description="Webflow application") - config: List[ReactFlowConfig] = [] - custom_css: List[str] = [] - custom_js: List[str] = [] - custom_html: List[str] = [] - sidebar_visible: bool = False - sidebar_label: str = "Application" - sidebar_default_open: bool = False - sidebar: List[SideBar] = [] - static_dir: Optional[str] = None - initialized = False - router = APIRouter() - html_store: List[str] = [] - - @classmethod - def initialize(cls): - if cls.initialized: - return - cls.app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - mount_static_files(cls.app, static_dir=cls.static_dir) - cls.initialized = True - - @classmethod - def refresh_static_files(cls): - cls.custom_css = [] - cls.custom_js = [] - cls.custom_html = [] - - if cls.static_dir: - static_path = Path(cls.static_dir) - for file_path in static_path.rglob("*"): - if file_path.is_file(): - relative_path = file_path.relative_to(static_path) - if file_path.suffix == ".css": - cls.custom_css.append(f"/static/{relative_path}") - elif file_path.suffix == ".js": - cls.custom_js.append(f"/static/{relative_path}") - elif file_path.suffix == ".html": - cls.custom_html.append(f"/static/{relative_path}") - - @classmethod - @ensure_initialized - def add_node(cls, node_id: str, label: str, position: Dict[str, float], **kwargs): - node = NodeData(id=node_id, label=label, position=position, **kwargs) - cls.nodes.append(node) - - @classmethod - @ensure_initialized - def sidebar(cls, visible: bool, label: str, default_open: bool, items: List[Dict[str, str]]): - cls.sidebar_visible = visible - cls.sidebar_label = label - cls.sidebar_default_open = default_open - cls.sidebar = [SideBar(**item) for item in items] - - @classmethod - @ensure_initialized - def add_edge(cls, edge_id: str, source: str, target: str, **kwargs): - edge = EdgeData(id=edge_id, source=source, target=target, **kwargs) - cls.edges.append(edge) - - @classmethod - @ensure_initialized - def set_metadata(cls, title: str, **kwargs): - cls.metadata = Metadata(title=title, **kwargs) - - @classmethod - @ensure_initialized - def set_config(cls, **kwargs): - config = ReactFlowConfig(**kwargs) - cls.config.append(config) - - @classmethod - def set_static_directory(cls, directory: str): - """Set the directory where CSS & JS files are stored.""" - absolute_directory = Path(directory) - if absolute_directory.exists(): - mount_static_files(cls.app, static_dir=str(absolute_directory.resolve())) - cls.static_dir = str(absolute_directory.resolve()) - cls.refresh_static_files() - else: - raise ValueError(f"Directory {absolute_directory} does not exist.") - - @classmethod - def set_custom_css(cls, path: str): - if not cls.static_dir: - logly.warn("Static directory not set.") - return - abs_path = Path(cls.static_dir) / path - if not abs_path.exists(): - logly.warn(f"CSS file {path} not found.") - return - cls.custom_css.append(f"/static/{path}") - - @classmethod - def set_custom_js(cls, path: str): - if not cls.static_dir: - logly.warn("Static directory not set.") - return - abs_path = Path(cls.static_dir) / path - if not abs_path.exists(): - logly.warn(f"JS file {path} not found.") - return - cls.custom_js.append(f"/static/{path}") - - @classmethod - def set_custom_html(cls, path: str): - if not cls.static_dir: - logly.warn("Static directory not set.") - return - abs_path = Path(cls.static_dir) / path - if not abs_path.exists(): - logly.warn(f"HTML file {path} not found.") - return - cls.custom_html.append(f"/static/{path}") - - @classmethod - def serve_file(cls, filename: str): - if not cls.static_dir: - logly.warn("Static directory not set.") - raise HTTPException(status_code=500, detail="Static directory not set") - file_path = Path(cls.static_dir) / filename - if file_path.exists(): - # Determine media type based on file extension - media_type = "application/octet-stream" - if file_path.suffix == ".css": - media_type = "text/css" - elif file_path.suffix == ".js": - media_type = "application/javascript" - elif file_path.suffix == ".html": - media_type = "text/html" - # Serve the file with headers to prevent caching - headers = {"Cache-Control": "no-cache, no-store, must-revalidate"} - return FileResponse(str(file_path), media_type=media_type, headers=headers) - else: - logly.warn(f"{filename} not found in the static directory.") - raise HTTPException(status_code=404, detail=f"{filename} not found") - - @classmethod - @ensure_initialized - def add_html(cls, content: str): - cls.html_store.append(content) - - @classmethod - @ensure_initialized - def get_html(cls): - return cls.html_store - - @classmethod - def launch(cls, host: str = "127.0.0.1", port: int = 8000, reload: bool = True): - cls.initialize() - import uvicorn - - uvicorn.run(cls.app, host=host, port=port, reload=reload) - - @classmethod - def create_router(cls): - cls.router = APIRouter() - - @cls.router.get("/api/status") - async def get_status(): - return { - "status": "online", - "message": "Server is running smoothly", - "timestamp": datetime.datetime.now().isoformat(), - } - - @cls.router.get( - "/api/nodes", - response_model=List[NodeData], - response_model_exclude_none=True, - ) - async def get_nodes(): - return cls.nodes - - @cls.router.get( - "/api/edges", - response_model=List[EdgeData], - response_model_exclude_none=True, - ) - async def get_edges(): - return cls.edges - - @cls.router.get( - "/api/sidebar", - response_model=SidebarResponse, - response_model_exclude_none=True, - ) - async def get_sidebar(): - return SidebarResponse( - visible=cls.sidebar_visible, - label=cls.sidebar_label, - default_open=cls.sidebar_default_open, - items=cls.sidebar, - ) - - @cls.router.get("/api/metadata", response_model=Metadata) - async def get_metadata(): - return cls.metadata - - @cls.router.get("/api/filepaths") - async def get_file_paths(): - cls.refresh_static_files() - warnings = { - "css": [], - "js": [], - "html": [], - } - for path in cls.custom_css: - if not (Path(cls.static_dir) / path.replace("/static/", "")).exists(): - warnings["css"].append(path) - for path in cls.custom_js: - if not (Path(cls.static_dir) / path.replace("/static/", "")).exists(): - warnings["js"].append(path) - for path in cls.custom_html: - if not (Path(cls.static_dir) / path.replace("/static/", "")).exists(): - warnings["html"].append(path) - - if warnings["css"]: - logly.warn(f"CSS files not found: {warnings['css']}") - if warnings["js"]: - logly.warn(f"JS files not found: {warnings['js']}") - if warnings["html"]: - logly.warn(f"HTML files not found: {warnings['html']}") - - return JSONResponse( - content={ - "css": cls.custom_css, - "js": cls.custom_js, - "html": cls.custom_html, - } - ) - - @cls.router.get( - "/api/config", - response_model=List[ReactFlowConfig], - response_model_exclude_none=True, - ) - async def get_config(): - return cls.config - - @cls.router.get("/static/{filename:path}") - async def get_static_file(filename: str): - return cls.serve_file(filename) - - @cls.router.post("/api/html") - async def set_html(content: HtmlContent): - cls.add_html(content.content) - return {"status": "success"} - - @cls.router.get("/api/html") - async def get_html(): - return {"content": cls.get_html()} - - cls.app.include_router(cls.router) - - -WebFlow_API.create_router() -WebFlow_API.initialize() diff --git a/webflow/modules/serve.py b/webflow/modules/serve.py deleted file mode 100644 index f8b49fa..0000000 --- a/webflow/modules/serve.py +++ /dev/null @@ -1,8 +0,0 @@ -import uvicorn -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware - -from webflow.modules import WebFlow_API -from webflow.modules.mount import mount_static_files - -app = WebFlow_API.app diff --git a/webflow/modules/types.py b/webflow/modules/types.py index 35063b4..262f203 100644 --- a/webflow/modules/types.py +++ b/webflow/modules/types.py @@ -66,17 +66,17 @@ class Metadata(BaseModel): ogImage: Optional[str] = None -class SideBar(BaseModel): - title: str - icon: str - url: str +class SideBar(BaseModel): + title: Optional[str] = None + icon: Optional[str] = None + url: Optional[str] = None class SidebarResponse(BaseModel): - visible: bool - label: str - default_open: bool - items: List[SideBar] + visible: Optional[bool] = None + label: Optional[str] = None + default_open: Optional[bool] = None + items: Optional[List[SideBar]] = None # Optional list class ReactFlowConfig(BaseModel): diff --git a/webflow/modules/webflow_api.py b/webflow/modules/webflow_api.py new file mode 100644 index 0000000..12e4547 --- /dev/null +++ b/webflow/modules/webflow_api.py @@ -0,0 +1,183 @@ +import datetime +from pathlib import Path +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import FileResponse +from typing import Dict, List, Optional + +from webflow.logly import logly +from webflow.modules.mount import mount_static_files +from webflow.modules.types import ( + NodeData, + EdgeData, + Metadata, + SideBar, + ReactFlowConfig, +) + + +def ensure_initialized(method): + def wrapper(cls, *args, **kwargs): + if not cls.initialized: + cls.initialize() + return method(cls, *args, **kwargs) + return wrapper + + +class WebFlow_API: + app = FastAPI() + nodes: List[NodeData] = [] + edges: List[EdgeData] = [] + metadata: Metadata = Metadata(title="PyWebflow", description="Webflow application") + config: List[ReactFlowConfig] = [] + custom_css: List[str] = [] + custom_js: List[str] = [] + custom_html: List[str] = [] + sidebar_visible: bool = False + sidebar_label: str = "Application" + sidebar_default_open: bool = False + sidebar: List[SideBar] = [] + static_dir: Optional[str] = None + initialized = False + html_store: List[str] = [] + + @classmethod + def initialize(cls): + if cls.initialized: + return + cls.app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + mount_static_files(cls.app, static_dir=cls.static_dir) + cls.initialized = True + + @classmethod + def refresh_static_files(cls): + cls.custom_css = [] + cls.custom_js = [] + cls.custom_html = [] + if cls.static_dir: + static_path = Path(cls.static_dir) + for file_path in static_path.rglob("*"): + if file_path.is_file(): + relative_path = file_path.relative_to(static_path) + if file_path.suffix == ".css": + cls.custom_css.append(f"/static/{relative_path}") + elif file_path.suffix == ".js": + cls.custom_js.append(f"/static/{relative_path}") + elif file_path.suffix == ".html": + cls.custom_html.append(f"/static/{relative_path}") + + @classmethod + @ensure_initialized + def add_node(cls, node_id: str, label: str, position: Dict[str, float], **kwargs): + node = NodeData(id=node_id, label=label, position=position, **kwargs) + cls.nodes.append(node) + + @classmethod + @ensure_initialized + def sidebar(visible: bool, label: str, default_open: bool, items: List[Dict[str, str]]): + WebFlow_API.sidebar_visible = visible + WebFlow_API.sidebar_label = label + WebFlow_API.sidebar_default_open = default_open + WebFlow_API.sidebar = [SideBar(**item) for item in items] + + @classmethod + @ensure_initialized + def add_edge(cls, edge_id: str, source: str, target: str, **kwargs): + edge = EdgeData(id=edge_id, source=source, target=target, **kwargs) + cls.edges.append(edge) + + @classmethod + @ensure_initialized + def set_metadata(cls, title: str, **kwargs): + cls.metadata = Metadata(title=title, **kwargs) + + @classmethod + @ensure_initialized + def set_config(cls, **kwargs): + config = ReactFlowConfig(**kwargs) + cls.config.append(config) + + @classmethod + def set_static_directory(cls, directory: str): + absolute_directory = Path(directory) + if absolute_directory.exists(): + mount_static_files(cls.app, static_dir=str(absolute_directory.resolve())) + cls.static_dir = str(absolute_directory.resolve()) + cls.refresh_static_files() + else: + raise ValueError(f"Directory {absolute_directory} does not exist.") + + @classmethod + def set_custom_css(cls, path: str): + if not cls.static_dir: + logly.warn("Static directory not set.") + return + abs_path = Path(cls.static_dir) / path + if not abs_path.exists(): + logly.warn(f"CSS file {path} not found.") + return + cls.custom_css.append(f"/static/{path}") + + @classmethod + def set_custom_js(cls, path: str): + if not cls.static_dir: + logly.warn("Static directory not set.") + return + abs_path = Path(cls.static_dir) / path + if not abs_path.exists(): + logly.warn(f"JS file {path} not found.") + return + cls.custom_js.append(f"/static/{path}") + + @classmethod + def set_custom_html(cls, path: str): + if not cls.static_dir: + logly.warn("Static directory not set.") + return + abs_path = Path(cls.static_dir) / path + if not abs_path.exists(): + logly.warn(f"HTML file {path} not found.") + return + cls.custom_html.append(f"/static/{path}") + + @classmethod + def serve_file(cls, filename: str): + if not cls.static_dir: + logly.warn("Static directory not set.") + raise HTTPException(status_code=500, detail="Static directory not set") + file_path = Path(cls.static_dir) / filename + if file_path.exists(): + media_type = "application/octet-stream" + if file_path.suffix == ".css": + media_type = "text/css" + elif file_path.suffix == ".js": + media_type = "application/javascript" + elif file_path.suffix == ".html": + media_type = "text/html" + headers = {"Cache-Control": "no-cache, no-store, must-revalidate"} + return FileResponse(str(file_path), media_type=media_type, headers=headers) + else: + logly.warn(f"{filename} not found in the static directory.") + raise HTTPException(status_code=404, detail=f"{filename} not found") + + @classmethod + @ensure_initialized + def add_html(cls, content: str): + cls.html_store.append(content) + + @classmethod + @ensure_initialized + def get_html(cls): + return cls.html_store + + @classmethod + def launch(cls, host: str = "127.0.0.1", port: int = 8000, reload: bool = True): + cls.initialize() + import uvicorn + uvicorn.run(cls.app, host=host, port=port, reload=reload) diff --git a/webflow/webflow.py b/webflow/webflow.py index 579f5e0..694c0da 100644 --- a/webflow/webflow.py +++ b/webflow/webflow.py @@ -3,12 +3,11 @@ from webflow.ascii import ascii_art from webflow.logly import logly -from webflow.modules import parse_arguments, app -from webflow.modules.routes import WebFlow_API, Metadata +from webflow.modules import parse_arguments, WebFlow_API def get_app(): - return app + return WebFlow_API.app def add_node(node_id: str, label: str, position: Dict[str, float], **kwargs): @@ -53,6 +52,7 @@ def config(**kwargs): def launch(attributes=True): args = parse_arguments() + WebFlow_API.initialize() if attributes: print(ascii_art) @@ -100,4 +100,4 @@ def launch(attributes=True): port=args.port, reload=args.reload, log_config=custom_log_config, - ) + ) \ No newline at end of file From 616b4897ee79012f733d1b9219d9ac2124d5858c Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Mon, 3 Mar 2025 16:18:53 +0530 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20Add=20example=20environment=20c?= =?UTF-8?q?onfiguration=20for=20development=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env-example | 1 + 1 file changed, 1 insertion(+) create mode 100644 .env-example diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..fad5c9d --- /dev/null +++ b/.env-example @@ -0,0 +1 @@ +HATCH_ENV=development \ No newline at end of file From 214a5163611dc0c005ae324fc6fb4c8df5e1bd71 Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Mon, 3 Mar 2025 16:30:39 +0530 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Refactor=20code=20f?= =?UTF-8?q?or=20improved=20readability=20and=20consistency=20across=20comp?= =?UTF-8?q?onents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 5 ++- bun.lockb | Bin 284280 -> 284296 bytes package.json | 3 +- packages/frontend/.husky/pre-commit | 1 - packages/frontend/package.json | 1 - packages/frontend/src/App.tsx | 4 +- packages/frontend/src/Layout.tsx | 38 +++++++++--------- packages/frontend/src/Root.tsx | 15 +++++-- .../frontend/src/components/InjectedHtml.tsx | 6 +-- packages/frontend/src/components/Metadata.tsx | 24 ++++++++--- packages/frontend/src/hooks/assets.ts | 5 ++- packages/frontend/src/styles.ts | 2 +- packages/frontend/vite.config.ts | 6 ++- 13 files changed, 67 insertions(+), 43 deletions(-) delete mode 100644 packages/frontend/.husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit index 4dfead0..4798a67 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,4 @@ -bun test +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +bun run format:frontend && bun run format:api diff --git a/bun.lockb b/bun.lockb index 9953289ad26c448081ece65bdfb62105ac4f65c3..d6bd3dc24241311287835ca87aa8871c772f7c91 100644 GIT binary patch delta 39270 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 z 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 delta 39371 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%0AW(R~w8`Qi|hy1He4G!Z-Ne*); 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 CzT=qy diff --git a/package.json b/package.json index a84884a..b7a25ab 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,17 @@ "packages/*" ], "scripts": { + "prepare": "husky install", "start:frontend": "bun run --cwd packages/frontend dev", "build:frontend": "bun run --cwd packages/frontend build", "lint:frontend": "bun run --cwd packages/frontend lint", "preview:frontend": "bun run --cwd packages/frontend preview", - "prepare": "husky", "format": "bun run format:frontend && bun run format:api", "format:frontend": "prettier --write 'packages/frontend/**/*.{js,ts,tsx,json,md,scss,css}'", "format:api": "prettier --write 'packages/api/**/*.{js,ts,tsx,json,md,scss,css}'" }, "devDependencies": { + "husky": "^9.1.7", "prettier": "3.5.1" }, "packageManager": "bun@latest" diff --git a/packages/frontend/.husky/pre-commit b/packages/frontend/.husky/pre-commit deleted file mode 100644 index 4dfead0..0000000 --- a/packages/frontend/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -bun test diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 2e25846..ccf1b63 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -83,7 +83,6 @@ "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", - "husky": "^9.1.7", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", "rollup-plugin-visualizer": "^5.14.0", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 367146a..48e396c 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -12,7 +12,9 @@ import { } from '@pywebflow/api/src/sidebar.ts'; export function App() { - const [sidebarItems, setSidebarItems] = useState([]); + const [sidebarItems, setSidebarItems] = useState( + [], + ); const [sidebarVisible, setSidebarVisible] = useState(false); const [sidebarLabel, setSidebarLabel] = useState(''); const [defaultOpen, setDefaultOpen] = useState(false); diff --git a/packages/frontend/src/Layout.tsx b/packages/frontend/src/Layout.tsx index c0ffff1..69d51a5 100644 --- a/packages/frontend/src/Layout.tsx +++ b/packages/frontend/src/Layout.tsx @@ -30,29 +30,27 @@ export default function Layout() { const { theme, setTheme, systemTheme } = useTheme(); const currentTheme = theme === 'system' ? systemTheme : theme; -useEffect(() => { - const loadInitialConfig = async () => { - try { - const fetchedConfig = await getConfig(); - - if (Array.isArray(fetchedConfig) && fetchedConfig.length > 0) { - if (fetchedConfig[0]?.colorMode) { - setTheme(fetchedConfig[0].colorMode); + useEffect(() => { + const loadInitialConfig = async () => { + try { + const fetchedConfig = await getConfig(); + + if (Array.isArray(fetchedConfig) && fetchedConfig.length > 0) { + if (fetchedConfig[0]?.colorMode) { + setTheme(fetchedConfig[0].colorMode); + } + setConfig(fetchedConfig[0]); + } else { + setConfig({}); } - setConfig(fetchedConfig[0]); - } else { - setConfig({}); + } catch (error) { + console.error('Error fetching initial config:', error); + setConfig({}); // Fallback to empty object on error } - } catch (error) { - console.error("Error fetching initial config:", error); - setConfig({}); // Fallback to empty object on error - } - }; - - loadInitialConfig(); -}, [setTheme]); - + }; + loadInitialConfig(); + }, [setTheme]); useEffect(() => { const updateProgress = () => { diff --git a/packages/frontend/src/Root.tsx b/packages/frontend/src/Root.tsx index ce2793e..0864007 100644 --- a/packages/frontend/src/Root.tsx +++ b/packages/frontend/src/Root.tsx @@ -10,7 +10,9 @@ import { getConfig } from '@pywebflow/api/src/config'; // Lazy load components with preloading const Layout = React.lazy(() => import(/* webpackPreload: true */ './Layout')); -const MetaData = React.lazy(() => import(/* webpackPreload: true */ './components/Metadata')); +const MetaData = React.lazy( + () => import(/* webpackPreload: true */ './components/Metadata'), +); // Preload critical components asynchronously const preloadAssets = () => { @@ -22,7 +24,9 @@ preloadAssets(); const Root = () => { const [mounted, setMounted] = useState(false); - const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>('system'); + const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>( + 'system', + ); useEffect(() => { // Fetch config data @@ -45,7 +49,12 @@ const Root = () => { return ( - + {mounted && ( diff --git a/packages/frontend/src/components/InjectedHtml.tsx b/packages/frontend/src/components/InjectedHtml.tsx index 2dc131f..e05f7b9 100644 --- a/packages/frontend/src/components/InjectedHtml.tsx +++ b/packages/frontend/src/components/InjectedHtml.tsx @@ -18,12 +18,10 @@ const InjectedHtml: React.FC = () => { return (
{htmlContents.map((htmlContent, index) => ( -
- {parse(DOMPurify.sanitize(htmlContent))} -
+
{parse(DOMPurify.sanitize(htmlContent))}
))}
); }; -export default InjectedHtml; \ No newline at end of file +export default InjectedHtml; diff --git a/packages/frontend/src/components/Metadata.tsx b/packages/frontend/src/components/Metadata.tsx index 872e7f4..a0b5c6b 100644 --- a/packages/frontend/src/components/Metadata.tsx +++ b/packages/frontend/src/components/Metadata.tsx @@ -30,17 +30,29 @@ const MetaData: React.FC = () => { return ( {metadata.title} - {metadata.description && } - {metadata.keywords && } + {metadata.description && ( + + )} + {metadata.keywords && ( + + )} {metadata.author && } - {metadata.viewport && } + {metadata.viewport && ( + + )} {metadata.charset && } {metadata.robots && } {metadata.canonical && } - {metadata.ogTitle && } - {metadata.ogDescription && } + {metadata.ogTitle && ( + + )} + {metadata.ogDescription && ( + + )} {metadata.ogUrl && } - {metadata.ogImage && } + {metadata.ogImage && ( + + )} ); }; diff --git a/packages/frontend/src/hooks/assets.ts b/packages/frontend/src/hooks/assets.ts index 5c0a31a..aeb0179 100644 --- a/packages/frontend/src/hooks/assets.ts +++ b/packages/frontend/src/hooks/assets.ts @@ -10,7 +10,9 @@ export const injectAssets = async (): Promise => { return; } - const cssFiles = Array.isArray(assets.css) ? assets.css.filter(Boolean) : []; + const cssFiles = Array.isArray(assets.css) + ? assets.css.filter(Boolean) + : []; const jsFiles = Array.isArray(assets.js) ? assets.js.filter(Boolean) : []; // Inject CSS files @@ -35,7 +37,6 @@ export const injectAssets = async (): Promise => { console.log(`Injected JS: ${src}`); } }); - } catch (error) { console.error('Error loading assets:', error); } diff --git a/packages/frontend/src/styles.ts b/packages/frontend/src/styles.ts index d0ee88c..b8f6ea6 100644 --- a/packages/frontend/src/styles.ts +++ b/packages/frontend/src/styles.ts @@ -1,4 +1,4 @@ import './styles/index.scss'; import '@xyflow/react/dist/style.css'; import '@radix-ui/themes/styles.css'; -import '@mantine/core/styles.css'; \ No newline at end of file +import '@mantine/core/styles.css'; diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 8943208..d0e932a 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,12 +1,14 @@ import { defineConfig, PluginOption } from 'vite'; -import react from "@vitejs/plugin-react-swc"; +import react from '@vitejs/plugin-react-swc'; import path from 'path'; import tailwindcss from '@tailwindcss/vite'; import { visualizer } from 'rollup-plugin-visualizer'; const isProduction = process.env.NODE_ENV === 'production'; -const API_BASE_URL = isProduction ? 'https://your-production-api.com' : 'http://127.0.0.1:8000'; +const API_BASE_URL = isProduction + ? 'https://your-production-api.com' + : 'http://127.0.0.1:8000'; export default defineConfig({ plugins: [react(), tailwindcss(), visualizer() as PluginOption], From c9223c4c10b797ecb892a54b21df4ac7f4497c8a Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Mon, 3 Mar 2025 16:36:03 +0530 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=90=9B=20Update=20husky=20prepare=20s?= =?UTF-8?q?cript=20and=20simplify=20pre-commit=20formatting=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 4798a67..c0ff859 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -bun run format:frontend && bun run format:api +bun run format diff --git a/package.json b/package.json index b7a25ab..f4422e3 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "packages/*" ], "scripts": { - "prepare": "husky install", + "prepare": "husky", "start:frontend": "bun run --cwd packages/frontend dev", "build:frontend": "bun run --cwd packages/frontend build", "lint:frontend": "bun run --cwd packages/frontend lint", From 7267998a4b0afb7da4174e25a5a48e185dccda4b Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Fri, 7 Mar 2025 21:18:54 +0530 Subject: [PATCH 5/6] =?UTF-8?q?=E2=9C=A8=20Implement=20authentication=20fu?= =?UTF-8?q?nctionality=20and=20enhance=20routing=20with=20dynamic=20loadin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bun.lockb | Bin 284296 -> 290728 bytes package.json | 2 +- packages/frontend/package.json | 26 +- packages/frontend/src/App.tsx | 75 +---- packages/frontend/src/Flow.tsx | 71 +++++ packages/frontend/src/NotFound.tsx | 13 + packages/frontend/src/Root.tsx | 80 +---- packages/frontend/src/ThemeProvider.tsx | 83 ++++++ packages/frontend/src/components/loader.tsx | 16 + .../src/components/loadingcomponent.tsx | 29 ++ packages/frontend/src/feature-flags.ts | 2 + packages/frontend/src/routes.tsx | 38 +++ packages/frontend/src/utils/types.ts | 5 + pyproject.toml | 6 +- requirements.txt | 8 + uv.lock | 277 ++++++++++++++++-- webflow/__init__.py | 2 + webflow/modules/auth.py | 26 ++ webflow/modules/routes.py | 12 +- webflow/modules/webflow_api.py | 18 +- webflow/webflow.py | 13 +- 21 files changed, 610 insertions(+), 192 deletions(-) create mode 100644 packages/frontend/src/Flow.tsx create mode 100644 packages/frontend/src/NotFound.tsx create mode 100644 packages/frontend/src/ThemeProvider.tsx create mode 100644 packages/frontend/src/components/loader.tsx create mode 100644 packages/frontend/src/components/loadingcomponent.tsx create mode 100644 packages/frontend/src/feature-flags.ts create mode 100644 packages/frontend/src/routes.tsx create mode 100644 webflow/modules/auth.py diff --git a/bun.lockb b/bun.lockb index d6bd3dc24241311287835ca87aa8871c772f7c91..ef427fdfd4bc91855a2a67a2037ee48feca37a8e 100644 GIT binary patch delta 64222 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 delta 60476 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 z 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 TcvUMytf#{I)r?Grspx+I!5weL diff --git a/package.json b/package.json index f4422e3..7ddbffe 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "husky": "^9.1.7", - "prettier": "3.5.1" + "prettier": "3.5.3" }, "packageManager": "bun@latest" } diff --git a/packages/frontend/package.json b/packages/frontend/package.json index ccf1b63..2edcf12 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -16,6 +16,7 @@ "prepare": "husky" }, "dependencies": { + "@headlessui/react": "^2.2.0", "@mantine/carousel": "^7.17.1", "@mantine/charts": "^7.17.1", "@mantine/code-highlight": "^7.17.1", @@ -38,9 +39,9 @@ "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/themes": "^3.2.1", - "@tabler/icons-react": "^3.30.0", - "@tailwindcss/postcss": "^4.0.9", - "@tailwindcss/vite": "^4.0.9", + "@tabler/icons-react": "^3.31.0", + "@tailwindcss/postcss": "^4.0.12", + "@tailwindcss/vite": "^4.0.12", "@tiptap/extension-link": "^2.11.5", "@tiptap/pm": "^2.11.5", "@tiptap/react": "^2.11.5", @@ -52,7 +53,7 @@ "dayjs": "^1.11.13", "dompurify": "^3.2.4", "embla-carousel-react": "^8.5.2", - "framer-motion": "^12.4.7", + "framer-motion": "^12.4.10", "html-react-parser": "^5.2.2", "lucide-react": "^0.477.0", "next-themes": "^0.4.4", @@ -62,24 +63,25 @@ "react-helmet-async": "^2.0.5", "react-icons": "^5.5.0", "react-remark": "^2.1.0", - "react-router-dom": "^7.2.0", + "react-router-dom": "^7.3.0", "recharts": "^2.15.1", "sass": "^1.85.1", "tailwind-merge": "^3.0.2", - "tailwindcss": "^4.0.9", - "tailwindcss-animate": "^1.0.7" + "tailwindcss": "^4.0.12", + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.3" }, "license": "apache-2.0", "devDependencies": { - "@types/node": "^22.13.8", + "@types/node": "^22.13.9", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", - "@typescript-eslint/eslint-plugin": "^8.25.0", - "@typescript-eslint/parser": "^8.25.0", + "@typescript-eslint/eslint-plugin": "^8.26.0", + "@typescript-eslint/parser": "^8.26.0", "@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react-swc": "^3.8.0", "eslint": "^9.21.0", - "eslint-config-prettier": "^10.0.2", + "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.19", @@ -87,7 +89,7 @@ "postcss-simple-vars": "^7.0.1", "rollup-plugin-visualizer": "^5.14.0", "typescript": "^5.8.2", - "vite": "^6.2.0" + "vite": "^6.2.1" }, "keywords": [ "pywebflow", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 48e396c..73e0627 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,71 +1,12 @@ -import { useEffect, useState } from 'react'; -import ControlsComp from './components/Controls.tsx'; -import MinimapComp from './components/Minimap.tsx'; -import Status from './components/Status.tsx'; -import { SidebarProvider, SidebarTrigger } from './components/ui/sidebar.tsx'; -import AppSidebar from './components/App-Sidebar.tsx'; -import BackgroundWrapper from './components/BackgroundWrapper.tsx'; -import InjectedHtml from './components/InjectedHtml.tsx'; -import { - getSidebarItems, - SidebarResponse, -} from '@pywebflow/api/src/sidebar.ts'; - -export function App() { - const [sidebarItems, setSidebarItems] = useState( - [], - ); - const [sidebarVisible, setSidebarVisible] = useState(false); - const [sidebarLabel, setSidebarLabel] = useState(''); - const [defaultOpen, setDefaultOpen] = useState(false); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchSidebarItems = async () => { - try { - setLoading(true); - setError(null); - - const response: SidebarResponse = await getSidebarItems(); - setSidebarItems(response.items); - setSidebarVisible(response.visible); - setSidebarLabel(response.label); - setDefaultOpen(response.default_open); - } catch (err) { - console.error('Error fetching sidebar items:', err); - setError('Failed to load sidebar'); - } finally { - setLoading(false); - } - }; - - fetchSidebarItems(); - }, []); +import { Suspense } from 'react'; +import { RouterProvider } from 'react-router-dom'; +import router from './routes.tsx'; +import { LoadingPage } from './components/loader.tsx'; +export default function App() { return ( -
- -
- {loading &&

Loading sidebar...

} - {error &&

{error}

} - - {!loading && !error && sidebarVisible && ( - - )} - {!loading && !error && sidebarVisible && } - -
- - - - - -
-
-
-
+ }> + + ); } - -export default App; diff --git a/packages/frontend/src/Flow.tsx b/packages/frontend/src/Flow.tsx new file mode 100644 index 0000000..48e396c --- /dev/null +++ b/packages/frontend/src/Flow.tsx @@ -0,0 +1,71 @@ +import { useEffect, useState } from 'react'; +import ControlsComp from './components/Controls.tsx'; +import MinimapComp from './components/Minimap.tsx'; +import Status from './components/Status.tsx'; +import { SidebarProvider, SidebarTrigger } from './components/ui/sidebar.tsx'; +import AppSidebar from './components/App-Sidebar.tsx'; +import BackgroundWrapper from './components/BackgroundWrapper.tsx'; +import InjectedHtml from './components/InjectedHtml.tsx'; +import { + getSidebarItems, + SidebarResponse, +} from '@pywebflow/api/src/sidebar.ts'; + +export function App() { + const [sidebarItems, setSidebarItems] = useState( + [], + ); + const [sidebarVisible, setSidebarVisible] = useState(false); + const [sidebarLabel, setSidebarLabel] = useState(''); + const [defaultOpen, setDefaultOpen] = useState(false); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchSidebarItems = async () => { + try { + setLoading(true); + setError(null); + + const response: SidebarResponse = await getSidebarItems(); + setSidebarItems(response.items); + setSidebarVisible(response.visible); + setSidebarLabel(response.label); + setDefaultOpen(response.default_open); + } catch (err) { + console.error('Error fetching sidebar items:', err); + setError('Failed to load sidebar'); + } finally { + setLoading(false); + } + }; + + fetchSidebarItems(); + }, []); + + return ( +
+ +
+ {loading &&

Loading sidebar...

} + {error &&

{error}

} + + {!loading && !error && sidebarVisible && ( + + )} + {!loading && !error && sidebarVisible && } + +
+ + + + + +
+
+
+
+ ); +} + +export default App; diff --git a/packages/frontend/src/NotFound.tsx b/packages/frontend/src/NotFound.tsx new file mode 100644 index 0000000..8cde3f9 --- /dev/null +++ b/packages/frontend/src/NotFound.tsx @@ -0,0 +1,13 @@ +import { Link } from "react-router-dom"; + +export default function NotFound() { + return ( +
+

404

+

Oops! The page you're looking for doesn't exist.

+ + Go Home + +
+ ); +} diff --git a/packages/frontend/src/Root.tsx b/packages/frontend/src/Root.tsx index 0864007..c68299d 100644 --- a/packages/frontend/src/Root.tsx +++ b/packages/frontend/src/Root.tsx @@ -1,83 +1,9 @@ import './styles.ts'; -import React, { Suspense, useEffect, useState } from 'react'; import ReactDOM from 'react-dom/client'; -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import { MantineProvider } from '@mantine/core'; import { ThemeProvider } from 'next-themes'; -import { Theme } from '@radix-ui/themes'; -import { HelmetProvider } from 'react-helmet-async'; -import { getConfig } from '@pywebflow/api/src/config'; -// Lazy load components with preloading -const Layout = React.lazy(() => import(/* webpackPreload: true */ './Layout')); -const MetaData = React.lazy( - () => import(/* webpackPreload: true */ './components/Metadata'), +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement, ); -// Preload critical components asynchronously -const preloadAssets = () => { - import(/* webpackPreload: true */ './Layout'); - import(/* webpackPreload: true */ './components/Metadata'); -}; - -preloadAssets(); - -const Root = () => { - const [mounted, setMounted] = useState(false); - const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>( - 'system', - ); - - useEffect(() => { - // Fetch config data - const fetchConfig = async () => { - try { - const fetchedConfig = await getConfig(); - if (fetchedConfig?.data?.length > 0) { - setDefaultTheme(fetchedConfig.data[0].colorMode || 'system'); - } - } catch (error) { - console.error('Error fetching config:', error); - } - }; - fetchConfig(); - }, []); - - useEffect(() => { - setMounted(true); - }, []); - - return ( - - - - - {mounted && ( - - - } /> - - - )} - - - - - - - - ); -}; - -export default Root; - -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -); +root.render(); diff --git a/packages/frontend/src/ThemeProvider.tsx b/packages/frontend/src/ThemeProvider.tsx new file mode 100644 index 0000000..0864007 --- /dev/null +++ b/packages/frontend/src/ThemeProvider.tsx @@ -0,0 +1,83 @@ +import './styles.ts'; +import React, { Suspense, useEffect, useState } from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import { MantineProvider } from '@mantine/core'; +import { ThemeProvider } from 'next-themes'; +import { Theme } from '@radix-ui/themes'; +import { HelmetProvider } from 'react-helmet-async'; +import { getConfig } from '@pywebflow/api/src/config'; + +// Lazy load components with preloading +const Layout = React.lazy(() => import(/* webpackPreload: true */ './Layout')); +const MetaData = React.lazy( + () => import(/* webpackPreload: true */ './components/Metadata'), +); + +// Preload critical components asynchronously +const preloadAssets = () => { + import(/* webpackPreload: true */ './Layout'); + import(/* webpackPreload: true */ './components/Metadata'); +}; + +preloadAssets(); + +const Root = () => { + const [mounted, setMounted] = useState(false); + const [defaultTheme, setDefaultTheme] = useState<'light' | 'dark' | 'system'>( + 'system', + ); + + useEffect(() => { + // Fetch config data + const fetchConfig = async () => { + try { + const fetchedConfig = await getConfig(); + if (fetchedConfig?.data?.length > 0) { + setDefaultTheme(fetchedConfig.data[0].colorMode || 'system'); + } + } catch (error) { + console.error('Error fetching config:', error); + } + }; + fetchConfig(); + }, []); + + useEffect(() => { + setMounted(true); + }, []); + + return ( + + + + + {mounted && ( + + + } /> + + + )} + + + + + + + + ); +}; + +export default Root; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/packages/frontend/src/components/loader.tsx b/packages/frontend/src/components/loader.tsx new file mode 100644 index 0000000..1efc862 --- /dev/null +++ b/packages/frontend/src/components/loader.tsx @@ -0,0 +1,16 @@ +import { cn } from '../lib/utils.ts'; +import LoadingComponent from './loadingcomponent.tsx'; + + +export function LoadingPage({ overlay = false }: { overlay?: boolean }) { + return ( +
+ +
+ ); +} diff --git a/packages/frontend/src/components/loadingcomponent.tsx b/packages/frontend/src/components/loadingcomponent.tsx new file mode 100644 index 0000000..c347475 --- /dev/null +++ b/packages/frontend/src/components/loadingcomponent.tsx @@ -0,0 +1,29 @@ +import { JSX } from 'react'; +import { LoadingComponentProps } from '../utils/types.ts'; + +export default function LoadingComponent({ + remSize, +}: LoadingComponentProps): JSX.Element { + return ( +
+ +

+ Loading... +
+ ); +} diff --git a/packages/frontend/src/feature-flags.ts b/packages/frontend/src/feature-flags.ts new file mode 100644 index 0000000..1ffd3eb --- /dev/null +++ b/packages/frontend/src/feature-flags.ts @@ -0,0 +1,2 @@ +export const ENABLE_DARK_MODE = true; +export const ENABLE_API = true; diff --git a/packages/frontend/src/routes.tsx b/packages/frontend/src/routes.tsx new file mode 100644 index 0000000..b95bee0 --- /dev/null +++ b/packages/frontend/src/routes.tsx @@ -0,0 +1,38 @@ +import { createBrowserRouter, RouteObject } from "react-router-dom"; +import { lazy, Suspense, useEffect, useState } from "react"; +import { LoadingPage } from "./components/loader.tsx"; +import axios from "axios"; + +const NotFound = lazy(() => import("./NotFound.tsx")); + +const fetchRoutes = async () => { + try { + const response = await axios.get("/api/routes"); // Fetch route config from API + return response.data; + } catch (error) { + console.error("Failed to fetch routes", error); + return []; + } +}; + +const DynamicRoutes = () => { + const [routes, setRoutes] = useState([]); + + useEffect(() => { + fetchRoutes().then((data) => { + const mappedRoutes = data.map((route: any) => ({ + path: route.path, + element: }>, + })); + setRoutes(mappedRoutes); + }); + }, []); + + return createBrowserRouter([ + ...routes, + { path: "*", element: }, + ]); +}; + +const router = DynamicRoutes(); +export default router; diff --git a/packages/frontend/src/utils/types.ts b/packages/frontend/src/utils/types.ts index aa58b0a..d58f174 100644 --- a/packages/frontend/src/utils/types.ts +++ b/packages/frontend/src/utils/types.ts @@ -1,5 +1,10 @@ import React from 'react'; + +export type LoadingComponentProps = { + remSize: number; +}; + // NodeData Type export interface NodeData { id: string; diff --git a/pyproject.toml b/pyproject.toml index 99653d0..b7e4c10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,8 +33,12 @@ classifiers = [ "Topic :: Software Development :: Libraries" ] dependencies = [ + "cryptography>=44.0.2", "fastapi>=0.115.6,<0.116.0", "logly @ git+https://github.com/muhammad-fiaz/logly.git@9920c22e118facaa5f3145df0a9b4b9e9b9a8839", + "passlib[bcrypt]>=1.7.4", + "pyjwt>=2.10.1", + "python-dotenv>=1.0.1", "uvicorn>=0.34.0,<0.35.0", ] @@ -84,7 +88,7 @@ source = "uv-dynamic-versioning" [tool.uv-dynamic-versioning] vcs = "git" metadata = false -style = "semver" +style = "pep440" latest-tag = true bump = true diff --git a/requirements.txt b/requirements.txt index f6f66ef..578412c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,8 @@ annotated-types==0.7.0 # via pydantic anyio==4.8.0 # via starlette +bcrypt==4.3.0 + # via passlib click==8.1.8 # via uvicorn colorama==0.4.6 @@ -27,6 +29,8 @@ mdurl==0.1.2 # via markdown-it-py packaging==24.2 # via pytest +passlib==1.7.4 + # via pywebflow (pyproject.toml) pluggy==1.5.0 # via pytest pydantic==2.10.4 @@ -37,8 +41,12 @@ pydantic-core==2.27.2 # via pydantic pygments==2.19.1 # via rich +pyjwt==2.10.1 + # via pywebflow (pyproject.toml) pytest==8.3.4 # via pywebflow (pyproject.toml) +python-dotenv==1.0.1 + # via pywebflow (pyproject.toml) rich==13.9.4 # via logly ruff==0.9.6 diff --git a/uv.lock b/uv.lock index cc22837..43c8b5c 100644 --- a/uv.lock +++ b/uv.lock @@ -25,6 +25,133 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, ] +[[package]] +name = "bcrypt" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/5d/6d7433e0f3cd46ce0b43cd65e1db465ea024dbb8216fb2404e919c2ad77b/bcrypt-4.3.0.tar.gz", hash = "sha256:3a3fd2204178b6d2adcf09cb4f6426ffef54762577a7c9b54c159008cb288c18", size = 25697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/2c/3d44e853d1fe969d229bd58d39ae6902b3d924af0e2b5a60d17d4b809ded/bcrypt-4.3.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f01e060f14b6b57bbb72fc5b4a83ac21c443c9a2ee708e04a10e9192f90a6281", size = 483719 }, + { url = "https://files.pythonhosted.org/packages/a1/e2/58ff6e2a22eca2e2cff5370ae56dba29d70b1ea6fc08ee9115c3ae367795/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5eeac541cefd0bb887a371ef73c62c3cd78535e4887b310626036a7c0a817bb", size = 272001 }, + { url = "https://files.pythonhosted.org/packages/37/1f/c55ed8dbe994b1d088309e366749633c9eb90d139af3c0a50c102ba68a1a/bcrypt-4.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59e1aa0e2cd871b08ca146ed08445038f42ff75968c7ae50d2fdd7860ade2180", size = 277451 }, + { url = "https://files.pythonhosted.org/packages/d7/1c/794feb2ecf22fe73dcfb697ea7057f632061faceb7dcf0f155f3443b4d79/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f", size = 272792 }, + { url = "https://files.pythonhosted.org/packages/13/b7/0b289506a3f3598c2ae2bdfa0ea66969812ed200264e3f61df77753eee6d/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74a8d21a09f5e025a9a23e7c0fd2c7fe8e7503e4d356c0a2c1486ba010619f09", size = 289752 }, + { url = "https://files.pythonhosted.org/packages/dc/24/d0fb023788afe9e83cc118895a9f6c57e1044e7e1672f045e46733421fe6/bcrypt-4.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:0142b2cb84a009f8452c8c5a33ace5e3dfec4159e7735f5afe9a4d50a8ea722d", size = 277762 }, + { url = "https://files.pythonhosted.org/packages/e4/38/cde58089492e55ac4ef6c49fea7027600c84fd23f7520c62118c03b4625e/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:12fa6ce40cde3f0b899729dbd7d5e8811cb892d31b6f7d0334a1f37748b789fd", size = 272384 }, + { url = "https://files.pythonhosted.org/packages/de/6a/d5026520843490cfc8135d03012a413e4532a400e471e6188b01b2de853f/bcrypt-4.3.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:5bd3cca1f2aa5dbcf39e2aa13dd094ea181f48959e1071265de49cc2b82525af", size = 277329 }, + { url = "https://files.pythonhosted.org/packages/b3/a3/4fc5255e60486466c389e28c12579d2829b28a527360e9430b4041df4cf9/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:335a420cfd63fc5bc27308e929bee231c15c85cc4c496610ffb17923abf7f231", size = 305241 }, + { url = "https://files.pythonhosted.org/packages/c7/15/2b37bc07d6ce27cc94e5b10fd5058900eb8fb11642300e932c8c82e25c4a/bcrypt-4.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:0e30e5e67aed0187a1764911af023043b4542e70a7461ad20e837e94d23e1d6c", size = 309617 }, + { url = "https://files.pythonhosted.org/packages/5f/1f/99f65edb09e6c935232ba0430c8c13bb98cb3194b6d636e61d93fe60ac59/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b8d62290ebefd49ee0b3ce7500f5dbdcf13b81402c05f6dafab9a1e1b27212f", size = 335751 }, + { url = "https://files.pythonhosted.org/packages/00/1b/b324030c706711c99769988fcb694b3cb23f247ad39a7823a78e361bdbb8/bcrypt-4.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2ef6630e0ec01376f59a006dc72918b1bf436c3b571b80fa1968d775fa02fe7d", size = 355965 }, + { url = "https://files.pythonhosted.org/packages/aa/dd/20372a0579dd915dfc3b1cd4943b3bca431866fcb1dfdfd7518c3caddea6/bcrypt-4.3.0-cp313-cp313t-win32.whl", hash = "sha256:7a4be4cbf241afee43f1c3969b9103a41b40bcb3a3f467ab19f891d9bc4642e4", size = 155316 }, + { url = "https://files.pythonhosted.org/packages/6d/52/45d969fcff6b5577c2bf17098dc36269b4c02197d551371c023130c0f890/bcrypt-4.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c1949bf259a388863ced887c7861da1df681cb2388645766c89fdfd9004c669", size = 147752 }, + { url = "https://files.pythonhosted.org/packages/11/22/5ada0b9af72b60cbc4c9a399fdde4af0feaa609d27eb0adc61607997a3fa/bcrypt-4.3.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:f81b0ed2639568bf14749112298f9e4e2b28853dab50a8b357e31798686a036d", size = 498019 }, + { url = "https://files.pythonhosted.org/packages/b8/8c/252a1edc598dc1ce57905be173328eda073083826955ee3c97c7ff5ba584/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:864f8f19adbe13b7de11ba15d85d4a428c7e2f344bac110f667676a0ff84924b", size = 279174 }, + { url = "https://files.pythonhosted.org/packages/29/5b/4547d5c49b85f0337c13929f2ccbe08b7283069eea3550a457914fc078aa/bcrypt-4.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e36506d001e93bffe59754397572f21bb5dc7c83f54454c990c74a468cd589e", size = 283870 }, + { url = "https://files.pythonhosted.org/packages/be/21/7dbaf3fa1745cb63f776bb046e481fbababd7d344c5324eab47f5ca92dd2/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:842d08d75d9fe9fb94b18b071090220697f9f184d4547179b60734846461ed59", size = 279601 }, + { url = "https://files.pythonhosted.org/packages/6d/64/e042fc8262e971347d9230d9abbe70d68b0a549acd8611c83cebd3eaec67/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7c03296b85cb87db865d91da79bf63d5609284fc0cab9472fdd8367bbd830753", size = 297660 }, + { url = "https://files.pythonhosted.org/packages/50/b8/6294eb84a3fef3b67c69b4470fcdd5326676806bf2519cda79331ab3c3a9/bcrypt-4.3.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:62f26585e8b219cdc909b6a0069efc5e4267e25d4a3770a364ac58024f62a761", size = 284083 }, + { url = "https://files.pythonhosted.org/packages/62/e6/baff635a4f2c42e8788fe1b1633911c38551ecca9a749d1052d296329da6/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:beeefe437218a65322fbd0069eb437e7c98137e08f22c4660ac2dc795c31f8bb", size = 279237 }, + { url = "https://files.pythonhosted.org/packages/39/48/46f623f1b0c7dc2e5de0b8af5e6f5ac4cc26408ac33f3d424e5ad8da4a90/bcrypt-4.3.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:97eea7408db3a5bcce4a55d13245ab3fa566e23b4c67cd227062bb49e26c585d", size = 283737 }, + { url = "https://files.pythonhosted.org/packages/49/8b/70671c3ce9c0fca4a6cc3cc6ccbaa7e948875a2e62cbd146e04a4011899c/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:191354ebfe305e84f344c5964c7cd5f924a3bfc5d405c75ad07f232b6dffb49f", size = 312741 }, + { url = "https://files.pythonhosted.org/packages/27/fb/910d3a1caa2d249b6040a5caf9f9866c52114d51523ac2fb47578a27faee/bcrypt-4.3.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:41261d64150858eeb5ff43c753c4b216991e0ae16614a308a15d909503617732", size = 316472 }, + { url = "https://files.pythonhosted.org/packages/dc/cf/7cf3a05b66ce466cfb575dbbda39718d45a609daa78500f57fa9f36fa3c0/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:33752b1ba962ee793fa2b6321404bf20011fe45b9afd2a842139de3011898fef", size = 343606 }, + { url = "https://files.pythonhosted.org/packages/e3/b8/e970ecc6d7e355c0d892b7f733480f4aa8509f99b33e71550242cf0b7e63/bcrypt-4.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:50e6e80a4bfd23a25f5c05b90167c19030cf9f87930f7cb2eacb99f45d1c3304", size = 362867 }, + { url = "https://files.pythonhosted.org/packages/a9/97/8d3118efd8354c555a3422d544163f40d9f236be5b96c714086463f11699/bcrypt-4.3.0-cp38-abi3-win32.whl", hash = "sha256:67a561c4d9fb9465ec866177e7aebcad08fe23aaf6fbd692a6fab69088abfc51", size = 160589 }, + { url = "https://files.pythonhosted.org/packages/29/07/416f0b99f7f3997c69815365babbc2e8754181a4b1899d921b3c7d5b6f12/bcrypt-4.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:584027857bc2843772114717a7490a37f68da563b3620f78a849bcb54dc11e62", size = 152794 }, + { url = "https://files.pythonhosted.org/packages/6e/c1/3fa0e9e4e0bfd3fd77eb8b52ec198fd6e1fd7e9402052e43f23483f956dd/bcrypt-4.3.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0d3efb1157edebfd9128e4e46e2ac1a64e0c1fe46fb023158a407c7892b0f8c3", size = 498969 }, + { url = "https://files.pythonhosted.org/packages/ce/d4/755ce19b6743394787fbd7dff6bf271b27ee9b5912a97242e3caf125885b/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08bacc884fd302b611226c01014eca277d48f0a05187666bca23aac0dad6fe24", size = 279158 }, + { url = "https://files.pythonhosted.org/packages/9b/5d/805ef1a749c965c46b28285dfb5cd272a7ed9fa971f970435a5133250182/bcrypt-4.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6746e6fec103fcd509b96bacdfdaa2fbde9a553245dbada284435173a6f1aef", size = 284285 }, + { url = "https://files.pythonhosted.org/packages/ab/2b/698580547a4a4988e415721b71eb45e80c879f0fb04a62da131f45987b96/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:afe327968aaf13fc143a56a3360cb27d4ad0345e34da12c7290f1b00b8fe9a8b", size = 279583 }, + { url = "https://files.pythonhosted.org/packages/f2/87/62e1e426418204db520f955ffd06f1efd389feca893dad7095bf35612eec/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d9af79d322e735b1fc33404b5765108ae0ff232d4b54666d46730f8ac1a43676", size = 297896 }, + { url = "https://files.pythonhosted.org/packages/cb/c6/8fedca4c2ada1b6e889c52d2943b2f968d3427e5d65f595620ec4c06fa2f/bcrypt-4.3.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f1e3ffa1365e8702dc48c8b360fef8d7afeca482809c5e45e653af82ccd088c1", size = 284492 }, + { url = "https://files.pythonhosted.org/packages/4d/4d/c43332dcaaddb7710a8ff5269fcccba97ed3c85987ddaa808db084267b9a/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3004df1b323d10021fda07a813fd33e0fd57bef0e9a480bb143877f6cba996fe", size = 279213 }, + { url = "https://files.pythonhosted.org/packages/dc/7f/1e36379e169a7df3a14a1c160a49b7b918600a6008de43ff20d479e6f4b5/bcrypt-4.3.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:531457e5c839d8caea9b589a1bcfe3756b0547d7814e9ce3d437f17da75c32b0", size = 284162 }, + { url = "https://files.pythonhosted.org/packages/1c/0a/644b2731194b0d7646f3210dc4d80c7fee3ecb3a1f791a6e0ae6bb8684e3/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:17a854d9a7a476a89dcef6c8bd119ad23e0f82557afbd2c442777a16408e614f", size = 312856 }, + { url = "https://files.pythonhosted.org/packages/dc/62/2a871837c0bb6ab0c9a88bf54de0fc021a6a08832d4ea313ed92a669d437/bcrypt-4.3.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6fb1fd3ab08c0cbc6826a2e0447610c6f09e983a281b919ed721ad32236b8b23", size = 316726 }, + { url = "https://files.pythonhosted.org/packages/0c/a1/9898ea3faac0b156d457fd73a3cb9c2855c6fd063e44b8522925cdd8ce46/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e965a9c1e9a393b8005031ff52583cedc15b7884fce7deb8b0346388837d6cfe", size = 343664 }, + { url = "https://files.pythonhosted.org/packages/40/f2/71b4ed65ce38982ecdda0ff20c3ad1b15e71949c78b2c053df53629ce940/bcrypt-4.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:79e70b8342a33b52b55d93b3a59223a844962bef479f6a0ea318ebbcadf71505", size = 363128 }, + { url = "https://files.pythonhosted.org/packages/11/99/12f6a58eca6dea4be992d6c681b7ec9410a1d9f5cf368c61437e31daa879/bcrypt-4.3.0-cp39-abi3-win32.whl", hash = "sha256:b4d4e57f0a63fd0b358eb765063ff661328f69a04494427265950c71b992a39a", size = 160598 }, + { url = "https://files.pythonhosted.org/packages/a9/cf/45fb5261ece3e6b9817d3d82b2f343a505fd58674a92577923bc500bd1aa/bcrypt-4.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:e53e074b120f2877a35cc6c736b8eb161377caae8925c17688bd46ba56daaa5b", size = 152799 }, + { url = "https://files.pythonhosted.org/packages/55/2d/0c7e5ab0524bf1a443e34cdd3926ec6f5879889b2f3c32b2f5074e99ed53/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c950d682f0952bafcceaf709761da0a32a942272fad381081b51096ffa46cea1", size = 275367 }, + { url = "https://files.pythonhosted.org/packages/10/4f/f77509f08bdff8806ecc4dc472b6e187c946c730565a7470db772d25df70/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:107d53b5c67e0bbc3f03ebf5b030e0403d24dda980f8e244795335ba7b4a027d", size = 280644 }, + { url = "https://files.pythonhosted.org/packages/35/18/7d9dc16a3a4d530d0a9b845160e9e5d8eb4f00483e05d44bb4116a1861da/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b693dbb82b3c27a1604a3dff5bfc5418a7e6a781bb795288141e5f80cf3a3492", size = 274881 }, + { url = "https://files.pythonhosted.org/packages/df/c4/ae6921088adf1e37f2a3a6a688e72e7d9e45fdd3ae5e0bc931870c1ebbda/bcrypt-4.3.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:b6354d3760fcd31994a14c89659dee887f1351a06e5dac3c1142307172a79f90", size = 280203 }, + { url = "https://files.pythonhosted.org/packages/4c/b1/1289e21d710496b88340369137cc4c5f6ee036401190ea116a7b4ae6d32a/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a839320bf27d474e52ef8cb16449bb2ce0ba03ca9f44daba6d93fa1d8828e48a", size = 275103 }, + { url = "https://files.pythonhosted.org/packages/94/41/19be9fe17e4ffc5d10b7b67f10e459fc4eee6ffe9056a88de511920cfd8d/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bdc6a24e754a555d7316fa4774e64c6c3997d27ed2d1964d55920c7c227bc4ce", size = 280513 }, + { url = "https://files.pythonhosted.org/packages/aa/73/05687a9ef89edebdd8ad7474c16d8af685eb4591c3c38300bb6aad4f0076/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:55a935b8e9a1d2def0626c4269db3fcd26728cbff1e84f0341465c31c4ee56d8", size = 274685 }, + { url = "https://files.pythonhosted.org/packages/63/13/47bba97924ebe86a62ef83dc75b7c8a881d53c535f83e2c54c4bd701e05c/bcrypt-4.3.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57967b7a28d855313a963aaea51bf6df89f833db4320da458e5b3c5ab6d4c938", size = 280110 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, + { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 }, + { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 }, + { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 }, + { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 }, + { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 }, + { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 }, + { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 }, + { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 }, + { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 }, + { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 }, + { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 }, + { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 }, +] + [[package]] name = "click" version = "8.1.8" @@ -46,6 +173,51 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "cryptography" +version = "44.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/25/4ce80c78963834b8a9fd1cc1266be5ed8d1840785c0f2e1b73b8d128d505/cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0", size = 710807 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/ef/83e632cfa801b221570c5f58c0369db6fa6cef7d9ff859feab1aae1a8a0f/cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7", size = 6676361 }, + { url = "https://files.pythonhosted.org/packages/30/ec/7ea7c1e4c8fc8329506b46c6c4a52e2f20318425d48e0fe597977c71dbce/cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1", size = 3952350 }, + { url = "https://files.pythonhosted.org/packages/27/61/72e3afdb3c5ac510330feba4fc1faa0fe62e070592d6ad00c40bb69165e5/cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb", size = 4166572 }, + { url = "https://files.pythonhosted.org/packages/26/e4/ba680f0b35ed4a07d87f9e98f3ebccb05091f3bf6b5a478b943253b3bbd5/cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843", size = 3958124 }, + { url = "https://files.pythonhosted.org/packages/9c/e8/44ae3e68c8b6d1cbc59040288056df2ad7f7f03bbcaca6b503c737ab8e73/cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5", size = 3678122 }, + { url = "https://files.pythonhosted.org/packages/27/7b/664ea5e0d1eab511a10e480baf1c5d3e681c7d91718f60e149cec09edf01/cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c", size = 4191831 }, + { url = "https://files.pythonhosted.org/packages/2a/07/79554a9c40eb11345e1861f46f845fa71c9e25bf66d132e123d9feb8e7f9/cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a", size = 3960583 }, + { url = "https://files.pythonhosted.org/packages/bb/6d/858e356a49a4f0b591bd6789d821427de18432212e137290b6d8a817e9bf/cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308", size = 4191753 }, + { url = "https://files.pythonhosted.org/packages/b2/80/62df41ba4916067fa6b125aa8c14d7e9181773f0d5d0bd4dcef580d8b7c6/cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688", size = 4079550 }, + { url = "https://files.pythonhosted.org/packages/f3/cd/2558cc08f7b1bb40683f99ff4327f8dcfc7de3affc669e9065e14824511b/cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", size = 4298367 }, + { url = "https://files.pythonhosted.org/packages/71/59/94ccc74788945bc3bd4cf355d19867e8057ff5fdbcac781b1ff95b700fb1/cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79", size = 2772843 }, + { url = "https://files.pythonhosted.org/packages/ca/2c/0d0bbaf61ba05acb32f0841853cfa33ebb7a9ab3d9ed8bb004bd39f2da6a/cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa", size = 3209057 }, + { url = "https://files.pythonhosted.org/packages/9e/be/7a26142e6d0f7683d8a382dd963745e65db895a79a280a30525ec92be890/cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3", size = 6677789 }, + { url = "https://files.pythonhosted.org/packages/06/88/638865be7198a84a7713950b1db7343391c6066a20e614f8fa286eb178ed/cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639", size = 3951919 }, + { url = "https://files.pythonhosted.org/packages/d7/fc/99fe639bcdf58561dfad1faa8a7369d1dc13f20acd78371bb97a01613585/cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd", size = 4167812 }, + { url = "https://files.pythonhosted.org/packages/53/7b/aafe60210ec93d5d7f552592a28192e51d3c6b6be449e7fd0a91399b5d07/cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181", size = 3958571 }, + { url = "https://files.pythonhosted.org/packages/16/32/051f7ce79ad5a6ef5e26a92b37f172ee2d6e1cce09931646eef8de1e9827/cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea", size = 3679832 }, + { url = "https://files.pythonhosted.org/packages/78/2b/999b2a1e1ba2206f2d3bca267d68f350beb2b048a41ea827e08ce7260098/cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699", size = 4193719 }, + { url = "https://files.pythonhosted.org/packages/72/97/430e56e39a1356e8e8f10f723211a0e256e11895ef1a135f30d7d40f2540/cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9", size = 3960852 }, + { url = "https://files.pythonhosted.org/packages/89/33/c1cf182c152e1d262cac56850939530c05ca6c8d149aa0dcee490b417e99/cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23", size = 4193906 }, + { url = "https://files.pythonhosted.org/packages/e1/99/87cf26d4f125380dc674233971069bc28d19b07f7755b29861570e513650/cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922", size = 4081572 }, + { url = "https://files.pythonhosted.org/packages/b3/9f/6a3e0391957cc0c5f84aef9fbdd763035f2b52e998a53f99345e3ac69312/cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4", size = 4298631 }, + { url = "https://files.pythonhosted.org/packages/e2/a5/5bc097adb4b6d22a24dea53c51f37e480aaec3465285c253098642696423/cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5", size = 2773792 }, + { url = "https://files.pythonhosted.org/packages/33/cf/1f7649b8b9a3543e042d3f348e398a061923ac05b507f3f4d95f11938aa9/cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", size = 3210957 }, + { url = "https://files.pythonhosted.org/packages/99/10/173be140714d2ebaea8b641ff801cbcb3ef23101a2981cbf08057876f89e/cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb", size = 3396886 }, + { url = "https://files.pythonhosted.org/packages/2f/b4/424ea2d0fce08c24ede307cead3409ecbfc2f566725d4701b9754c0a1174/cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41", size = 3892387 }, + { url = "https://files.pythonhosted.org/packages/28/20/8eaa1a4f7c68a1cb15019dbaad59c812d4df4fac6fd5f7b0b9c5177f1edd/cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562", size = 4109922 }, + { url = "https://files.pythonhosted.org/packages/11/25/5ed9a17d532c32b3bc81cc294d21a36c772d053981c22bd678396bc4ae30/cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5", size = 3895715 }, + { url = "https://files.pythonhosted.org/packages/63/31/2aac03b19c6329b62c45ba4e091f9de0b8f687e1b0cd84f101401bece343/cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa", size = 4109876 }, + { url = "https://files.pythonhosted.org/packages/99/ec/6e560908349843718db1a782673f36852952d52a55ab14e46c42c8a7690a/cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d", size = 3131719 }, + { url = "https://files.pythonhosted.org/packages/d6/d7/f30e75a6aa7d0f65031886fa4a1485c2fbfe25a1896953920f6a9cfe2d3b/cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d", size = 3887513 }, + { url = "https://files.pythonhosted.org/packages/9c/b4/7a494ce1032323ca9db9a3661894c66e0d7142ad2079a4249303402d8c71/cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471", size = 4107432 }, + { url = "https://files.pythonhosted.org/packages/45/f8/6b3ec0bc56123b344a8d2b3264a325646d2dcdbdd9848b5e6f3d37db90b3/cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615", size = 3891421 }, + { url = "https://files.pythonhosted.org/packages/57/ff/f3b4b2d007c2a646b0f69440ab06224f9cf37a977a72cdb7b50632174e8a/cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390", size = 4107081 }, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -57,16 +229,16 @@ wheels = [ [[package]] name = "fastapi" -version = "0.115.8" +version = "0.115.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/b2/5a5dc4affdb6661dea100324e19a7721d5dc524b464fe8e366c093fd7d87/fastapi-0.115.8.tar.gz", hash = "sha256:0ce9111231720190473e222cdf0f07f7206ad7e53ea02beb1d2dc36e2f0741e9", size = 295403 } +sdist = { url = "https://files.pythonhosted.org/packages/b5/28/c5d26e5860df807241909a961a37d45e10533acef95fc368066c7dd186cd/fastapi-0.115.11.tar.gz", hash = "sha256:cc81f03f688678b92600a65a5e618b93592c65005db37157147204d8924bf94f", size = 294441 } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/7d/2d6ce181d7a5f51dedb8c06206cbf0ec026a99bf145edd309f9e17c3282f/fastapi-0.115.8-py3-none-any.whl", hash = "sha256:753a96dd7e036b34eeef8babdfcfe3f28ff79648f86551eb36bfc1b0bf4a8cbf", size = 94814 }, + { url = "https://files.pythonhosted.org/packages/b3/5d/4d8bbb94f0dbc22732350c06965e40740f4a92ca560e90bb566f4f73af41/fastapi-0.115.11-py3-none-any.whl", hash = "sha256:32e1541b7b74602e4ef4a0260ecaf3aadf9d4f19590bba3e1bf2ac4666aa2c64", size = 94926 }, ] [[package]] @@ -136,6 +308,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, ] +[[package]] +name = "passlib" +version = "1.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554 }, +] + +[package.optional-dependencies] +bcrypt = [ + { name = "bcrypt" }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -145,6 +331,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + [[package]] name = "pydantic" version = "2.10.6" @@ -265,9 +460,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, ] +[[package]] +name = "pyjwt" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, +] + [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -277,17 +481,30 @@ dependencies = [ { name = "pluggy" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 } +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 }, + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, ] [[package]] name = "pywebflow" source = { editable = "." } dependencies = [ + { name = "cryptography" }, { name = "fastapi" }, { name = "logly" }, + { name = "passlib", extra = ["bcrypt"] }, + { name = "pyjwt" }, + { name = "python-dotenv" }, { name = "uvicorn" }, ] @@ -305,9 +522,13 @@ dev = [ [package.metadata] requires-dist = [ + { name = "cryptography" }, { name = "fastapi", specifier = ">=0.115.6,<0.116.0" }, { name = "logly", git = "https://github.com/muhammad-fiaz/logly.git?rev=9920c22e118facaa5f3145df0a9b4b9e9b9a8839" }, + { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.4" }, + { name = "pyjwt", specifier = ">=2.10.1" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.4" }, + { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.9.5" }, { name = "uvicorn", specifier = ">=0.34.0,<0.35.0" }, ] @@ -334,27 +555,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.9.7" +version = "0.9.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/39/8b/a86c300359861b186f18359adf4437ac8e4c52e42daa9eedc731ef9d5b53/ruff-0.9.7.tar.gz", hash = "sha256:643757633417907510157b206e490c3aa11cab0c087c912f60e07fbafa87a4c6", size = 3669813 } +sdist = { url = "https://files.pythonhosted.org/packages/6f/c3/418441a8170e8d53d05c0b9dad69760dbc7b8a12c10dbe6db1e1205d2377/ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933", size = 3717448 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/f3/3a1d22973291226df4b4e2ff70196b926b6f910c488479adb0eeb42a0d7f/ruff-0.9.7-py3-none-linux_armv6l.whl", hash = "sha256:99d50def47305fe6f233eb8dabfd60047578ca87c9dcb235c9723ab1175180f4", size = 11774588 }, - { url = "https://files.pythonhosted.org/packages/8e/c9/b881f4157b9b884f2994fd08ee92ae3663fb24e34b0372ac3af999aa7fc6/ruff-0.9.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d59105ae9c44152c3d40a9c40d6331a7acd1cdf5ef404fbe31178a77b174ea66", size = 11746848 }, - { url = "https://files.pythonhosted.org/packages/14/89/2f546c133f73886ed50a3d449e6bf4af27d92d2f960a43a93d89353f0945/ruff-0.9.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f313b5800483770bd540cddac7c90fc46f895f427b7820f18fe1822697f1fec9", size = 11177525 }, - { url = "https://files.pythonhosted.org/packages/d7/93/6b98f2c12bf28ab9def59c50c9c49508519c5b5cfecca6de871cf01237f6/ruff-0.9.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042ae32b41343888f59c0a4148f103208bf6b21c90118d51dc93a68366f4e903", size = 11996580 }, - { url = "https://files.pythonhosted.org/packages/8e/3f/b3fcaf4f6d875e679ac2b71a72f6691a8128ea3cb7be07cbb249f477c061/ruff-0.9.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87862589373b33cc484b10831004e5e5ec47dc10d2b41ba770e837d4f429d721", size = 11525674 }, - { url = "https://files.pythonhosted.org/packages/f0/48/33fbf18defb74d624535d5d22adcb09a64c9bbabfa755bc666189a6b2210/ruff-0.9.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a17e1e01bee0926d351a1ee9bc15c445beae888f90069a6192a07a84af544b6b", size = 12739151 }, - { url = "https://files.pythonhosted.org/packages/63/b5/7e161080c5e19fa69495cbab7c00975ef8a90f3679caa6164921d7f52f4a/ruff-0.9.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c1f880ac5b2cbebd58b8ebde57069a374865c73f3bf41f05fe7a179c1c8ef22", size = 13416128 }, - { url = "https://files.pythonhosted.org/packages/4e/c8/b5e7d61fb1c1b26f271ac301ff6d9de5e4d9a9a63f67d732fa8f200f0c88/ruff-0.9.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e63fc20143c291cab2841dbb8260e96bafbe1ba13fd3d60d28be2c71e312da49", size = 12870858 }, - { url = "https://files.pythonhosted.org/packages/da/cb/2a1a8e4e291a54d28259f8fc6a674cd5b8833e93852c7ef5de436d6ed729/ruff-0.9.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91ff963baed3e9a6a4eba2a02f4ca8eaa6eba1cc0521aec0987da8d62f53cbef", size = 14786046 }, - { url = "https://files.pythonhosted.org/packages/ca/6c/c8f8a313be1943f333f376d79724260da5701426c0905762e3ddb389e3f4/ruff-0.9.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88362e3227c82f63eaebf0b2eff5b88990280fb1ecf7105523883ba8c3aaf6fb", size = 12550834 }, - { url = "https://files.pythonhosted.org/packages/9d/ad/f70cf5e8e7c52a25e166bdc84c082163c9c6f82a073f654c321b4dff9660/ruff-0.9.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0372c5a90349f00212270421fe91874b866fd3626eb3b397ede06cd385f6f7e0", size = 11961307 }, - { url = "https://files.pythonhosted.org/packages/52/d5/4f303ea94a5f4f454daf4d02671b1fbfe2a318b5fcd009f957466f936c50/ruff-0.9.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d76b8ab60e99e6424cd9d3d923274a1324aefce04f8ea537136b8398bbae0a62", size = 11612039 }, - { url = "https://files.pythonhosted.org/packages/eb/c8/bd12a23a75603c704ce86723be0648ba3d4ecc2af07eecd2e9fa112f7e19/ruff-0.9.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0c439bdfc8983e1336577f00e09a4e7a78944fe01e4ea7fe616d00c3ec69a3d0", size = 12168177 }, - { url = "https://files.pythonhosted.org/packages/cc/57/d648d4f73400fef047d62d464d1a14591f2e6b3d4a15e93e23a53c20705d/ruff-0.9.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:115d1f15e8fdd445a7b4dc9a30abae22de3f6bcabeb503964904471691ef7606", size = 12610122 }, - { url = "https://files.pythonhosted.org/packages/49/79/acbc1edd03ac0e2a04ae2593555dbc9990b34090a9729a0c4c0cf20fb595/ruff-0.9.7-py3-none-win32.whl", hash = "sha256:e9ece95b7de5923cbf38893f066ed2872be2f2f477ba94f826c8defdd6ec6b7d", size = 9988751 }, - { url = "https://files.pythonhosted.org/packages/6d/95/67153a838c6b6ba7a2401241fd8a00cd8c627a8e4a0491b8d853dedeffe0/ruff-0.9.7-py3-none-win_amd64.whl", hash = "sha256:3770fe52b9d691a15f0b87ada29c45324b2ace8f01200fb0c14845e499eb0c2c", size = 11002987 }, - { url = "https://files.pythonhosted.org/packages/63/6a/aca01554949f3a401991dc32fe22837baeaccb8a0d868256cbb26a029778/ruff-0.9.7-py3-none-win_arm64.whl", hash = "sha256:b075a700b2533feb7a01130ff656a4ec0d5f340bb540ad98759b8401c32c2037", size = 10177763 }, + { url = "https://files.pythonhosted.org/packages/bc/c3/2c4afa9ba467555d074b146d9aed0633a56ccdb900839fb008295d037b89/ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367", size = 10027252 }, + { url = "https://files.pythonhosted.org/packages/33/d1/439e58487cf9eac26378332e25e7d5ade4b800ce1eec7dc2cfc9b0d7ca96/ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7", size = 10840721 }, + { url = "https://files.pythonhosted.org/packages/50/44/fead822c38281ba0122f1b76b460488a175a9bd48b130650a6fb6dbcbcf9/ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d", size = 10161439 }, + { url = "https://files.pythonhosted.org/packages/11/ae/d404a2ab8e61ddf6342e09cc6b7f7846cce6b243e45c2007dbe0ca928a5d/ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a", size = 10336264 }, + { url = "https://files.pythonhosted.org/packages/6a/4e/7c268aa7d84cd709fb6f046b8972313142cffb40dfff1d2515c5e6288d54/ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe", size = 9908774 }, + { url = "https://files.pythonhosted.org/packages/cc/26/c618a878367ef1b76270fd027ca93692657d3f6122b84ba48911ef5f2edc/ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c", size = 11428127 }, + { url = "https://files.pythonhosted.org/packages/d7/9a/c5588a93d9bfed29f565baf193fe802fa676a0c837938137ea6cf0576d8c/ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be", size = 12133187 }, + { url = "https://files.pythonhosted.org/packages/3e/ff/e7980a7704a60905ed7e156a8d73f604c846d9bd87deda9cabfa6cba073a/ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590", size = 11602937 }, + { url = "https://files.pythonhosted.org/packages/24/78/3690444ad9e3cab5c11abe56554c35f005b51d1d118b429765249095269f/ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb", size = 13771698 }, + { url = "https://files.pythonhosted.org/packages/6e/bf/e477c2faf86abe3988e0b5fd22a7f3520e820b2ee335131aca2e16120038/ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0", size = 11249026 }, + { url = "https://files.pythonhosted.org/packages/f7/82/cdaffd59e5a8cb5b14c408c73d7a555a577cf6645faaf83e52fe99521715/ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17", size = 10220432 }, + { url = "https://files.pythonhosted.org/packages/fe/a4/2507d0026225efa5d4412b6e294dfe54725a78652a5c7e29e6bd0fc492f3/ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1", size = 9874602 }, + { url = "https://files.pythonhosted.org/packages/d5/be/f3aab1813846b476c4bcffe052d232244979c3cd99d751c17afb530ca8e4/ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57", size = 10851212 }, + { url = "https://files.pythonhosted.org/packages/8b/45/8e5fd559bea0d2f57c4e12bf197a2fade2fac465aa518284f157dfbca92b/ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e", size = 11327490 }, + { url = "https://files.pythonhosted.org/packages/42/55/e6c90f13880aeef327746052907e7e930681f26a164fe130ddac28b08269/ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1", size = 10227912 }, + { url = "https://files.pythonhosted.org/packages/35/b2/da925693cb82a1208aa34966c0f36cb222baca94e729dd22a587bc22d0f3/ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1", size = 11355632 }, + { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 }, ] [[package]] @@ -368,15 +589,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.45.3" +version = "0.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } +sdist = { url = "https://files.pythonhosted.org/packages/44/b6/fb9a32e3c5d59b1e383c357534c63c2d3caa6f25bf3c59dd89d296ecbaec/starlette-0.46.0.tar.gz", hash = "sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50", size = 2575568 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, + { url = "https://files.pythonhosted.org/packages/41/94/8af675a62e3c91c2dee47cf92e602cfac86e8767b1a1ac3caf1b327c2ab0/starlette-0.46.0-py3-none-any.whl", hash = "sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038", size = 71991 }, ] [[package]] diff --git a/webflow/__init__.py b/webflow/__init__.py index d30e81d..d3cf7e6 100644 --- a/webflow/__init__.py +++ b/webflow/__init__.py @@ -10,6 +10,7 @@ sidebar, config, add_html_content, +block ) from webflow.__version__ import version as __version__ @@ -26,4 +27,5 @@ "config", "add_html_content", "__version__", + "block", ] diff --git a/webflow/modules/auth.py b/webflow/modules/auth.py new file mode 100644 index 0000000..7afdd9b --- /dev/null +++ b/webflow/modules/auth.py @@ -0,0 +1,26 @@ +from cryptography.fernet import Fernet +from typing import Dict + +# Fixed key for encryption and decryption +SECRET_KEY = Fernet.generate_key() +cipher = Fernet(SECRET_KEY) + +def encrypt_credentials(credentials: Dict[str, str]) -> Dict[str, str]: + """ + Encrypts the authentication credentials. + """ + encrypted_credentials = {} + for key, value in credentials.items(): + encrypted_value = cipher.encrypt(str(value).encode()).decode() + encrypted_credentials[key] = encrypted_value + return encrypted_credentials + +def decrypt_credentials(encrypted_credentials: Dict[str, str]) -> Dict[str, str]: + """ + Decrypts the authentication credentials. + """ + decrypted_credentials = {} + for key, value in encrypted_credentials.items(): + decrypted_value = cipher.decrypt(value.encode()).decode() + decrypted_credentials[key] = decrypted_value + return decrypted_credentials \ No newline at end of file diff --git a/webflow/modules/routes.py b/webflow/modules/routes.py index 78c4c1a..3d3b550 100644 --- a/webflow/modules/routes.py +++ b/webflow/modules/routes.py @@ -1,7 +1,8 @@ import datetime -from fastapi import APIRouter + +from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import JSONResponse -from typing import List +from typing import List, Dict from webflow.modules.types import NodeData, EdgeData, Metadata, SidebarResponse, HtmlContent, ReactFlowConfig from webflow.modules.webflow_api import WebFlow_API @@ -34,7 +35,6 @@ async def get_sidebar(): items=items ) - @router.get("/api/metadata", response_model=Metadata) async def get_metadata(): return WebFlow_API.metadata @@ -65,5 +65,9 @@ async def get_file_paths(): async def get_static_file(filename: str): return WebFlow_API.serve_file(filename) -WebFlow_API.app.include_router(router) +@router.post("/api/auth") +async def set_authentication(): + return {"status": "success", "message": "Authentication credentials set successfully"} + +WebFlow_API.app.include_router(router) \ No newline at end of file diff --git a/webflow/modules/webflow_api.py b/webflow/modules/webflow_api.py index 12e4547..ded6e43 100644 --- a/webflow/modules/webflow_api.py +++ b/webflow/modules/webflow_api.py @@ -14,6 +14,7 @@ SideBar, ReactFlowConfig, ) +from webflow.modules.auth import encrypt_credentials, decrypt_credentials # Import encryption functions def ensure_initialized(method): @@ -40,6 +41,7 @@ class WebFlow_API: static_dir: Optional[str] = None initialized = False html_store: List[str] = [] + authentication: Optional[Dict[str, str]] = None @classmethod def initialize(cls): @@ -177,7 +179,21 @@ def get_html(cls): return cls.html_store @classmethod - def launch(cls, host: str = "127.0.0.1", port: int = 8000, reload: bool = True): + def launch(cls, host: str = "127.0.0.1", port: int = 8000, reload: bool = True, authentication: Dict[str, str] = None): + if authentication: + cls.set_authentication(authentication) cls.initialize() import uvicorn uvicorn.run(cls.app, host=host, port=port, reload=reload) + + @classmethod + def set_authentication(cls, authentication: Dict[str, str]): + cls.authentication = authentication + + @classmethod + def route(cls, page): + pass + + @classmethod + def block(cls, route): + pass \ No newline at end of file diff --git a/webflow/webflow.py b/webflow/webflow.py index 694c0da..eb862d3 100644 --- a/webflow/webflow.py +++ b/webflow/webflow.py @@ -1,5 +1,6 @@ import uvicorn from typing import Dict, List +from contextlib import contextmanager from webflow.ascii import ascii_art from webflow.logly import logly @@ -50,11 +51,21 @@ def config(**kwargs): WebFlow_API.set_config(**kwargs) -def launch(attributes=True): +@contextmanager +def block(route: str): + try: + block_instance = WebFlow_API.route(route) + yield block_instance + finally: + pass # Cleanup if needed + + +def launch(attributes=True, authentication: Dict[str, str] = None): args = parse_arguments() WebFlow_API.initialize() if attributes: print(ascii_art) + WebFlow_API.set_authentication(authentication) # Log launch details using Logly. logly.Config(color_enabled=True) From e7b1bf7f96d98333fe28a7c47f35ce33f6c4937b Mon Sep 17 00:00:00 2001 From: Muhammad Fiaz Date: Fri, 7 Mar 2025 21:19:14 +0530 Subject: [PATCH 6/6] =?UTF-8?q?=E2=9C=A8=20Implement=20authentication=20fu?= =?UTF-8?q?nctionality=20and=20enhance=20routing=20with=20dynamic=20loadin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/NotFound.tsx | 11 ++++++--- packages/frontend/src/Root.tsx | 2 +- packages/frontend/src/components/loader.tsx | 5 ++--- packages/frontend/src/routes.tsx | 25 +++++++++++---------- packages/frontend/src/utils/types.ts | 1 - 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/frontend/src/NotFound.tsx b/packages/frontend/src/NotFound.tsx index 8cde3f9..077e382 100644 --- a/packages/frontend/src/NotFound.tsx +++ b/packages/frontend/src/NotFound.tsx @@ -1,11 +1,16 @@ -import { Link } from "react-router-dom"; +import { Link } from 'react-router-dom'; export default function NotFound() { return (

404

-

Oops! The page you're looking for doesn't exist.

- +

+ Oops! The page you're looking for doesn't exist. +

+ Go Home
diff --git a/packages/frontend/src/Root.tsx b/packages/frontend/src/Root.tsx index c68299d..e0edab2 100644 --- a/packages/frontend/src/Root.tsx +++ b/packages/frontend/src/Root.tsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom/client'; import { ThemeProvider } from 'next-themes'; const root = ReactDOM.createRoot( - document.getElementById("root") as HTMLElement, + document.getElementById('root') as HTMLElement, ); root.render(); diff --git a/packages/frontend/src/components/loader.tsx b/packages/frontend/src/components/loader.tsx index 1efc862..2360136 100644 --- a/packages/frontend/src/components/loader.tsx +++ b/packages/frontend/src/components/loader.tsx @@ -1,13 +1,12 @@ import { cn } from '../lib/utils.ts'; import LoadingComponent from './loadingcomponent.tsx'; - export function LoadingPage({ overlay = false }: { overlay?: boolean }) { return (
diff --git a/packages/frontend/src/routes.tsx b/packages/frontend/src/routes.tsx index b95bee0..aad118c 100644 --- a/packages/frontend/src/routes.tsx +++ b/packages/frontend/src/routes.tsx @@ -1,16 +1,16 @@ -import { createBrowserRouter, RouteObject } from "react-router-dom"; -import { lazy, Suspense, useEffect, useState } from "react"; -import { LoadingPage } from "./components/loader.tsx"; -import axios from "axios"; +import { createBrowserRouter, RouteObject } from 'react-router-dom'; +import { lazy, Suspense, useEffect, useState } from 'react'; +import { LoadingPage } from './components/loader.tsx'; +import axios from 'axios'; -const NotFound = lazy(() => import("./NotFound.tsx")); +const NotFound = lazy(() => import('./NotFound.tsx')); const fetchRoutes = async () => { try { - const response = await axios.get("/api/routes"); // Fetch route config from API + const response = await axios.get('/api/routes'); // Fetch route config from API return response.data; } catch (error) { - console.error("Failed to fetch routes", error); + console.error('Failed to fetch routes', error); return []; } }; @@ -22,16 +22,17 @@ const DynamicRoutes = () => { fetchRoutes().then((data) => { const mappedRoutes = data.map((route: any) => ({ path: route.path, - element: }>, + element: ( + }> + + + ), })); setRoutes(mappedRoutes); }); }, []); - return createBrowserRouter([ - ...routes, - { path: "*", element: }, - ]); + return createBrowserRouter([...routes, { path: '*', element: }]); }; const router = DynamicRoutes(); diff --git a/packages/frontend/src/utils/types.ts b/packages/frontend/src/utils/types.ts index d58f174..82d63d9 100644 --- a/packages/frontend/src/utils/types.ts +++ b/packages/frontend/src/utils/types.ts @@ -1,6 +1,5 @@ import React from 'react'; - export type LoadingComponentProps = { remSize: number; };