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 diff --git a/.husky/pre-commit b/.husky/pre-commit index 4dfead0..c0ff859 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 diff --git a/bun.lockb b/bun.lockb index 2598051..ef427fd 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index a84884a..7ddbffe 100644 --- a/package.json +++ b/package.json @@ -5,17 +5,18 @@ "packages/*" ], "scripts": { + "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", "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": { - "prettier": "3.5.1" + "husky": "^9.1.7", + "prettier": "3.5.3" }, "packageManager": "bun@latest" } diff --git a/packages/frontend/.env-example b/packages/frontend/.env-example new file mode 100644 index 0000000..4ff8c6f --- /dev/null +++ b/packages/frontend/.env-example @@ -0,0 +1,2 @@ +VITE_API_BASE_URL=http://127.0.0.1:8000 +NODE_ENV=development \ No newline at end of file diff --git a/packages/frontend/.gitignore b/packages/frontend/.gitignore index c6c8459..b79aa62 100644 --- a/packages/frontend/.gitignore +++ b/packages/frontend/.gitignore @@ -22,4 +22,10 @@ dist-ssr *.njsproj *.sln *.sw? -/stats.html \ No newline at end of file +/stats.html + + +.env +.env.local + +.vscode \ No newline at end of file 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 069ad54..2edcf12 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -16,19 +16,20 @@ "prepare": "husky" }, "dependencies": { - "@mantine/carousel": "^7.17.0", - "@mantine/charts": "^7.17.0", - "@mantine/code-highlight": "^7.17.0", - "@mantine/core": "^7.17.0", - "@mantine/dates": "^7.17.0", - "@mantine/dropzone": "^7.17.0", - "@mantine/form": "^7.17.0", - "@mantine/hooks": "^7.17.0", - "@mantine/modals": "^7.17.0", - "@mantine/notifications": "^7.17.0", - "@mantine/nprogress": "^7.17.0", - "@mantine/spotlight": "^7.17.0", - "@mantine/tiptap": "^7.17.0", + "@headlessui/react": "^2.2.0", + "@mantine/carousel": "^7.17.1", + "@mantine/charts": "^7.17.1", + "@mantine/code-highlight": "^7.17.1", + "@mantine/core": "^7.17.1", + "@mantine/dates": "^7.17.1", + "@mantine/dropzone": "^7.17.1", + "@mantine/form": "^7.17.1", + "@mantine/hooks": "^7.17.1", + "@mantine/modals": "^7.17.1", + "@mantine/notifications": "^7.17.1", + "@mantine/nprogress": "^7.17.1", + "@mantine/spotlight": "^7.17.1", + "@mantine/tiptap": "^7.17.1", "@pywebflow/api": "workspace:*", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-navigation-menu": "^1.2.5", @@ -37,22 +38,24 @@ "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", - "@radix-ui/themes": "^3.2.0", - "@tabler/icons-react": "^3.30.0", - "@tailwindcss/postcss": "^4.0.7", - "@tailwindcss/vite": "^4.0.7", + "@radix-ui/themes": "^3.2.1", + "@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", "@tiptap/starter-kit": "^2.11.5", - "@xyflow/react": "^12.4.3", + "@xyflow/react": "^12.4.4", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dayjs": "^1.11.13", + "dompurify": "^3.2.4", "embla-carousel-react": "^8.5.2", - "framer-motion": "^12.4.7", - "lucide-react": "^0.475.0", + "framer-motion": "^12.4.10", + "html-react-parser": "^5.2.2", + "lucide-react": "^0.477.0", "next-themes": "^0.4.4", "postcss": "^8.5.3", "react": "^19.0.0", @@ -60,32 +63,33 @@ "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.0", - "tailwind-merge": "^3.0.1", - "tailwindcss": "^4.0.7", - "tailwindcss-animate": "^1.0.7" + "sass": "^1.85.1", + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.0.12", + "tailwindcss-animate": "^1.0.7", + "zustand": "^5.0.3" }, "license": "apache-2.0", "devDependencies": { - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.3", - "@types/node": "^22.13.4", + "@types/node": "^22.13.9", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", - "@typescript-eslint/eslint-plugin": "^8.24.1", - "@typescript-eslint/parser": "^8.24.1", + "@typescript-eslint/eslint-plugin": "^8.26.0", + "@typescript-eslint/parser": "^8.26.0", "@vitejs/plugin-react": "^4.3.4", - "eslint": "^9.20.1", - "eslint-plugin-react-hooks": "^5.1.0", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.21.0", + "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", - "husky": "^9.1.7", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", "rollup-plugin-visualizer": "^5.14.0", - "typescript": "^5.7.3", - "vite": "^6.1.1" + "typescript": "^5.8.2", + "vite": "^6.2.1" }, "keywords": [ "pywebflow", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 8e19faf..73e0627 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,55 +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); - - useEffect(() => { - const fetchSidebarItems = async () => { - const response: SidebarResponse = await getSidebarItems(); - setSidebarItems(response.items); - setSidebarVisible(response.visible); - setSidebarLabel(response.label); - setDefaultOpen(response.default_open); - }; - - 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 ( -
- -
- {sidebarVisible && ( - - )} - {sidebarVisible && } -
- - - - - {/* Add the InjectedHtml component here */} -
-
-
-
+ }> + + ); } - -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/Layout.tsx b/packages/frontend/src/Layout.tsx index 7d09b47..69d51a5 100644 --- a/packages/frontend/src/Layout.tsx +++ b/packages/frontend/src/Layout.tsx @@ -34,12 +34,18 @@ export default function Layout() { const loadInitialConfig = async () => { try { const fetchedConfig = await getConfig(); - if (fetchedConfig[0].colorMode) { - setTheme(fetchedConfig[0].colorMode); + + if (Array.isArray(fetchedConfig) && fetchedConfig.length > 0) { + if (fetchedConfig[0]?.colorMode) { + setTheme(fetchedConfig[0].colorMode); + } + setConfig(fetchedConfig[0]); + } else { + setConfig({}); } - setConfig(fetchedConfig[0]); } catch (error) { console.error('Error fetching initial config:', error); + setConfig({}); // Fallback to empty object on error } }; diff --git a/packages/frontend/src/NotFound.tsx b/packages/frontend/src/NotFound.tsx new file mode 100644 index 0000000..077e382 --- /dev/null +++ b/packages/frontend/src/NotFound.tsx @@ -0,0 +1,18 @@ +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 bcb5c19..e0edab2 100644 --- a/packages/frontend/src/Root.tsx +++ b/packages/frontend/src/Root.tsx @@ -1,92 +1,9 @@ -import React, { Suspense, useEffect, useState } from 'react'; +import './styles.ts'; 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 './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 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(() => { - // 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); - } - } 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/InjectedHtml.tsx b/packages/frontend/src/components/InjectedHtml.tsx index 3f4bb4c..e05f7b9 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,13 +16,9 @@ const InjectedHtml: React.FC = () => { }, []); return ( -
+
{htmlContents.map((htmlContent, index) => ( -
+
{parse(DOMPurify.sanitize(htmlContent))}
))}
); diff --git a/packages/frontend/src/components/Metadata.tsx b/packages/frontend/src/components/Metadata.tsx index 2fee2f1..a0b5c6b 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,7 +24,7 @@ const MetaData: React.FC = () => { } }; - fetchMetadata(); + loadMetadataAndAssets(); }, []); return ( diff --git a/packages/frontend/src/components/loader.tsx b/packages/frontend/src/components/loader.tsx new file mode 100644 index 0000000..2360136 --- /dev/null +++ b/packages/frontend/src/components/loader.tsx @@ -0,0 +1,15 @@ +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/hooks/assets.ts b/packages/frontend/src/hooks/assets.ts index c16da2f..aeb0179 100644 --- a/packages/frontend/src/hooks/assets.ts +++ b/packages/frontend/src/hooks/assets.ts @@ -1,55 +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(); + + if (!assets || typeof assets !== 'object') { + console.error('Invalid assets data:', assets); + return; + } - const cssFiles = assets.css ?? []; // Default to an empty array if undefined - const jsFiles = assets.js ?? []; // Default to an empty array if undefined + 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/routes.tsx b/packages/frontend/src/routes.tsx new file mode 100644 index 0000000..aad118c --- /dev/null +++ b/packages/frontend/src/routes.tsx @@ -0,0 +1,39 @@ +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/styles.ts b/packages/frontend/src/styles.ts new file mode 100644 index 0000000..b8f6ea6 --- /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'; diff --git a/packages/frontend/src/utils/types.ts b/packages/frontend/src/utils/types.ts index aa58b0a..82d63d9 100644 --- a/packages/frontend/src/utils/types.ts +++ b/packages/frontend/src/utils/types.ts @@ -1,5 +1,9 @@ import React from 'react'; +export type LoadingComponentProps = { + remSize: number; +}; + // NodeData Type export interface NodeData { id: string; diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index fb85473..d0e932a 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,9 +1,15 @@ 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 +21,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/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 e419d8e..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,19 +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" -version = "0.0.1rc0" source = { editable = "." } dependencies = [ + { name = "cryptography" }, { name = "fastapi" }, { name = "logly" }, - { name = "rich" }, + { name = "passlib", extra = ["bcrypt"] }, + { name = "pyjwt" }, + { name = "python-dotenv" }, { name = "uvicorn" }, ] @@ -307,10 +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 = "rich", specifier = ">=13.9.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" }, ] @@ -337,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]] @@ -371,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/__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/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 c2a24cf..3d3b550 100644 --- a/webflow/modules/routes.py +++ b/webflow/modules/routes.py @@ -1,300 +1,73 @@ 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 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() +from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import JSONResponse +from typing import List, Dict + +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) + +@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/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..ded6e43 --- /dev/null +++ b/webflow/modules/webflow_api.py @@ -0,0 +1,199 @@ +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, +) +from webflow.modules.auth import encrypt_credentials, decrypt_credentials # Import encryption functions + + +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] = [] + authentication: Optional[Dict[str, str]] = None + + @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, 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 579f5e0..eb862d3 100644 --- a/webflow/webflow.py +++ b/webflow/webflow.py @@ -1,14 +1,14 @@ import uvicorn from typing import Dict, List +from contextlib import contextmanager 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): @@ -51,10 +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) @@ -100,4 +111,4 @@ def launch(attributes=True): port=args.port, reload=args.reload, log_config=custom_log_config, - ) + ) \ No newline at end of file