diff --git a/frontend/bun.lockb b/frontend/bun.lockb index b836523..2c1c38d 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/package.json b/frontend/package.json index 98afa13..839d848 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,6 +30,7 @@ "@tiptap/starter-kit": "^2.3.2", "dayjs": "^1.11.11", "embla-carousel-react": "^8.0.4", + "framer-motion": "^11.2.10", "immer": "^10.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/public/xfarm-animation.webm b/frontend/public/xfarm-animation.webm deleted file mode 100644 index 1edc2de..0000000 Binary files a/frontend/public/xfarm-animation.webm and /dev/null differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c503826..6416b06 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -73,6 +73,7 @@ export default function App() { Made with ❤️ and 🚩 by Pwnzer0tt1 + diff --git a/frontend/src/components/ChartView.tsx b/frontend/src/components/ChartView.tsx index 27f01d8..27e0f55 100644 --- a/frontend/src/components/ChartView.tsx +++ b/frontend/src/components/ChartView.tsx @@ -2,7 +2,7 @@ import { hashedColor } from "@/utils" import { flagsStatsQuery, statusQuery } from "@/utils/queries" import { getDateSmallFormatted } from "@/utils/time" import { AreaChart, ChartData } from "@mantine/charts" -import { Space, Title } from "@mantine/core" +import { Space } from "@mantine/core" import { useMemo } from "react" @@ -30,8 +30,6 @@ export const ChartView = () => { }, [stats.isFetching]) return <> - Chart - { - const [page, setPage] = useState(1) - const [bulkPageSize, tablePageSize] = useSettingsStore((state) => [state.pageSizeRequest, state.tablePageSize]) - const pagesForBulk = (bulkPageSize/tablePageSize) - const bulkedPage = Math.ceil(page/pagesForBulk) - const queryClient = useQueryClient() - const getTeamName = useTeamSolver() - const getServiceName = useServiceSolverByExploitId() - const getExploitName = useExploitSolver() - const getClientName = useClientSolver() - const [manualSubmissionModal, setManualSubmissionModal] = useState(false) - - const flagsStats = flagsStatsQuery() const status = statusQuery() - const flags = flagsQuery(bulkedPage) - const totFlags = flagsStats.data?.globals.flags.tot??0 - const totalPages = Math.ceil(totFlags/tablePageSize) - const thStyle: MantineStyleProp = { fontWeight: "bolder", fontSize: "130%", textTransform: "uppercase" } - - const tableData = useMemo(() => { - const offsetArray = ((page-1)%pagesForBulk)*tablePageSize - - return (flags.data?.items??[]).slice(offsetArray,offsetArray+tablePageSize).map((item) => { - const executionTime = (item.attack.start_time && item.attack.end_time)?secondDurationToString((new Date(item.attack.end_time).getTime()-new Date(item.attack.start_time).getTime())/1000):"unknown execution time" - return - {item.id} - {item.flag} {/* Insert click to attack execution details */} - {getServiceName(item.attack.exploit)}using {getExploitName(item.attack.exploit)} exploit - {getTeamName(item.attack.target)} - time: {executionTime}
by {getClientName(item.attack.executed_by)}
- {item.status_text??"No response from submitter"}
Submitted At: {item.last_submission_at?getDateFormatted(item.last_submission_at):"never"}
{/* item.submit_attempts + item.last_submission_at -> Status include number of tries if != 1 and last submission if failed */} - -
- }) - }, [flags.isFetching, page]) - const messages = (status.data?.messages??[]).map((msg, i) => { const lvl = msg.level??"warning" const title = msg.title??"Unknown message" @@ -71,93 +31,26 @@ export const HomePage = () => { return - {messages} - - - - - - - - - - - - - - - - - - - - - - {flags.isLoading?:null} - - - - - ID - Flag - Service - Team - Execution - Response - Status - - - {tableData} -
+ {messages?<>{messages}:null} + + + } color="red"> Flags + } color="green"> Attacks + }> Teams + + - {flags.isLoading?:null} - {totalPages==0?"No flags found!":null} - - -
- setManualSubmissionModal(false)} /> - + + + + + TODO + + + TODO + + + +
} \ No newline at end of file diff --git a/frontend/src/components/LoginProvider.tsx b/frontend/src/components/LoginProvider.tsx index 40cfa99..e257ee3 100644 --- a/frontend/src/components/LoginProvider.tsx +++ b/frontend/src/components/LoginProvider.tsx @@ -64,7 +64,6 @@ export const LoginProvider = ({ children }: { children:any }) => { }}> -
{ + + const [page, setPage] = useState(1) + const [bulkPageSize, tablePageSize] = useSettingsStore((state) => [state.pageSizeRequest, state.tablePageSize]) + const pagesForBulk = (bulkPageSize/tablePageSize) + const bulkedPage = Math.ceil(page/pagesForBulk) + const queryClient = useQueryClient() + const getTeamName = useTeamSolver() + const getServiceName = useServiceSolverByExploitId() + const getExploitName = useExploitSolver() + const getClientName = useClientSolver() + const [manualSubmissionModal, setManualSubmissionModal] = useState(false) + + const flagsStats = flagsStatsQuery() + const flags = flagsQuery(bulkedPage) + const totFlags = flagsStats.data?.globals.flags.tot??0 + const totalPages = Math.ceil(totFlags/tablePageSize) + const thStyle: MantineStyleProp = { fontWeight: "bolder", fontSize: "130%", textTransform: "uppercase" } + + const tableData = useMemo(() => { + const offsetArray = ((page-1)%pagesForBulk)*tablePageSize + + return (flags.data?.items??[]).slice(offsetArray,offsetArray+tablePageSize).map((item) => { + const executionTime = (item.attack.start_time && item.attack.end_time)?secondDurationToString((new Date(item.attack.end_time).getTime()-new Date(item.attack.start_time).getTime())/1000):"unknown execution time" + return + {item.id} + {item.flag} {/* Insert click to attack execution details */} + {getServiceName(item.attack.exploit)}using {getExploitName(item.attack.exploit)} exploit + {getTeamName(item.attack.target)} + time: {executionTime}
by {getClientName(item.attack.executed_by)}
+ {item.status_text??"No response from submitter"}
Submitted At: {item.last_submission_at?getDateFormatted(item.last_submission_at):"never"}
{/* item.submit_attempts + item.last_submission_at -> Status include number of tries if != 1 and last submission if failed */} + +
+ }) + }, [flags.isFetching, page]) + + const flags_tot_stats = { + ok: flagsStats.data?.globals.flags.ok??0, + timeout: flagsStats.data?.globals.flags.timeout??0, + invalid: flagsStats.data?.globals.flags.invalid??0, + wait: flagsStats.data?.globals.flags.wait??0 + } + + return + + + + + + + + + + + + + + + + + + + 0?[ + { value: flags_tot_stats.ok, color: 'lime', name: "Accepted" }, + { value: flags_tot_stats.timeout, color: 'yellow', name: "Expired" }, + { value: flags_tot_stats.invalid, color: 'red', name: "Rejected"}, + { value: flags_tot_stats.wait, color: 'indigo', name: "Queued"}, + ]:[ + { value: 1, color: 'gray', name: "No flags" }, + ]} + + startAngle={0} + endAngle={180} + paddingAngle={1} + size={160} + thickness={35} + withTooltip={totFlags>0} + tooltipDataSource="all" + mx="auto" + withLabelsLine + withLabels={totFlags>0} + style={{ marginBottom: -75 }} + chartLabel={totFlags>0?`${totFlags} Flags`:"No flags"} + /> + + + {flags.isLoading?:null} + + + + + ID + Flag + Service + Team + Execution + Response + Status + + + {tableData} +
+ + {flags.isLoading?:null} + {totalPages==0?"No flags found!":null} + + +
+ setManualSubmissionModal(false)} /> + +
+} \ No newline at end of file diff --git a/frontend/src/components/SetupScreen.tsx b/frontend/src/components/SetupScreen.tsx index 996d152..fa2a359 100644 --- a/frontend/src/components/SetupScreen.tsx +++ b/frontend/src/components/SetupScreen.tsx @@ -6,7 +6,6 @@ export const SetupScreen = () => { return - You need an auto-setup script to run!! In the future will be available a setup wizard on this page. diff --git a/frontend/src/components/WelcomeTitle.tsx b/frontend/src/components/WelcomeTitle.tsx index ec3b32a..70a9355 100644 --- a/frontend/src/components/WelcomeTitle.tsx +++ b/frontend/src/components/WelcomeTitle.tsx @@ -1,4 +1,13 @@ import { Box, Image, Title } from "@mantine/core" +import { MotionStyle, cubicBezier, easeIn, motion, motionValue, useTime, useTransform } from "framer-motion" + +import BombIcon from "@/svg/bomb.svg" +import FlagIcon from "@/svg/flag.svg" +import LaptopIcon from "@/svg/laptop.svg" +import ServerIcon from "@/svg/server.svg" +import ConnectedServerIcon from "@/svg/connected-server.svg" +import JoyStickIcon from "@/svg/joystick.svg" +import { useEffect, useState } from "react" export const WelcomeTitle = ( { title, description, showAnimation }: @@ -22,15 +31,285 @@ export const WelcomeTitle = ( {description??<>The attack manager and flag submitter by Pwnzer0tt1} - { (showAnimation??true)? - :null} + { (showAnimation??true)?:null}
+} + + +export const WelcomeAnimation = () => { + + const time = useTime() + const [animationDuration, setAnimationDuration] = useState(1) + const time_loop = motionValue(0) + + time.on("change", (v) => { + time_loop.set(v%animationDuration) + }) + + const tArr = (times: number[]): [number[], number] => { + let accumulator = 0 + let time_result = times.map(t => { + accumulator += t + return accumulator + }) + return [time_result, accumulator] + } + const animate_timing = cubicBezier(0.17, 0.67, 0.83, 0.67) + const animate_linear = easeIn + + const [initial_icons_scale_time, end_initial_icons_scale_time] = tArr( + [0, 200, 450] + ) + + const initial_icons: MotionStyle = { + scale: useTransform( + time_loop, + initial_icons_scale_time, + [0, 1.2, 1], + { clamp: false, ease: animate_timing}, + ), + } + + const init_middle_icons = end_initial_icons_scale_time+200 + const [middle_icons_scale_time, end_middle_icons_scale_time] = tArr( + [init_middle_icons, 200, 300] + ) + + const middle_icons:MotionStyle = { + opacity: useTransform(time_loop,[init_middle_icons],[1], + { clamp: false, ease: animate_timing}, + ), + scale: useTransform( + time_loop, + middle_icons_scale_time, + [0, 1.2, 1], + { clamp: false, ease: animate_timing}, + ), + } + const init_bomb_time = end_middle_icons_scale_time+200 + const [inital_bomb_scale_time, end_inital_bomb_scale_time] = tArr( + [init_bomb_time, 200, 300] + ) + const [bomb_rotate_time, end_bomb_rotate_time] = tArr( + [init_bomb_time, 400, 400, 400] + ) + const end_enter_time = Math.max(end_inital_bomb_scale_time, end_bomb_rotate_time) + const [bomb_x_time, end_bomb_x_time] = tArr( + [end_enter_time, 800] + ) + const [final_bomb_scale_time, end_final_bomb_scale_time] = tArr( + [end_bomb_x_time, 300, 400] + ) + + const bomb_animation: MotionStyle = { + opacity: useTransform(time_loop,[1400],[1], + { clamp: false, ease: animate_timing}, + ), + scale: useTransform( + time_loop, + [...inital_bomb_scale_time, ...final_bomb_scale_time], + [0, 1.2, 1, 1, 1.3, 0], + { clamp: false, ease: animate_timing}, + ), + rotate: useTransform( + time_loop, + bomb_rotate_time, + [0, 30, -30, 0], + { clamp: false, ease: animate_timing}, + ), + x: useTransform( + time_loop, + bomb_x_time, + [0, -100], + { clamp: false, ease: animate_linear}, + ), + } + + const init_flag_animation = end_final_bomb_scale_time+200 + const [inital_flag_scale_time, end_inital_flag_scale_time] = tArr( + [init_flag_animation, 200, 300] + ) + + const delay_flag_remain = 300 + const [x_flag_time_laptop, end_x_flag_time_laptop] = tArr( + [end_inital_flag_scale_time+100, 600] + ) + const [x_flag_time_server, end_x_flag_time_server] = tArr( + [end_x_flag_time_laptop+delay_flag_remain, 600] + ) + const [x_flag_time_submit, end_x_flag_time_submit] = tArr( + [end_x_flag_time_server+delay_flag_remain, 600] + ) + const end_flag_move = end_x_flag_time_submit+delay_flag_remain + + const [final_scale_flag_time, end_final_scale_flag_time] = tArr( + [end_flag_move, 200, 300] + ) + + const flag_animation: MotionStyle = { + opacity: useTransform(time_loop,[0, init_flag_animation-1, init_flag_animation],[0, 0, 1], + { clamp: false, ease: animate_timing}, + ), + rotate: useTransform( + time_loop, + [ + end_x_flag_time_laptop, + end_x_flag_time_laptop+(delay_flag_remain/3), + end_x_flag_time_laptop+(delay_flag_remain/3)*2, + end_x_flag_time_laptop+delay_flag_remain, + end_x_flag_time_server, + end_x_flag_time_server+(delay_flag_remain/3), + end_x_flag_time_server+(delay_flag_remain/3)*2, + end_x_flag_time_server+delay_flag_remain, + end_x_flag_time_submit, + end_x_flag_time_submit+(delay_flag_remain/3), + end_x_flag_time_submit+(delay_flag_remain/3)*2, + end_flag_move, + ], + [0, 30, -30, 0, 0, 30, -30, 0, 0, 30, -30, 0], + { clamp: false, ease: animate_timing}, + ), + scale: useTransform( + time_loop, + [ + ...inital_flag_scale_time, + ...final_scale_flag_time, + ], + [0, 1.2, 1, 1, 1.2, 0], + { clamp: false, ease: animate_timing}, + ), + x: useTransform( + time_loop, + [ + ...x_flag_time_laptop, + ...x_flag_time_server, + ...x_flag_time_submit, + ], + [0, 130, 130, 265, 265, 420], + { clamp: false, ease: animate_linear}, + ), + } + + const init_plus_1_time = end_final_scale_flag_time+200 + + const [initial_plus_1_time, end_initial_plus_1_time] = tArr( + [init_plus_1_time, 200, 300] + ) + + const [up_plus_1_time, end_up_plus_1_time] = tArr( + [init_plus_1_time, 200] + ) + + const final_init_plus_1_time = Math.max(end_initial_plus_1_time, end_up_plus_1_time) + + const [final_plus_1_time, end_final_plus_1_time] = tArr( + [final_init_plus_1_time+1200, 200, 300] + ) + + const [rotate_plus_1_time, end_rotate_plus_1_time] = tArr( + [final_init_plus_1_time, 400, 400, 200] + ) + + const end_plus_1_time = Math.max(end_final_plus_1_time, end_rotate_plus_1_time) + + const plus_1_animation: MotionStyle = { + opacity: useTransform(time_loop,[0, init_plus_1_time-1, init_plus_1_time],[0, 0, 1], + { clamp: false, ease: animate_timing}, + ), + scale: useTransform( + time_loop, + [ + ...initial_plus_1_time, + ...final_plus_1_time, + ], + [0, 1.2, 1, 1, 1.2, 0], + { clamp: false, ease: animate_timing}, + ), + rotate: useTransform( + time_loop, + rotate_plus_1_time, + [0, 10, -10, 0], + { clamp: false, ease: animate_timing}, + ), + y: useTransform( + time_loop, + up_plus_1_time, + [0, -80], + { clamp: false, ease: animate_timing }, + ), + } + + const [ending_animate_all, end_time] = tArr( + [end_plus_1_time+800, 150] + ) + + const spaceBetween = 70 + + useEffect(() => { + setAnimationDuration(end_time+300) + }, [end_time]) + + return + +
+ +
+ + + + +
+ + + + +1 + } \ No newline at end of file diff --git a/frontend/src/svg/bomb.svg b/frontend/src/svg/bomb.svg new file mode 100644 index 0000000..1d86d96 --- /dev/null +++ b/frontend/src/svg/bomb.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/src/svg/connected-server.svg b/frontend/src/svg/connected-server.svg new file mode 100644 index 0000000..de544f0 --- /dev/null +++ b/frontend/src/svg/connected-server.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/svg/flag.svg b/frontend/src/svg/flag.svg new file mode 100644 index 0000000..eccb857 --- /dev/null +++ b/frontend/src/svg/flag.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/src/svg/joystick.svg b/frontend/src/svg/joystick.svg new file mode 100644 index 0000000..ed8b9ad --- /dev/null +++ b/frontend/src/svg/joystick.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/frontend/src/svg/laptop.svg b/frontend/src/svg/laptop.svg new file mode 100644 index 0000000..3115208 --- /dev/null +++ b/frontend/src/svg/laptop.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/svg/server.svg b/frontend/src/svg/server.svg new file mode 100644 index 0000000..286e3ea --- /dev/null +++ b/frontend/src/svg/server.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file