mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 07:00:43 +08:00
feat: support multi payload generate
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { Button, ButtonProps } from "@/components/ui/button.tsx";
|
||||
import { cn } from "@/lib/utils.ts";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { HTMLProps, useEffect, useState } from "react";
|
||||
import { HTMLProps, ReactNode, useEffect, useState } from "react";
|
||||
import { CopyToClipboard } from "react-copy-to-clipboard";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { materialDark } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
@@ -31,15 +30,8 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
|
||||
toast.success("复制成功", { duration: 1000 });
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
size="icon"
|
||||
type="button"
|
||||
variant={variant}
|
||||
className={cn("relative z-10 h-8 w-8 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50", className)}
|
||||
{...props}
|
||||
>
|
||||
<span className="sr-only">Copy</span>
|
||||
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
<Button variant="ghost" size="icon" type="button" className="h-7 w-7 [&_svg]:h-4 [&_svg]:w-4">
|
||||
{hasCopied ? <Check /> : <Copy />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
);
|
||||
@@ -47,41 +39,57 @@ export function CopyButton({ value, className, src, variant = "ghost", ...props
|
||||
|
||||
export function CodeViewer({
|
||||
code,
|
||||
header,
|
||||
language,
|
||||
height,
|
||||
showLineNumbers = true,
|
||||
wrapLongLines = false,
|
||||
height = 500,
|
||||
}: {
|
||||
code: string;
|
||||
language: string;
|
||||
showLineNumbers?: boolean;
|
||||
wrapLongLines?: boolean;
|
||||
height?: number;
|
||||
}) {
|
||||
wrapLongLines = true,
|
||||
}: CodeViewerProps) {
|
||||
const lineProps: lineTagPropsFunction | HTMLProps<HTMLElement> | undefined = wrapLongLines
|
||||
? { style: { overflowWrap: "break-word", whiteSpace: "pre-wrap" } }
|
||||
: undefined;
|
||||
return (
|
||||
<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: height,
|
||||
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
|
||||
overflowWrap: wrapLongLines ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
<div className="rounded-lg border">
|
||||
{header && (
|
||||
<div className="flex items-center justify-between border-b p-2">
|
||||
{header}
|
||||
<CopyButton value={code} variant="ghost" size="sm" />
|
||||
</div>
|
||||
)}
|
||||
{!header && (
|
||||
<div className="flex items-center justify-end border-b p-2">
|
||||
<CopyButton value={code} variant="ghost" size="sm" />
|
||||
</div>
|
||||
)}
|
||||
<div className="relative overflow-hidden text-xs wrap-all">
|
||||
<SyntaxHighlighter
|
||||
language={language}
|
||||
style={materialDark}
|
||||
showLineNumbers={showLineNumbers}
|
||||
wrapLongLines={wrapLongLines}
|
||||
lineProps={lineProps}
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
padding: showLineNumbers ? 0 : "1em 1em",
|
||||
borderRadius: "0 0 var(--radius) var(--radius)",
|
||||
height: height,
|
||||
whiteSpace: wrapLongLines ? "pre-wrap" : "pre",
|
||||
overflowWrap: wrapLongLines ? "normal" : "break-word",
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface CodeViewerProps {
|
||||
code: string;
|
||||
language: string;
|
||||
header?: ReactNode;
|
||||
height?: string | number;
|
||||
showLineNumbers?: boolean;
|
||||
wrapLongLines?: boolean;
|
||||
lineProps?: (lineNumber: number) => React.HTMLProps<HTMLElement>;
|
||||
}
|
||||
|
||||
@@ -12,19 +12,17 @@ interface CopyableFieldProps {
|
||||
}
|
||||
|
||||
export function CopyableField({ label, value, text }: CopyableFieldProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [hasCopied, setHasCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = useCallback(() => {
|
||||
if (typeof value === "string") {
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
setCopied(true);
|
||||
toast.success(`复制${label}成功`, {
|
||||
duration: 1000,
|
||||
});
|
||||
setTimeout(() => setCopied(false), 1000);
|
||||
});
|
||||
}
|
||||
}, [value, label]);
|
||||
setHasCopied(true);
|
||||
toast.success(`复制${label}成功`, {
|
||||
duration: 1000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
setHasCopied(false);
|
||||
}, 1000);
|
||||
}, [label]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between h-9">
|
||||
@@ -35,7 +33,7 @@ export function CopyableField({ label, value, text }: CopyableFieldProps) {
|
||||
{value && (
|
||||
<CopyToClipboard text={value as string} onCopy={copyToClipboard}>
|
||||
<Button variant="ghost" size="icon" type="button">
|
||||
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
)}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert.tsx";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Separator } from "@/components/ui/separator.tsx";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs.tsx";
|
||||
import { downloadBytes } from "@/lib/utils.ts";
|
||||
@@ -24,8 +25,9 @@ import {
|
||||
GenerateResult,
|
||||
GodzillaShellToolConfig,
|
||||
} from "@/types/shell.ts";
|
||||
import { TFunction } from "i18next";
|
||||
import { CircleHelpIcon, TriangleAlertIcon } from "lucide-react";
|
||||
import { Fragment } from "react";
|
||||
import { Fragment, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@@ -219,14 +221,111 @@ function BasicInfo({ generateResult }: { generateResult?: GenerateResult }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function ShellResult({
|
||||
packResult,
|
||||
function MultiPackResult({
|
||||
allPackResults,
|
||||
packMethod,
|
||||
generateResult,
|
||||
}: { packResult: string; packMethod: string; generateResult?: GenerateResult }) {
|
||||
t,
|
||||
}: {
|
||||
allPackResults: object | undefined;
|
||||
packMethod: string;
|
||||
t: TFunction;
|
||||
}) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const packMethods = Object.keys(allPackResults ?? {});
|
||||
const [selectedMethod, setSelectedMethod] = useState(packMethods[0]);
|
||||
const [packResult, setPackResult] = useState(allPackResults?.[selectedMethod as keyof typeof allPackResults] ?? "");
|
||||
return (
|
||||
<Fragment>
|
||||
<CodeViewer
|
||||
code={packResult ?? ""}
|
||||
header={
|
||||
<div className="flex items-center justify-between text-xs gap-2">
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
setSelectedMethod(value);
|
||||
setPackResult(allPackResults?.[value as keyof typeof allPackResults] ?? "");
|
||||
}}
|
||||
value={selectedMethod}
|
||||
>
|
||||
<SelectTrigger className="h-7 text-xs [&_svg]:h-4 [&_svg]:w-4">
|
||||
<span className="text-muted-foreground">{t("packageConfig.title")}: </span>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{packMethods.map((method) => (
|
||||
<SelectItem key={method} value={method} className="text-xs">
|
||||
{method}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={350}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function renderResultComponent(
|
||||
packResult: string | undefined,
|
||||
allPackResults: Map<string, string> | undefined,
|
||||
packMethod: string,
|
||||
t: TFunction,
|
||||
generateResult?: GenerateResult,
|
||||
) {
|
||||
const showCode = packMethod === "JSP";
|
||||
const isAgent = packMethod.startsWith("Agent");
|
||||
const isJar = packMethod === "Jar";
|
||||
if (allPackResults) {
|
||||
return <MultiPackResult allPackResults={allPackResults} packMethod={packMethod} t={t} />;
|
||||
}
|
||||
|
||||
if (isAgent) {
|
||||
return <AgentResult packResult={packResult ?? ""} generateResult={generateResult} />;
|
||||
}
|
||||
if (isJar) {
|
||||
return <JarResult packResult={packResult ?? ""} generateResult={generateResult} />;
|
||||
}
|
||||
if (!isAgent && !isJar) {
|
||||
return (
|
||||
<Fragment>
|
||||
<CodeViewer
|
||||
code={packResult ?? ""}
|
||||
header={
|
||||
<div className="flex items-center justify-between text-xs gap-2">
|
||||
<span>
|
||||
{t("packageConfig.title")}:{packMethod}
|
||||
</span>
|
||||
<span className="text-muted-foreground">({packResult?.length})</span>
|
||||
</div>
|
||||
}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={350}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ShellResult({
|
||||
packResult,
|
||||
allPackResults,
|
||||
packMethod,
|
||||
generateResult,
|
||||
}: {
|
||||
packResult: string | undefined;
|
||||
allPackResults: Map<string, string> | undefined;
|
||||
packMethod: string;
|
||||
generateResult?: GenerateResult;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -241,20 +340,7 @@ export function ShellResult({
|
||||
<div className="mb-4">
|
||||
<BasicInfo generateResult={generateResult} />
|
||||
</div>
|
||||
{!isAgent && !isJar && (
|
||||
<Fragment>
|
||||
<div className="flex items-center justify-end text-sm text-muted-foreground">({packResult.length})</div>
|
||||
<CodeViewer
|
||||
code={packResult}
|
||||
wrapLongLines={!showCode}
|
||||
showLineNumbers={showCode}
|
||||
language={showCode ? "java" : "text"}
|
||||
height={400}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{isAgent && <AgentResult packResult={packResult} generateResult={generateResult} />}
|
||||
{isJar && <JarResult packResult={packResult} generateResult={generateResult} />}
|
||||
{renderResultComponent(packResult, allPackResults, packMethod, t, generateResult)}
|
||||
</TabsContent>
|
||||
<TabsContent value="shell" className="mt-4">
|
||||
<Alert>
|
||||
@@ -281,7 +367,9 @@ export function ShellResult({
|
||||
</div>
|
||||
<CodeViewer
|
||||
showLineNumbers={false}
|
||||
header={<div className="text-xs">{generateResult?.shellClassName}</div>}
|
||||
wrapLongLines={true}
|
||||
height={600}
|
||||
code={generateResult?.shellBytesBase64Str ?? ""}
|
||||
language="text"
|
||||
/>
|
||||
@@ -312,6 +400,8 @@ export function ShellResult({
|
||||
<CodeViewer
|
||||
showLineNumbers={false}
|
||||
wrapLongLines={true}
|
||||
header={<div className="text-xs">{generateResult?.injectorClassName}</div>}
|
||||
height={600}
|
||||
code={generateResult?.injectorBytesBase64Str ?? ""}
|
||||
language="text"
|
||||
/>
|
||||
|
||||
@@ -60,7 +60,7 @@ export const resources = {
|
||||
AgentJar: "AgentJar",
|
||||
Deserialize: "Deserialize(Only CB4, 1.9.x)",
|
||||
ScriptEngine: "ScriptEngine",
|
||||
XxlJob: "XXL-JOB Executor"
|
||||
XxlJob: "XXL-JOB Executor",
|
||||
},
|
||||
},
|
||||
tips: {
|
||||
|
||||
@@ -53,7 +53,8 @@ function IndexComponent() {
|
||||
},
|
||||
});
|
||||
|
||||
const [packResult, setPackResult] = useState<string>("");
|
||||
const [packResult, setPackResult] = useState<string | undefined>();
|
||||
const [allPackResults, setAllPackResults] = useState<Map<string, string> | undefined>();
|
||||
const [generateResult, setGenerateResult] = useState<GenerateResult>();
|
||||
const [packMethod, setPackMethod] = useState<string>("");
|
||||
const [isActionPending, startTransition] = useTransition();
|
||||
@@ -98,6 +99,7 @@ function IndexComponent() {
|
||||
if (response.ok) {
|
||||
const json: GenerateResponse = await response.json();
|
||||
setPackResult(json.packResult);
|
||||
setAllPackResults(json.allPackResults);
|
||||
setGenerateResult(json.generateResult);
|
||||
setPackMethod(values.packingMethod);
|
||||
toast.success(t("success.generated"));
|
||||
@@ -124,7 +126,12 @@ function IndexComponent() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full xl:w-1/2 space-y-4">
|
||||
<ShellResult packMethod={packMethod} generateResult={generateResult} packResult={packResult} />
|
||||
<ShellResult
|
||||
packMethod={packMethod}
|
||||
generateResult={generateResult}
|
||||
packResult={packResult}
|
||||
allPackResults={allPackResults}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
@@ -61,8 +61,9 @@ export interface MainConfig {
|
||||
export type PackerConfig = Array<string>;
|
||||
|
||||
export interface GenerateResponse {
|
||||
packResult: string;
|
||||
generateResult: GenerateResult;
|
||||
packResult?: string;
|
||||
allPackResults?: Map<string, string>;
|
||||
}
|
||||
|
||||
export interface APIErrorResponse {
|
||||
|
||||
Reference in New Issue
Block a user