diff --git a/Dockerfile b/Dockerfile index 4fa908c..a589938 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # Needed for start.py to detect the Dockerfile -FROM --platform=$BUILDPLATFORM oven/bun AS frontend +FROM --platform=$BUILDPLATFORM oven/bun AS frontend WORKDIR /app ADD ./frontend/package.json . ADD ./frontend/bun.lockb . diff --git a/backend/app.py b/backend/app.py index e2c927b..6ba5685 100644 --- a/backend/app.py +++ b/backend/app.py @@ -10,6 +10,7 @@ from utils import API_VERSION, FIREGEX_PORT, JWT_ALGORITHM, get_interfaces, sock from utils.loader import frontend_deploy, load_routers from utils.models import ChangePasswordModel, IpInterface, PasswordChangeForm, PasswordForm, ResetRequest, StatusModel, StatusMessageModel from contextlib import asynccontextmanager +from fastapi.middleware.cors import CORSMiddleware # DB init db = SQLite('db/firegex.db') @@ -32,6 +33,9 @@ async def lifespan(app): app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan) utils.socketio = SocketManager(app, "/sock", socketio_path="") +if DEBUG: + app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) + def APP_STATUS(): return "init" if db.get("password") is None else "run" def JWT_SECRET(): return db.get("secret") @@ -158,7 +162,7 @@ if __name__ == '__main__': os.chdir(os.path.dirname(os.path.realpath(__file__))) uvicorn.run( "app:app", - host="0.0.0.0" if DEBUG else None, + host="::" if DEBUG else None, port=FIREGEX_PORT, reload=DEBUG, access_log=True, diff --git a/docs/Firegex_Screenshot.png b/docs/Firegex_Screenshot.png index 398ff42..7960b95 100644 Binary files a/docs/Firegex_Screenshot.png and b/docs/Firegex_Screenshot.png differ diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 2ea2843..0bc4166 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/package.json b/frontend/package.json index b127304..bbdd0a4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -2,39 +2,33 @@ "name": "firegex-frontend", "version": "0.1.0", "private": true, + "type": "module", "dependencies": { - "@emotion/react": "^11.11.0", - "@hello-pangea/dnd": "^16.3.0", - "@mantine/core": "^6.0.21", - "@mantine/form": "^6.0.21", - "@mantine/hooks": "^6.0.21", - "@mantine/modals": "^6.0.21", - "@mantine/notifications": "^6.0.21", - "@mantine/prism": "^6.0.21", - "@mantine/spotlight": "^6.0.21", - "@tanstack/react-query": "^4.35.3", - "@testing-library/dom": "^9.3.0", - "@testing-library/jest-dom": "^5.16.4", - "@testing-library/react": "^13.3.0", - "@testing-library/user-event": "^13.5.0", + "@hello-pangea/dnd": "^16.6.0", + "@mantine/core": "^7.13.2", + "@mantine/form": "^7.13.2", + "@mantine/hooks": "^7.13.2", + "@mantine/modals": "^7.13.2", + "@mantine/notifications": "^7.13.2", + "@tanstack/react-query": "^4.36.1", "@types/jest": "^27.5.2", - "@types/node": "^20.2.5", - "@types/react": "^18.0.12", - "@types/react-dom": "^18.0.5", + "@types/node": "^20.16.11", + "@types/react": "^18.3.11", + "@types/react-dom": "^18.3.1", "buffer": "^6.0.3", - "react": "^18.1.0", - "react-dom": "^18.1.0", - "react-icons": "^4.4.0", - "react-router-dom": "^6.3.0", - "sass": "^1.62.1", - "socket.io-client": "^4.5.1", - "typescript": "^4.7.3", - "web-vitals": "^2.1.4" + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-icons": "^5.3.0", + "react-router-dom": "^6.27.0", + "socket.io-client": "^4.8.0", + "typescript": "^4.9.5", + "web-vitals": "^2.1.4", + "zustand": "^5.0.0-rc.2" }, "scripts": { - "start": "vite", + "dev": "vite", "build": "tsc && vite build", - "serve": "vite preview" + "preview": "vite preview" }, "eslintConfig": { "extends": [ @@ -55,10 +49,10 @@ ] }, "devDependencies": { - "@tanstack/react-query-devtools": "^4.35.3", - "@vitejs/plugin-react": "^4.0.0", - "vite": "^4.3.9", - "vite-plugin-svgr": "^3.2.0", - "vite-tsconfig-paths": "^4.2.0" + "@tanstack/react-query-devtools": "^4.36.1", + "@vitejs/plugin-react": "^4.3.2", + "vite": "^4.5.5", + "vite-plugin-svgr": "^3.3.0", + "vite-tsconfig-paths": "^4.3.2" } } diff --git a/frontend/public/header-logo.png b/frontend/public/header-logo.png index 2f0bc63..67770de 100644 Binary files a/frontend/public/header-logo.png and b/frontend/public/header-logo.png differ diff --git a/frontend/src/.dockerignore b/frontend/src/.dockerignore new file mode 100644 index 0000000..767c392 --- /dev/null +++ b/frontend/src/.dockerignore @@ -0,0 +1,31 @@ +Dockerfile + +**/*.pyc +**/__pycache__/ +**/.vscode/** +**/.vscode/ +**/.mypy_cache/** +**/.mypy_cache/ + +**/node_modules +**/.pnp +**/.pnp.js + +# testing +/coverage + +/build/ +/build/** +/node_modules/ + +# misc +**/.DS_Store +**/.env.local +**/.env.development.local +**/.env.test.local +**/.env.production.local + +**/npm-debug.log* +**/yarn-debug.log* +**/yarn-error.log* + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index efce8fe..8a027a7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { Button, Group, Loader, LoadingOverlay, Notification, Space, PasswordInput, Title } from '@mantine/core'; +import { Button, Group, Loader, LoadingOverlay, Notification, Space, PasswordInput, Title, Box } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useEffect, useState } from 'react'; import { ImCross } from 'react-icons/im'; @@ -66,15 +66,15 @@ function App() { if (loading){ return }else if (reqError){ - return
- Error launching Firegex! 🔥 + return + Error launching Firegex! 🔥 - Error communicating with backend + Error communicating with backend Error: {reqError} -
+ }else if (systemStatus.status === "init"){ const submitRequest = async (values:PasswordSend) => { @@ -90,8 +90,8 @@ function App() { } - return
- Setup: Choose the password for access to the firewall 🔒 + return + Setup: Choose the password for access to the firewall 🔒
- + @@ -108,7 +108,7 @@ function App() { } color="red" onClose={()=>{setError(null)}}> Error: {error} :null} -
+ }else if (systemStatus.status === "run" && !systemStatus.loggined){ const submitRequest = async (values:PasswordSend) => { setLoadingBtn(true) @@ -123,10 +123,10 @@ function App() { } - return
- Welcome to Firegex 🔥 + return + Welcome to Firegex 🔥 - Before you use the firewall, insert the password 🔒 + Before you use the firewall, insert the password 🔒
- + @@ -143,7 +143,7 @@ function App() { } color="red" onClose={()=>{setError(null)}}> Error: {error} :null} -
+ }else if (systemStatus.status === "run" && systemStatus.loggined){ return }> @@ -159,11 +159,11 @@ function App() { }else{ - return
- Error launching Firegex! 🔥 + return + Error launching Firegex! 🔥 - Error communicating with backend -
+ Error communicating with backend + } } diff --git a/frontend/src/_vars.scss b/frontend/src/_vars.scss deleted file mode 100644 index 6df635d..0000000 --- a/frontend/src/_vars.scss +++ /dev/null @@ -1,4 +0,0 @@ - -$primary_color: #242a33; -$secondary_color: #1A1B1E; -$third_color:#25262b; diff --git a/frontend/src/components/AddNewRegex.tsx b/frontend/src/components/AddNewRegex.tsx index fddbb57..623120a 100644 --- a/frontend/src/components/AddNewRegex.tsx +++ b/frontend/src/components/AddNewRegex.tsx @@ -108,7 +108,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> }> Using whitelist means that EVERY packet that doesn't match the regex will be DROPPED... In most cases this cause the service interruption. :null} - + diff --git a/frontend/src/components/FilterTypeSelector.tsx b/frontend/src/components/FilterTypeSelector.tsx index ab7999a..aa331be 100644 --- a/frontend/src/components/FilterTypeSelector.tsx +++ b/frontend/src/components/FilterTypeSelector.tsx @@ -1,5 +1,4 @@ import { Box, Center, SegmentedControl } from "@mantine/core"; -import React from "react"; import { FaListAlt } from "react-icons/fa"; import { TiCancel } from "react-icons/ti"; diff --git a/frontend/src/components/Footer/index.module.scss b/frontend/src/components/Footer/index.module.scss deleted file mode 100644 index be3716c..0000000 --- a/frontend/src/components/Footer/index.module.scss +++ /dev/null @@ -1,8 +0,0 @@ -@use "../../vars" as *; -@use "../../index.scss" as *; - -.footer{ - margin-top: 50px; - background-color: $primary_color; - @extend .center-flex; -} \ No newline at end of file diff --git a/frontend/src/components/Footer/index.tsx b/frontend/src/components/Footer/index.tsx deleted file mode 100644 index 66c157a..0000000 --- a/frontend/src/components/Footer/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Footer, Space } from '@mantine/core'; -import image from "./pwnzer0tt1.svg" - -import style from "./index.module.scss"; -import { Link } from 'react-router-dom'; - -function FooterPage() { - return
- Made by
Pwnzer0tt1 -
-} - -export default FooterPage; diff --git a/frontend/src/components/Footer/pwnzer0tt1.svg b/frontend/src/components/Footer/pwnzer0tt1.svg deleted file mode 100644 index 5a3e87b..0000000 --- a/frontend/src/components/Footer/pwnzer0tt1.svg +++ /dev/null @@ -1,595 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/components/Header/ResetModal.tsx b/frontend/src/components/Header/ResetModal.tsx index b0e2fe4..0b9d46f 100644 --- a/frontend/src/components/Header/ResetModal.tsx +++ b/frontend/src/components/Header/ResetModal.tsx @@ -47,7 +47,7 @@ function ResetModal({ opened, onClose }:{ opened: boolean, onClose: () => void } {...form.getInputProps('delete_data', { type: 'checkbox' })} /> - + diff --git a/frontend/src/components/Header/ResetPasswordModal.tsx b/frontend/src/components/Header/ResetPasswordModal.tsx index ad880d8..c607087 100644 --- a/frontend/src/components/Header/ResetPasswordModal.tsx +++ b/frontend/src/components/Header/ResetPasswordModal.tsx @@ -46,7 +46,7 @@ function ResetPasswordModal({ opened, onClose }:{ opened: boolean, onClose: () = {...form.getInputProps('expire', { type: 'checkbox' })} /> - + diff --git a/frontend/src/components/Header/index.module.scss b/frontend/src/components/Header/index.module.scss deleted file mode 100644 index 369c4ef..0000000 --- a/frontend/src/components/Header/index.module.scss +++ /dev/null @@ -1,24 +0,0 @@ - -@use "../../vars" as *; -@use "../../index.scss" as *; - -.header{ - width: 100%; - background-color: $primary_color; - display: flex; - align-items: center; - justify-content: center; -} - -.divlogo{ - width: 110px; - height: 100%; - cursor: pointer; - @extend .center-flex; -} - -.navbtn{ - @extend .center-flex; - width: 30px; - margin:0; -} \ No newline at end of file diff --git a/frontend/src/components/Header/index.tsx b/frontend/src/components/Header/index.tsx index 7e28a08..938c8fb 100644 --- a/frontend/src/components/Header/index.tsx +++ b/frontend/src/components/Header/index.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; -import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, Header, Button, ThemeIcon } from '@mantine/core'; -import style from "./index.module.scss"; -import { errorNotify, getmainpath, isLargeScreen, logout } from '../../js/utils'; +import { ActionIcon, Divider, Image, Menu, Tooltip, Burger, Space, AppShell, Box, Title } from '@mantine/core'; +import { errorNotify, getMainPath, isLargeScreen, logout } from '../../js/utils'; import { AiFillHome } from "react-icons/ai" import { useNavigate } from 'react-router-dom'; import { FaLock } from 'react-icons/fa'; @@ -10,11 +9,13 @@ import { ImExit } from 'react-icons/im'; import ResetPasswordModal from './ResetPasswordModal'; import ResetModal from './ResetModal'; import { MenuDropDownWithButton } from '../MainLayout'; +import { useNavbarStore } from '../../js/store'; -function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:React.Dispatch>}) { +function HeaderPage(props: any) { const navigator = useNavigate() + const { navOpened, toggleNav } = useNavbarStore() const logout_action = () => { logout().then(r => { @@ -25,40 +26,42 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac } const go_to_home = () => { - navigator(`/${getmainpath()}`) + navigator(`/${getMainPath()}`) } const [changePasswordModal, setChangePasswordModal] = useState(false); const [resetFiregexModal, setResetFiregexModal] = useState(false); const [tooltipHomeOpened, setTooltipHomeOpened] = useState(false); const [tooltipLogoutOpened,setTooltipLogoutOpened] = useState(false); - const isLarge = isLargeScreen() - console.log(isLarge) - return
- - {isLarge?null:
- setNav((o) => !o)} - size="sm" - mr="xl" - /> -
} -
- - Firegex logonavigator("/")}/> - -
+ return + + + + + Firegex logonavigator("/")}/> + + + + [Fi]*regex +

By Pwnzer0tt1

+
+
-
+ Firewall Access - } onClick={() => setChangePasswordModal(true)}>Change Password + } onClick={() => setChangePasswordModal(true)}>Change Password Actions - } onClick={() => setResetFiregexModal(true)}>Reset Firegex + } onClick={() => setResetFiregexModal(true)}>Reset Firegex @@ -78,7 +81,7 @@ function HeaderPage({navOpen, setNav, ...other}: { navOpen: boolean, setNav:Reac setChangePasswordModal(false)} /> setResetFiregexModal(false)} /> -
+ } export default HeaderPage; diff --git a/frontend/src/components/InterfaceInput.tsx b/frontend/src/components/InterfaceInput.tsx index 6526c53..2e2b1b2 100644 --- a/frontend/src/components/InterfaceInput.tsx +++ b/frontend/src/components/InterfaceInput.tsx @@ -1,55 +1,119 @@ -import { AutocompleteItem, Select, SelectProps } from "@mantine/core"; -import React, { useState } from "react"; +import { Combobox, TextInput, useCombobox } from "@mantine/core"; +import { useState } from "react"; import { ipInterfacesQuery } from "../js/utils"; - -const AutoCompleteItem = React.forwardRef( - ({ netint, value, ...props }: ItemProps, ref) =>
- ( {netint} ) -{">"} {value} -
-); - -interface ItemProps extends AutocompleteItem { +interface ItemProps{ + value: string netint: string; } -interface InterfaceInputProps extends Omit{ - initialCustomInterfaces?:AutocompleteItem[], - includeInterfaceNames?:boolean +interface InterfaceInputProps{ + initialCustomInterfaces?:ItemProps[], + includeInterfaceNames?:boolean, + onChange?: (value:string) => void, + value?:string, + defaultValue?:string } -export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, ...props }:InterfaceInputProps) => { +export const InterfaceInput = ({ initialCustomInterfaces, includeInterfaceNames, onChange, value, defaultValue }:InterfaceInputProps) => { - const [customIpInterfaces, setCustomIpInterfaces] = useState(initialCustomInterfaces??[]); + const [customIpInterfaces, setCustomIpInterfaces] = useState(initialCustomInterfaces??[]); const interfacesQuery = ipInterfacesQuery() const getInterfaces = () => { if (interfacesQuery.isLoading || !interfacesQuery.data) return [] if(includeInterfaceNames){ - const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr, label:item.addr})) as AutocompleteItem[] - interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item, label:item})) + const result = interfacesQuery.data.map(item => ({netint:"IP", value:item.addr})) as ItemProps[] + interfacesQuery.data.map(item => item.name).filter((item, index, arr) => arr.indexOf(item) === index).forEach(item => result.push({netint:"INT", value:item})) return result } - return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr, label:item.addr})) as AutocompleteItem[]) + return (interfacesQuery.data.map(item => ({netint:item.name, value:item.addr})) as ItemProps[]) } const interfaces = getInterfaces() - return