mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 15:10:43 +08:00
feat: support boot-ui
This commit is contained in:
@@ -0,0 +1 @@
|
||||
VITE_APP_API_URL=
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
@@ -30,7 +30,7 @@
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"enabled": true,
|
||||
"lineWidth": 100,
|
||||
"lineWidth": 120,
|
||||
"ignore": ["node_modules", "tsconfig*", "dist"]
|
||||
},
|
||||
"organizeImports": {
|
||||
|
||||
Binary file not shown.
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
rm -rf ../boot/src/main/resources/static/assets/*
|
||||
|
||||
mkdir -p ../boot/src/main/resources/static/assets/
|
||||
cp dist/vite.svg ../boot/src/main/resources/static/
|
||||
cp -R dist/assets/* ../boot/src/main/resources/static/assets/
|
||||
|
||||
cp dist/index.html ../boot/src/main/resources/templates
|
||||
+24
-15
@@ -6,42 +6,51 @@
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "vite --port=3001",
|
||||
"build": "vite build",
|
||||
"build": "tsc -b && vite build --mode production",
|
||||
"serve": "vite preview",
|
||||
"check": "bunx @biomejs/biome check ./ --write",
|
||||
"start": "vite"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@tanstack/router-plugin": "^1.86.0",
|
||||
"@types/node": "^22.10.1",
|
||||
"@tanstack/router-plugin": "^1.91.1",
|
||||
"@types/node": "^22.10.2",
|
||||
"@types/react": "^19.0.1",
|
||||
"@types/react-dom": "^19.0.1",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.16",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-radio-group": "^1.2.1",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.1.1",
|
||||
"@radix-ui/react-tabs": "^1.1.1",
|
||||
"@tanstack/react-router": "^1.87.0",
|
||||
"@tanstack/router-devtools": "^1.87.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@radix-ui/react-checkbox": "^1.1.3",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||
"@radix-ui/react-label": "^2.1.1",
|
||||
"@radix-ui/react-radio-group": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.1.4",
|
||||
"@radix-ui/react-separator": "^1.1.1",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"@tanstack/react-query": "^5.62.8",
|
||||
"@tanstack/react-router": "^1.91.2",
|
||||
"@tanstack/router-devtools": "^1.91.2",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.54.1",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"sonner": "^1.7.1",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.24.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Button, ButtonProps } from "@/components/ui/button.tsx";
|
||||
import { cn } from "@/lib/utils.ts";
|
||||
import { CheckIcon, ClipboardIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { HTMLProps, useEffect, useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface CopyButtonProps extends ButtonProps {
|
||||
value: string;
|
||||
@@ -14,24 +15,21 @@ export function copyToClipboardWithMeta(value: string) {
|
||||
navigator.clipboard.writeText(value);
|
||||
}
|
||||
|
||||
export function CopyButton({
|
||||
value,
|
||||
className,
|
||||
src,
|
||||
variant = "ghost",
|
||||
...props
|
||||
}: CopyButtonProps) {
|
||||
export function CopyButton({ value, className, src, variant = "ghost", ...props }: CopyButtonProps) {
|
||||
const [hasCopied, setHasCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setHasCopied(false);
|
||||
}, 2000);
|
||||
}, []);
|
||||
if (hasCopied) {
|
||||
setTimeout(() => {
|
||||
setHasCopied(false);
|
||||
}, 1000);
|
||||
}
|
||||
}, [hasCopied]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="icon"
|
||||
type="button"
|
||||
variant={variant}
|
||||
className={cn(
|
||||
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:h-3 [&_svg]:w-3",
|
||||
@@ -40,6 +38,7 @@ export function CopyButton({
|
||||
onClick={() => {
|
||||
copyToClipboardWithMeta(value);
|
||||
setHasCopied(true);
|
||||
toast.success("复制成功");
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
@@ -53,22 +52,33 @@ export function CodeViewer({
|
||||
code,
|
||||
language,
|
||||
showLineNumbers = true,
|
||||
wrapLongLines = false,
|
||||
}: {
|
||||
code: string;
|
||||
language: string;
|
||||
showLineNumbers?: boolean;
|
||||
wrapLongLines?: boolean;
|
||||
}) {
|
||||
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined = wrapLongLines
|
||||
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
|
||||
: undefined;
|
||||
return (
|
||||
<div className="relative overflow-hidden text-xs">
|
||||
<div className="relative overflow-hidden text-xs wrap-all">
|
||||
<CopyButton value={code} className="absolute right-4 top-2" />
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={materialDark}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLongLines={wrapLongLines}
|
||||
lineProps={lineProps}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
paddingRight: showLineNumbers ? 0 : 24,
|
||||
paddingLeft: showLineNumbers ? 0 : 24,
|
||||
borderRadius: "var(--radius)",
|
||||
height: 600,
|
||||
height: 500,
|
||||
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
|
||||
overflowWrap: wrapLongLines ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
|
||||
@@ -1,17 +1,39 @@
|
||||
import { UrlPatternTip } from "@/components/tips/url-pattern-tip.tsx";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { Label } from "@/components/ui/label.tsx";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select.tsx";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { Switch } from "@/components/ui/switch.tsx";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { MainConfig } from "@/types/shell.ts";
|
||||
import { ServerIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
|
||||
const JDKVersion = [
|
||||
{ name: "Java6", value: "50" },
|
||||
{ name: "Java8", value: "52" },
|
||||
{ name: "Java9", value: "53" },
|
||||
{ name: "Java11", value: "55" },
|
||||
{ name: "Java17", value: "61" },
|
||||
{ name: "Java21", value: "65" },
|
||||
];
|
||||
|
||||
export function MainConfigCard({
|
||||
mainConfig,
|
||||
form,
|
||||
servers,
|
||||
}: {
|
||||
mainConfig: MainConfig | undefined;
|
||||
form: UseFormReturn<FormSchema>;
|
||||
servers?: string[];
|
||||
}) {
|
||||
const [shellToolMap, setShellToolMap] = useState<{ [toolName: string]: string[] }>();
|
||||
const [shellTools, setShellTools] = useState<string[]>([]);
|
||||
const [shellTypes, setShellTypes] = useState<string[]>([]);
|
||||
|
||||
export function MainConfigCard() {
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader className="pb-1">
|
||||
@@ -20,87 +42,280 @@ export function MainConfigCard() {
|
||||
<span>核心配置</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="server" className="text-sm">
|
||||
目标服务
|
||||
<FormProvider {...form}>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="server"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>目标服务</FormLabel>
|
||||
<Select
|
||||
onValueChange={(v) => {
|
||||
field.onChange(v);
|
||||
if (mainConfig) {
|
||||
setShellToolMap(mainConfig[v]);
|
||||
setShellTools(Object.keys(mainConfig[v]));
|
||||
setShellTypes([]);
|
||||
}
|
||||
}}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{servers?.map((server: string) => (
|
||||
<SelectItem key={server} value={server}>
|
||||
{server}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="targetJdkVersion"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>JRE(可选)</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{JDKVersion.map((v) => (
|
||||
<SelectItem key={v.value} value={v.value}>
|
||||
{v.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4 mt-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="debug"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id="debug" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<FormLabel htmlFor="debug">开启调试</FormLabel>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="bypassJavaModule"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
||||
<FormControl>
|
||||
<Switch id="bypassJavaModule" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<Label htmlFor="bypassJavaModule">bypassJavaModule</Label>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="lambda" disabled />
|
||||
<Label htmlFor="lambda">Lambda 类名 (WIP)</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="obfuscate" disabled />
|
||||
<Label htmlFor="obfuscate">开启混淆 (WIP)</Label>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="mt-4 mb-2" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="shellClassName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>内存马类名(可选)</FormLabel>
|
||||
<Input id="shellClassName" {...field} placeholder="请输入" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="grid grid-cols-3 gap-2 mt-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="shellTool"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>内存马工具类型</FormLabel>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={(value: string) => {
|
||||
field.onChange(value);
|
||||
if (shellToolMap) {
|
||||
setShellTypes(shellToolMap[value]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{shellTools.length ? (
|
||||
shellTools.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v}
|
||||
</SelectItem>
|
||||
))
|
||||
) : (
|
||||
<SelectItem value=" ">请先选择目标服务</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="shellType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>内存马挂载类型</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{shellTypes.length ? (
|
||||
shellTypes.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v}
|
||||
</SelectItem>
|
||||
))
|
||||
) : (
|
||||
<SelectItem value=" ">请先选择内存马工具类型</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="urlPattern"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col mt-1">
|
||||
<Label className="flex items-center">
|
||||
请求路径 <UrlPatternTip />
|
||||
</Label>
|
||||
<Input {...field} placeholder="请输入" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
{form.getValues().shellTool === "Godzilla" && (
|
||||
<div className="space-y-1">
|
||||
<Label>Godzilla 配置</Label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="godzillaPass"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1 flex items-center justify-start">
|
||||
<Label className="text-xs whitespace-nowrap w-1/2">密码:</Label>
|
||||
<Input {...field} placeholder="Pass" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="godzillaKey"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1 flex items-center justify-start">
|
||||
<Label className="text-xs whitespace-nowrap w-1/2">密钥:</Label>
|
||||
<Input {...field} placeholder="Key" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="godzillaHeaderName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1 flex items-center justify-start">
|
||||
<Label className="text-xs whitespace-nowrap w-1/2">请求头键:</Label>
|
||||
<Input {...field} placeholder="Header Name" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="godzillaHeaderValue"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1 flex items-center justify-start">
|
||||
<Label className="text-xs whitespace-nowrap w-1/2">请求头值:</Label>
|
||||
<Input {...field} placeholder="Header Value" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{form.getValues().shellTool === "Command" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="commandParamName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>请求参数</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="请输入" className="h-8" />
|
||||
</FormControl>
|
||||
<FormDescription>填写接收命令的请求参数,例如填 cmd 即 `?cmd=whoami` 来执行命令</FormDescription>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Separator className="mt-4 mb-2" />
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="injectorClassName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>注入器类名(可选)</FormLabel>
|
||||
<Input id="injectorClassName" {...field} placeholder="请输入" className="h-8" />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="space-y-1 mt-2">
|
||||
<Label htmlFor="interface" className="flex items-center gap-2">
|
||||
注入器类继承(可选)(WIP)
|
||||
</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="server" className="h-8">
|
||||
<Select disabled>
|
||||
<SelectTrigger id="interface" className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="tomcat">Tomcat</SelectItem>
|
||||
<SelectItem value="jetty">Jetty</SelectItem>
|
||||
<SelectItem value="undertow">Undertow</SelectItem>
|
||||
<SelectItem value="jboss">JBoss</SelectItem>
|
||||
<SelectItem value="wildfly">Wildfly</SelectItem>
|
||||
<SelectItem value="springmvc">SpringMVC</SelectItem>
|
||||
<SelectItem value="springwebflux">SpringWebflux</SelectItem>
|
||||
<SelectItem value="weblogic">WebLogic</SelectItem>
|
||||
<SelectItem value="websphere">WebSphere</SelectItem>
|
||||
<SelectItem value="resin">Resin</SelectItem>
|
||||
<SelectItem value="glassfish">Glassfish</SelectItem>
|
||||
<SelectItem value="bes">BES</SelectItem>
|
||||
<SelectItem value="tongweb">TongWeb</SelectItem>
|
||||
<SelectItem value="JDK_AbstractTranslet">JDK_AbstractTranslet</SelectItem>
|
||||
<SelectItem value="XALAN_AbstractTranslet">XALAN_AbstractTranslet</SelectItem>
|
||||
<SelectItem value="FASTJSON_GroovyASTTransformation">FASTJSON_GroovyASTTransformation</SelectItem>
|
||||
<SelectItem value="SnakeYaml_ScriptEngineFactory">SnakeYaml_ScriptEngineFactory</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="targetJdkVersion" className="text-sm">
|
||||
JRE
|
||||
</Label>
|
||||
<Select defaultValue="6">
|
||||
<SelectTrigger id="targetJdkVersion" className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="6">Java 6</SelectItem>
|
||||
<SelectItem value="7">Java 7</SelectItem>
|
||||
<SelectItem value="8">Java 8</SelectItem>
|
||||
<SelectItem value="9">Java 9</SelectItem>
|
||||
<SelectItem value="11">Java 11</SelectItem>
|
||||
<SelectItem value="17">Java 17</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<Label htmlFor="shellClassName" className="text-sm">
|
||||
自定义内存马类名(可选)
|
||||
</Label>
|
||||
<Input id="shellClassName" placeholder="请输入" className="h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="injectorClassName" className="text-sm">
|
||||
自定义注入器类名(可选)
|
||||
</Label>
|
||||
<Input id="injectorClassName" placeholder="请输入" className="h-8" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="obfuscate" />
|
||||
<Label htmlFor="obfuscate" className="text-sm">
|
||||
开启混淆
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="debug" />
|
||||
<Label htmlFor="debug" className="text-sm">
|
||||
开启调试
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch id="lambda" />
|
||||
<Label htmlFor="debug" className="text-sm">
|
||||
Lambda 类名
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</CardContent>
|
||||
</FormProvider>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { Label } from "@/components/ui/label.tsx";
|
||||
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/form.tsx";
|
||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group.tsx";
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { PackerConfig } from "@/types/shell.ts";
|
||||
import { PackageIcon } from "lucide-react";
|
||||
import { FormProvider, UseFormReturn } from "react-hook-form";
|
||||
|
||||
export function PackageConfigCard() {
|
||||
export function PackageConfigCard({
|
||||
packerConfig,
|
||||
form,
|
||||
}: {
|
||||
packerConfig: PackerConfig | undefined;
|
||||
form: UseFormReturn<FormSchema>;
|
||||
}) {
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader className="pb-1">
|
||||
@@ -12,74 +21,32 @@ export function PackageConfigCard() {
|
||||
<span>打包配置</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label className="text-sm">打包方式</Label>
|
||||
<RadioGroup defaultValue="base64">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="base64" id="base64" />
|
||||
<Label htmlFor="base64" className="text-xs">
|
||||
Base64
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="jsp" id="jsp" />
|
||||
<Label htmlFor="jsp" className="text-xs">
|
||||
JSP
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="classFile" id="classFile" />
|
||||
<Label htmlFor="classFile" className="text-xs">
|
||||
反序列化
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="scriptEngine" id="scriptEngine" />
|
||||
<Label htmlFor="scriptEngine" className="text-xs">
|
||||
ScriptEngine
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="elExpression" id="elExpression" />
|
||||
<Label htmlFor="elExpression" className="text-xs">
|
||||
EL Expression
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="ognlExpression" id="ognlExpression" />
|
||||
<Label htmlFor="ognlExpression" className="text-xs">
|
||||
OGNL Expression
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="spelExpression" id="spelExpression" />
|
||||
<Label htmlFor="spelExpression" className="text-xs">
|
||||
SpEL Expression
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="elExpression" id="elExpression" />
|
||||
<Label htmlFor="elExpression" className="text-xs">
|
||||
EL Expression
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="freemarkerExpression" id="freemarkerExpression" />
|
||||
<Label htmlFor="freemarkerExpression" className="text-xs">
|
||||
Freemarker
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="velocityExpression" id="velocityExpression" />
|
||||
<Label htmlFor="velocityExpression" className="text-xs">
|
||||
Velocity
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<CardContent>
|
||||
<FormProvider {...form}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="packingMethod"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-3">
|
||||
<FormLabel>打包方式</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup onValueChange={field.onChange} defaultValue={field.value} className="grid grid-cols-3">
|
||||
{Object.entries(packerConfig ?? {}).map(([name, value]) => (
|
||||
<FormItem key={value} className="flex items-center space-x-3 space-y-0">
|
||||
<FormControl>
|
||||
<RadioGroupItem value={value} id={value} />
|
||||
</FormControl>
|
||||
<FormLabel className="text-xs" htmlFor={value}>
|
||||
{name}
|
||||
</FormLabel>
|
||||
</FormItem>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</FormProvider>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { Input } from "@/components/ui/input.tsx";
|
||||
import { Label } from "@/components/ui/label.tsx";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select.tsx";
|
||||
import { FishSymbolIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ShellConfigCard() {
|
||||
const [shellTool, setShellTool] = useState<string>("");
|
||||
return (
|
||||
<Card className="w-full">
|
||||
<CardHeader className="pb-1">
|
||||
<CardTitle className="text-md flex items-center gap-2">
|
||||
<FishSymbolIcon className="h-5" />
|
||||
<span>内存马配置</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="shellType" className="text-sm">
|
||||
挂载类型
|
||||
</Label>
|
||||
<Select>
|
||||
<SelectTrigger id="shellType" className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="filter">Filter</SelectItem>
|
||||
<SelectItem value="servlet">Servlet</SelectItem>
|
||||
<SelectItem value="listener">Listener</SelectItem>
|
||||
<SelectItem value="agent">Agent</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="shellTool" className="text-sm">
|
||||
工具类型
|
||||
</Label>
|
||||
<Select onValueChange={(value: string) => setShellTool(value)}>
|
||||
<SelectTrigger id="shellTool" className="h-8">
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="command">命令执行</SelectItem>
|
||||
<SelectItem value="fileList">File List</SelectItem>
|
||||
<SelectItem value="gozilla">Godzilla</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{shellTool === "gozilla" && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-sm">Godzilla 配置</Label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Input placeholder="Pass" className="h-8 text-sm" />
|
||||
<Input placeholder="Key" className="h-8 text-sm" />
|
||||
<Input placeholder="Header Name" className="h-8 text-sm" />
|
||||
<Input placeholder="Header Value" className="h-8 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{shellTool === "command" && (
|
||||
<div className="space-y-1">
|
||||
<Label htmlFor="paramName" className="text-sm">
|
||||
接收命令请求参数
|
||||
</Label>
|
||||
<Input id="paramName" placeholder="请输入" className="h-8 text-sm" />
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -26,9 +26,7 @@ export function ThemeProvider({
|
||||
storageKey = "vite-ui-theme",
|
||||
...props
|
||||
}: ThemeProviderProps) {
|
||||
const [theme, setTheme] = useState<Theme>(
|
||||
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
|
||||
);
|
||||
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme);
|
||||
|
||||
useEffect(() => {
|
||||
const root = window.document.documentElement;
|
||||
@@ -37,9 +35,7 @@ export function ThemeProvider({
|
||||
|
||||
root.removeAttribute(mode);
|
||||
if (theme === "system") {
|
||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
|
||||
root.classList.add(systemTheme);
|
||||
root.setAttribute(mode, systemTheme);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
|
||||
export function JRETip() {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<InfoIcon className="cursor-pointer h-3" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>目标 JRE 版本,一般而言为了最大的兼容性,默认 Java 6 即可。</p>
|
||||
<p>特定情况下,例如 JDK8 才能使用 lambda 表达式,JDK9 以上存在模块限制时才需要选择特定的版本。</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip.tsx";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
|
||||
export function UrlPatternTip() {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<InfoIcon className="cursor-pointer h-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>当使用 Servlet 内存马时必须写具体的 urlPattern,不能使用 /*,不然无法使用</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
@@ -9,8 +9,7 @@ const alertVariants = cva(
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
@@ -29,21 +28,16 @@ Alert.displayName = "Alert";
|
||||
|
||||
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
<h5 ref={ref} className={cn("mb-1 font-medium leading-none tracking-tight", className)} {...props} />
|
||||
),
|
||||
);
|
||||
AlertTitle.displayName = "AlertTitle";
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
|
||||
));
|
||||
const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
|
||||
),
|
||||
);
|
||||
AlertDescription.displayName = "AlertDescription";
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription };
|
||||
|
||||
@@ -11,8 +11,7 @@ const buttonVariants = cva(
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
@@ -40,9 +39,7 @@ export interface ButtonProps
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
|
||||
);
|
||||
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
|
||||
},
|
||||
);
|
||||
Button.displayName = "Button";
|
||||
|
||||
@@ -2,15 +2,9 @@ import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("rounded-xl border bg-card text-card-foreground shadow", className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("rounded-xl border bg-card text-card-foreground shadow", className)} {...props} />
|
||||
));
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
@@ -22,11 +16,7 @@ CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
<div ref={ref} className={cn("font-semibold leading-none tracking-tight", className)} {...props} />
|
||||
),
|
||||
);
|
||||
CardTitle.displayName = "CardTitle";
|
||||
@@ -39,9 +29,7 @@ const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HT
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
),
|
||||
({ className, ...props }, ref) => <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />,
|
||||
);
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
|
||||
@@ -152,18 +152,12 @@ const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
<DropdownMenuPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
||||
));
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
||||
|
||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
|
||||
);
|
||||
return <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />;
|
||||
};
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import * as React from "react";
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, useFormContext } from "react-hook-form";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
> = {
|
||||
name: TName;
|
||||
};
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState, formState } = useFormContext();
|
||||
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error("useFormField should be used within <FormField>");
|
||||
}
|
||||
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
};
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
|
||||
|
||||
const FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => {
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div ref={ref} className={cn("space-y-2", className)} {...props} />
|
||||
</FormItemContext.Provider>
|
||||
);
|
||||
},
|
||||
);
|
||||
FormItem.displayName = "FormItem";
|
||||
|
||||
const FormLabel = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return <Label ref={ref} className={cn(error && "text-destructive", className)} htmlFor={formItemId} {...props} />;
|
||||
});
|
||||
FormLabel.displayName = "FormLabel";
|
||||
|
||||
const FormControl = React.forwardRef<React.ElementRef<typeof Slot>, React.ComponentPropsWithoutRef<typeof Slot>>(
|
||||
({ ...props }, ref) => {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
ref={ref}
|
||||
id={formItemId}
|
||||
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
FormControl.displayName = "FormControl";
|
||||
|
||||
const FormDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => {
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p ref={ref} id={formDescriptionId} className={cn("text-[0.8rem] text-muted-foreground", className)} {...props} />
|
||||
);
|
||||
},
|
||||
);
|
||||
FormDescription.displayName = "FormDescription";
|
||||
|
||||
const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, children, ...props }, ref) => {
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message) : children;
|
||||
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
ref={ref}
|
||||
id={formMessageId}
|
||||
className={cn("text-[0.8rem] font-medium text-destructive", className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
);
|
||||
},
|
||||
);
|
||||
FormMessage.displayName = "FormMessage";
|
||||
|
||||
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
|
||||
@@ -6,9 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
);
|
||||
const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
|
||||
@@ -94,11 +94,7 @@ const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
<SelectPrimitive.Label ref={ref} className={cn("px-2 py-1.5 text-sm font-semibold", className)} {...props} />
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
@@ -128,11 +124,7 @@ const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
<SelectPrimitive.Separator ref={ref} className={cn("-mx-1 my-1 h-px bg-muted", className)} {...props} />
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as React from "react";
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Separator = React.forwardRef<
|
||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||
|
||||
export { Separator };
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Toaster as Sonner } from "sonner";
|
||||
import { useTheme } from "@/components/theme-provider.tsx";
|
||||
|
||||
type ToasterProps = React.ComponentProps<typeof Sonner>;
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme();
|
||||
|
||||
return (
|
||||
<Sonner theme={theme as ToasterProps["theme"]} richColors className="toaster group" toastOptions={{}} {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
export { Toaster };
|
||||
@@ -0,0 +1,30 @@
|
||||
import * as React from "react";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
|
||||
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</TooltipPrimitive.Portal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as z from "zod";
|
||||
|
||||
const EnvSchema = z.object({
|
||||
API_URL: z.optional(z.string()),
|
||||
});
|
||||
|
||||
const createEnv = () => {
|
||||
// @ts-ignore
|
||||
const envVars = Object.entries(import.meta.env).reduce<Record<string, string>>((acc, curr) => {
|
||||
const [key, value] = curr;
|
||||
if (key.startsWith("VITE_APP_")) {
|
||||
if (typeof value === "string") {
|
||||
acc[key.replace("VITE_APP_", "")] = value;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
const parsedEnv = EnvSchema.safeParse(envVars);
|
||||
if (!parsedEnv.success) {
|
||||
throw new Error(
|
||||
`Invalid env provided.
|
||||
The following variables are missing or invalid:
|
||||
${Object.entries(parsedEnv.error.flatten().fieldErrors)
|
||||
.map(([k, v]) => `- ${k}: ${v}`)
|
||||
.join("\n")}
|
||||
`,
|
||||
);
|
||||
}
|
||||
return parsedEnv.data;
|
||||
};
|
||||
|
||||
export const env = createEnv();
|
||||
@@ -29,6 +29,7 @@
|
||||
--chart-5: 27 87% 67%;
|
||||
--radius: 0.3rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
@@ -56,10 +57,12 @@
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,32 @@
|
||||
import { type ClassValue, clsx } from "clsx";
|
||||
import { toast } from "sonner";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function downloadJavaClass(base64String?: string, className?: string) {
|
||||
if (!base64String || !className) {
|
||||
toast.warning("内存马字节码为空,无法下载, 请先生成内存马");
|
||||
return;
|
||||
}
|
||||
const byteCharacters = atob(base64String);
|
||||
const byteNumbers = new Array(byteCharacters.length);
|
||||
for (let i = 0; i < byteCharacters.length; i++) {
|
||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
||||
}
|
||||
const byteArray = new Uint8Array(byteNumbers);
|
||||
|
||||
// Create a Blob from the byte array
|
||||
const blob = new Blob([byteArray], { type: "application/java-vm" });
|
||||
|
||||
// Create a download link
|
||||
const link = document.createElement("a");
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = `${className.substring(className.lastIndexOf("."))}.class`;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
+12
-3
@@ -2,14 +2,17 @@ import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
import "./index.css";
|
||||
import { TailwindIndicator } from "@/components/tailwind-indicator.tsx";
|
||||
import { Toaster } from "@/components/ui/sonner.tsx";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
// Set up a Router instance
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
defaultPreload: "intent",
|
||||
});
|
||||
|
||||
// Register things for typesafety
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
@@ -20,5 +23,11 @@ const rootElement = document.getElementById("app") as HTMLElement;
|
||||
|
||||
if (!rootElement.innerHTML) {
|
||||
const root = ReactDOM.createRoot(rootElement);
|
||||
root.render(<RouterProvider router={router} />);
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Toaster />
|
||||
<RouterProvider router={router} />
|
||||
<TailwindIndicator />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,9 +67,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
};
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>();
|
||||
export const routeTree = rootRoute._addFileChildren(rootRouteChildren)._addFileTypes<FileRouteTypes>();
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { ModeToggle } from "@/components/mode-toggle.tsx";
|
||||
import { ThemeProvider } from "@/components/theme-provider.tsx";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
||||
import { ServerOffIcon } from "lucide-react";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootComponent,
|
||||
@@ -43,13 +40,7 @@ function RootComponent() {
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<Alert className="px-4 border-0 border-b">
|
||||
<ServerOffIcon className="h-4 w-4" />
|
||||
<AlertTitle>服务不可用!</AlertTitle>
|
||||
<AlertDescription>未检测到可用后端服务,生成功能暂不可用.</AlertDescription>
|
||||
</Alert>
|
||||
<Outlet />
|
||||
<TanStackRouterDevtools position="bottom-right" />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
+120
-16
@@ -1,30 +1,134 @@
|
||||
import { MainConfigCard } from "@/components/main-config-card.tsx";
|
||||
import { PackageConfigCard } from "@/components/package-config-card.tsx";
|
||||
import { ShellConfigCard } from "@/components/shell-config-card.tsx";
|
||||
import { ShellResult } from "@/components/shell-result.tsx";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Form } from "@/components/ui/form.tsx";
|
||||
import { env } from "@/config.ts";
|
||||
import { FormSchema, formSchema } from "@/types/schema.ts";
|
||||
import { APIErrorResponse, ConfigResponseType, GenerateResponse, GenerateResult } from "@/types/shell.ts";
|
||||
import { transformToPostData } from "@/utils/transformer.ts";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { WandSparklesIcon } from "lucide-react";
|
||||
import { ActivityIcon, LoaderCircle, ServerOffIcon, WandSparklesIcon } from "lucide-react";
|
||||
import { useState, useTransition } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: AboutComponent,
|
||||
component: IndexComponent,
|
||||
});
|
||||
|
||||
function AboutComponent() {
|
||||
function IndexComponent() {
|
||||
const { isPending, isError, data } = useQuery<ConfigResponseType>({
|
||||
queryKey: ["config"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${env.API_URL}/config`);
|
||||
return await response.json();
|
||||
},
|
||||
});
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
server: "",
|
||||
targetJdkVersion: "50",
|
||||
debug: false,
|
||||
bypassJavaModule: false,
|
||||
shellClassName: "",
|
||||
shellTool: "",
|
||||
shellType: "",
|
||||
urlPattern: "/*",
|
||||
godzillaPass: "pass",
|
||||
godzillaKey: "key",
|
||||
godzillaHeaderName: "User-Agent",
|
||||
godzillaHeaderValue: "test",
|
||||
commandParamName: "cmd",
|
||||
injectorClassName: "",
|
||||
packingMethod: "Base64",
|
||||
},
|
||||
});
|
||||
|
||||
const [packResult, setPackResult] = useState<string>("// 等待填写参数生成中");
|
||||
const [generateResult, setGenerateResult] = useState<GenerateResult>();
|
||||
const [packMethod, setPackMethod] = useState<string>("");
|
||||
const [isActionPending, startTransition] = useTransition();
|
||||
|
||||
async function onSubmit(values: FormSchema) {
|
||||
startTransition(async () => {
|
||||
if (values.shellType.endsWith("Servlet") && values.urlPattern === "/*") {
|
||||
toast.warning("Servlet 类型的需要填写具体的 URL Pattern,例如 /hello_servlet");
|
||||
return;
|
||||
}
|
||||
|
||||
const postData = transformToPostData(values);
|
||||
try {
|
||||
const response = await fetch(`${env.API_URL}/generate`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(postData),
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
if (response.ok) {
|
||||
const json: GenerateResponse = await response.json();
|
||||
setPackResult(json.packResult);
|
||||
setGenerateResult(json.generateResult);
|
||||
setPackMethod(values.packingMethod);
|
||||
toast.success("生成成功");
|
||||
} else {
|
||||
const json: APIErrorResponse = await response.json();
|
||||
toast.error(`生成失败,${json.error}`);
|
||||
}
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
toast.error(`生成失败,${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row gap-4 p-4">
|
||||
<div className="w-full md:w-1/2 space-y-4">
|
||||
<MainConfigCard />
|
||||
<ShellConfigCard />
|
||||
<PackageConfigCard />
|
||||
<Button className="w-full">
|
||||
<WandSparklesIcon />
|
||||
Generate
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full md:w-1/2 space-y-4">
|
||||
<ShellResult />
|
||||
<div className="mt-4">
|
||||
<div className="px-4">
|
||||
{isPending && (
|
||||
<Alert>
|
||||
<LoaderCircle className="animate-spin h-4 w-4" />
|
||||
<AlertTitle>Pending</AlertTitle>
|
||||
<AlertDescription>正在全力搜索可用的后端服务~</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
{isError && (
|
||||
<Alert variant="destructive">
|
||||
<ServerOffIcon className="h-4 w-4" />
|
||||
<AlertTitle>Not Work!</AlertTitle>
|
||||
<AlertDescription>未检测到可用后端服务,生成功能暂不可用.</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{data && (
|
||||
<Alert>
|
||||
<ActivityIcon className="h-4 w-4" />
|
||||
<AlertTitle>It Work!</AlertTitle>
|
||||
<AlertDescription>Let's start the party! 🎉</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col xl:flex-row gap-4 p-4">
|
||||
<div className="w-full xl:w-1/2 space-y-2">
|
||||
<MainConfigCard servers={data?.servers} mainConfig={data?.core} form={form} />
|
||||
<PackageConfigCard packerConfig={data?.packers} form={form} />
|
||||
<Button className="w-full" type="submit" disabled={isActionPending}>
|
||||
{isActionPending ? <LoaderCircle className="animate-spin" /> : <WandSparklesIcon />}
|
||||
Generate
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full xl:w-1/2 space-y-4">
|
||||
<ShellResult packMethod={packMethod} generateResult={generateResult} packResult={packResult} />
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const formSchema = z.object({
|
||||
server: z.string().min(1),
|
||||
targetJdkVersion: z.optional(z.string()),
|
||||
debug: z.optional(z.boolean()),
|
||||
bypassJavaModule: z.optional(z.boolean()),
|
||||
shellClassName: z.string().optional(),
|
||||
shellTool: z.string().min(1),
|
||||
shellType: z.string().min(1),
|
||||
urlPattern: z.optional(z.string()),
|
||||
godzillaPass: z.optional(z.string()),
|
||||
godzillaKey: z.optional(z.string()),
|
||||
godzillaHeaderName: z.optional(z.string()),
|
||||
godzillaHeaderValue: z.optional(z.string()),
|
||||
commandParamName: z.optional(z.string()),
|
||||
injectorClassName: z.optional(z.string()),
|
||||
packingMethod: z.string().min(1, { message: "请选择打包方式" }),
|
||||
});
|
||||
|
||||
export type FormSchema = z.infer<typeof formSchema>;
|
||||
@@ -0,0 +1,73 @@
|
||||
export interface ShellConfig {
|
||||
server: string;
|
||||
shellTool: string;
|
||||
shellType: string;
|
||||
targetJreVersion?: string;
|
||||
debug?: boolean;
|
||||
byPassJavaModule?: boolean;
|
||||
obfuscate?: boolean;
|
||||
}
|
||||
|
||||
export interface ShellToolConfig {
|
||||
shellClassName?: string;
|
||||
godzillaPass?: string;
|
||||
godzillaKey?: string;
|
||||
godzillaHeaderName?: string;
|
||||
godzillaHeaderValue?: string;
|
||||
commandParamName?: string;
|
||||
}
|
||||
|
||||
export interface CommandShellToolConfig {
|
||||
shellClassName?: string;
|
||||
paramName?: string;
|
||||
}
|
||||
|
||||
export interface GodzillaShellToolConfig {
|
||||
shellClassName?: string;
|
||||
pass?: string;
|
||||
key?: string;
|
||||
headerName?: string;
|
||||
headerValue?: string;
|
||||
}
|
||||
|
||||
export interface InjectorConfig {
|
||||
className?: string;
|
||||
urlPattern?: string;
|
||||
}
|
||||
|
||||
export interface ConfigResponseType {
|
||||
servers: string[];
|
||||
core: MainConfig;
|
||||
packers: PackerConfig;
|
||||
}
|
||||
|
||||
export interface MainConfig {
|
||||
[serverName: string]: {
|
||||
[toolName: string]: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface PackerConfig {
|
||||
[packerName: string]: string;
|
||||
}
|
||||
|
||||
export interface GenerateResponse {
|
||||
packResult: string;
|
||||
generateResult: GenerateResult;
|
||||
}
|
||||
|
||||
export interface APIErrorResponse {
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface GenerateResult {
|
||||
shellClassName: string;
|
||||
shellSize: number;
|
||||
shellBytesBase64Str: string;
|
||||
injectorClassName: string;
|
||||
injectorSize: number;
|
||||
injectorBytesBase64Str: string;
|
||||
shellConfig: ShellConfig;
|
||||
shellToolConfig: CommandShellToolConfig | GodzillaShellToolConfig;
|
||||
injectorConfig: InjectorConfig;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { FormSchema } from "@/types/schema.ts";
|
||||
import { InjectorConfig, ShellConfig, ShellToolConfig } from "@/types/shell.ts";
|
||||
|
||||
export function transformToPostData(formValue: FormSchema) {
|
||||
const shellConfig: ShellConfig = {
|
||||
server: formValue.server,
|
||||
shellTool: formValue.shellTool,
|
||||
shellType: formValue.shellType,
|
||||
targetJreVersion: formValue.targetJdkVersion,
|
||||
byPassJavaModule: formValue.bypassJavaModule,
|
||||
};
|
||||
const shellToolConfig: ShellToolConfig = {
|
||||
shellClassName: formValue.shellClassName,
|
||||
godzillaPass: formValue.godzillaPass,
|
||||
godzillaKey: formValue.godzillaKey,
|
||||
godzillaHeaderName: formValue.godzillaHeaderName,
|
||||
godzillaHeaderValue: formValue.godzillaHeaderValue,
|
||||
commandParamName: formValue.commandParamName,
|
||||
};
|
||||
|
||||
const injectorConfig: InjectorConfig = {
|
||||
urlPattern: formValue.urlPattern,
|
||||
className: formValue.injectorClassName,
|
||||
};
|
||||
return {
|
||||
shellConfig,
|
||||
shellToolConfig,
|
||||
injectorConfig,
|
||||
packer: formValue.packingMethod,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"root":["./src/config.ts","./src/main.tsx","./src/routetree.gen.ts","./src/components/code-viewer.tsx","./src/components/main-config-card.tsx","./src/components/mode-toggle.tsx","./src/components/package-config-card.tsx","./src/components/shell-result.tsx","./src/components/tailwind-indicator.tsx","./src/components/theme-provider.tsx","./src/components/tips/jre-tip.tsx","./src/components/tips/url-pattern-tip.tsx","./src/components/ui/alert.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/form.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/radio-group.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/lib/utils.ts","./src/routes/__root.tsx","./src/routes/index.tsx","./src/types/schema.ts","./src/types/shell.ts","./src/utils/transformer.ts"],"version":"5.7.2"}
|
||||
@@ -0,0 +1 @@
|
||||
{"root":["./vite.config.ts"],"version":"5.7.2"}
|
||||
Reference in New Issue
Block a user