Compare commits
No commits in common. "3ad2f762e0f057f5adc8e07361db382af0e0d269" and "898a170d7b9ff403e5f18be6ae24d583fd306aea" have entirely different histories.
3ad2f762e0
...
898a170d7b
|
|
@ -38,5 +38,3 @@ build/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/.idea/
|
/.idea/
|
||||||
/.mvn/
|
/.mvn/
|
||||||
xcp.conf
|
|
||||||
xcpdata.mv.db
|
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>ru.kirillius</groupId>
|
|
||||||
<artifactId>XCP</artifactId>
|
|
||||||
<version>1.0.0.0</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>api-generator</artifactId>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>ru.kirillius</groupId>
|
|
||||||
<artifactId>rpc</artifactId>
|
|
||||||
<version>1.0.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
|
||||||
<version>3.1.0</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>generate-js-client</id>
|
|
||||||
<phase>generate-sources</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>java</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>ru.kirillius.XCP.ApiGenerator.JavascriptClientGenerator</mainClass>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
package ru.kirillius.XCP.ApiGenerator;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class JavascriptClientGenerator {
|
|
||||||
|
|
||||||
private static final String SERVICES_PACKAGE = "ru.kirillius.XCP.RPC.Services";
|
|
||||||
private static final String OUTPUT_DIR = "target/generated-sources/api.js";
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
try {
|
|
||||||
generateJavascriptClient();
|
|
||||||
System.out.println("JavaScript API client generated successfully at: " + OUTPUT_DIR);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Failed to generate JavaScript client: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void generateJavascriptClient() throws IOException, ClassNotFoundException {
|
|
||||||
StringBuilder jsBuilder = new StringBuilder();
|
|
||||||
jsBuilder.append("API = {\n");
|
|
||||||
|
|
||||||
List<Class<?>> serviceClasses = findServiceClasses();
|
|
||||||
boolean first = true;
|
|
||||||
|
|
||||||
for (Class<?> serviceClass : serviceClasses) {
|
|
||||||
if (!first) {
|
|
||||||
jsBuilder.append(",\n");
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
String className = serviceClass.getSimpleName();
|
|
||||||
jsBuilder.append(" ").append(className).append(" : {\n");
|
|
||||||
|
|
||||||
Method[] methods = serviceClass.getDeclaredMethods();
|
|
||||||
List<Method> rpcMethods = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Method method : methods) {
|
|
||||||
Annotation[] annotations = method.getAnnotations();
|
|
||||||
for (Annotation annotation : annotations) {
|
|
||||||
if (annotation.annotationType().getSimpleName().equals("JsonRpcMethod")) {
|
|
||||||
rpcMethods.add(method);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < rpcMethods.size(); i++) {
|
|
||||||
Method method = rpcMethods.get(i);
|
|
||||||
Annotation jsonRpcMethod = null;
|
|
||||||
|
|
||||||
for (Annotation annotation : method.getAnnotations()) {
|
|
||||||
if (annotation.annotationType().getSimpleName().equals("JsonRpcMethod")) {
|
|
||||||
jsonRpcMethod = annotation;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateMethod(jsBuilder, method, jsonRpcMethod, className);
|
|
||||||
|
|
||||||
if (i < rpcMethods.size() - 1) {
|
|
||||||
jsBuilder.append(",\n");
|
|
||||||
} else {
|
|
||||||
jsBuilder.append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsBuilder.append(" }");
|
|
||||||
}
|
|
||||||
|
|
||||||
jsBuilder.append("\n}\n");
|
|
||||||
|
|
||||||
File outputFile = new File(OUTPUT_DIR);
|
|
||||||
outputFile.getParentFile().mkdirs();
|
|
||||||
|
|
||||||
try (FileWriter writer = new FileWriter(outputFile)) {
|
|
||||||
writer.write(jsBuilder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Class<?>> findServiceClasses() throws ClassNotFoundException {
|
|
||||||
List<Class<?>> serviceClasses = new ArrayList<>();
|
|
||||||
|
|
||||||
serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".Auth"));
|
|
||||||
serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".Profile"));
|
|
||||||
serviceClasses.add(Class.forName(SERVICES_PACKAGE + ".UserManagement"));
|
|
||||||
|
|
||||||
return serviceClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void generateMethod(StringBuilder builder, Method method, Annotation jsonRpcMethod, String className) {
|
|
||||||
String methodName = method.getName();
|
|
||||||
|
|
||||||
builder.append(" /**\n");
|
|
||||||
|
|
||||||
try {
|
|
||||||
Object description = jsonRpcMethod.annotationType().getMethod("description").invoke(jsonRpcMethod);
|
|
||||||
builder.append(" * ").append(description).append("\n");
|
|
||||||
|
|
||||||
Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod);
|
|
||||||
if (parameters instanceof Object[]) {
|
|
||||||
Object[] params = (Object[]) parameters;
|
|
||||||
for (Object param : params) {
|
|
||||||
Class<?> paramClass = param.getClass();
|
|
||||||
Object name = paramClass.getMethod("name").invoke(param);
|
|
||||||
Object optional = paramClass.getMethod("optional").invoke(param);
|
|
||||||
|
|
||||||
builder.append(" * @param ").append(name);
|
|
||||||
if (!(Boolean) optional) {
|
|
||||||
builder.append(" (required)");
|
|
||||||
} else {
|
|
||||||
builder.append(" (optional)");
|
|
||||||
}
|
|
||||||
builder.append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object returnType = jsonRpcMethod.annotationType().getMethod("returnType").invoke(jsonRpcMethod);
|
|
||||||
String returnTypeName = getSimpleTypeName((Class<?>) returnType);
|
|
||||||
builder.append(" * @returns ").append(returnTypeName).append("\n");
|
|
||||||
} catch (Exception e) {
|
|
||||||
builder.append(" * Method: ").append(methodName).append("\n");
|
|
||||||
builder.append(" * @returns unknown\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(" */\n");
|
|
||||||
|
|
||||||
builder.append(" ").append(methodName).append(" : async function(");
|
|
||||||
|
|
||||||
List<String> paramNames = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod);
|
|
||||||
if (parameters instanceof Object[]) {
|
|
||||||
Object[] params = (Object[]) parameters;
|
|
||||||
for (Object param : params) {
|
|
||||||
Object name = param.getClass().getMethod("name").invoke(param);
|
|
||||||
Object optional = param.getClass().getMethod("optional").invoke(param);
|
|
||||||
if (!(Boolean) optional) {
|
|
||||||
paramNames.add(name.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// No parameters available
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(String.join(", ", paramNames));
|
|
||||||
builder.append(") {\n");
|
|
||||||
builder.append(" return await JSONRPC.invoke(\"").append(className).append(".").append(methodName).append("\", {");
|
|
||||||
|
|
||||||
List<String> paramPairs = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
Object parameters = jsonRpcMethod.annotationType().getMethod("parameters").invoke(jsonRpcMethod);
|
|
||||||
if (parameters instanceof Object[]) {
|
|
||||||
Object[] params = (Object[]) parameters;
|
|
||||||
for (Object param : params) {
|
|
||||||
Object name = param.getClass().getMethod("name").invoke(param);
|
|
||||||
Object optional = param.getClass().getMethod("optional").invoke(param);
|
|
||||||
if (!(Boolean) optional) {
|
|
||||||
paramPairs.add(name.toString() + ":" + name.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// No parameters available
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(String.join(", ", paramPairs));
|
|
||||||
builder.append("});\n");
|
|
||||||
builder.append(" }");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getSimpleTypeName(Class<?> type) {
|
|
||||||
if (type == null) return "unknown";
|
|
||||||
if (type == boolean.class) return "boolean";
|
|
||||||
if (type == int.class || type == long.class) return "number";
|
|
||||||
if (type == String.class) return "string";
|
|
||||||
if (type == void.class || type == Void.class) return "void";
|
|
||||||
|
|
||||||
String typeName = type.getSimpleName();
|
|
||||||
if (type.isArray()) {
|
|
||||||
return "Array";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeName.contains("Node") || typeName.contains("Object")) {
|
|
||||||
return "Object";
|
|
||||||
}
|
|
||||||
|
|
||||||
return typeName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,6 @@ import ru.kirillius.XCP.Services.ServiceLoadPriority;
|
||||||
import ru.kirillius.XCP.Services.WebService;
|
import ru.kirillius.XCP.Services.WebService;
|
||||||
import ru.kirillius.XCP.web.WebServiceImpl;
|
import ru.kirillius.XCP.web.WebServiceImpl;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -40,44 +39,23 @@ public class Application implements Context {
|
||||||
private final ConfigManager configManager;
|
private final ConfigManager configManager;
|
||||||
private final Map<Class<? extends Service>, Service> services = new ConcurrentHashMap<>();
|
private final Map<Class<? extends Service>, Service> services = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private Config loadConfig() {
|
|
||||||
var configFile = configManager.getConfigFile().getAbsolutePath();
|
|
||||||
|
|
||||||
try {
|
|
||||||
configFile = configManager.getConfigFile().getCanonicalPath();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warning("Unable to determine real path of file " + configFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
Config config;
|
|
||||||
if (configManager.isExist()) {
|
|
||||||
try {
|
|
||||||
config = configManager.load();
|
|
||||||
log.info("Loaded config file: " + configFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Error loading config file " + configFile, e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.warning("Unable to find config file " + configFile + ". Using default values.");
|
|
||||||
config = configManager.create();
|
|
||||||
log.info("Saving default config file to " + configFile);
|
|
||||||
try {
|
|
||||||
configManager.save(config);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Unable to save config file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Application(String[] args) {
|
public Application(String[] args) {
|
||||||
launchArgs = Arrays.stream(args).toList();
|
launchArgs = Arrays.stream(args).toList();
|
||||||
loggingSystem = new LoggingSystemImpl(this);
|
loggingSystem = new LoggingSystemImpl(this);
|
||||||
log = loggingSystem.createLogger(Application.class);
|
log = loggingSystem.createLogger(Application.class);
|
||||||
configManager = new ConfigManagerImpl(this);
|
configManager = new ConfigManagerImpl(this);
|
||||||
config = loadConfig();
|
if (configManager.isExist()) {
|
||||||
|
try {
|
||||||
|
config = configManager.load();
|
||||||
|
log.info("Loaded config file: " + configManager.getConfigFile().getAbsolutePath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error loading config file " + configManager.getConfigFile(), e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warning("Unable to find config file " + configManager.getConfigFile().getAbsolutePath() + ". Using default values.");
|
||||||
|
config = configManager.create();
|
||||||
|
}
|
||||||
securityManager = new SecurityManagerImpl();
|
securityManager = new SecurityManagerImpl();
|
||||||
try {
|
try {
|
||||||
loadServices();
|
loadServices();
|
||||||
|
|
@ -98,18 +76,17 @@ public class Application implements Context {
|
||||||
var order = aClass.getAnnotation(ServiceLoadPriority.class);
|
var order = aClass.getAnnotation(ServiceLoadPriority.class);
|
||||||
return order == null ? 100000 : order.value();
|
return order == null ? 100000 : order.value();
|
||||||
})).forEach(aClass -> {
|
})).forEach(aClass -> {
|
||||||
@SuppressWarnings("unchecked") var facade = (Class<? extends Service>) Arrays.stream(aClass.getInterfaces())
|
log.info("Loading service " + aClass.getSimpleName());
|
||||||
.filter(Service.class::isAssignableFrom)
|
|
||||||
.findFirst().
|
|
||||||
orElseThrow(() -> new ClassCastException("Unable to get service interface from class " + aClass.getSimpleName()));
|
|
||||||
log.info("Loading service " + facade.getSimpleName());
|
|
||||||
try {
|
try {
|
||||||
var constructor = aClass.getConstructor();
|
var constructor = aClass.getConstructor();
|
||||||
try {
|
try {
|
||||||
var service = constructor.newInstance();
|
var service = constructor.newInstance();
|
||||||
try {
|
try {
|
||||||
service.initialize(this);
|
service.initialize(this);
|
||||||
|
@SuppressWarnings("unchecked") var facade = (Class<? extends Service>) Arrays.stream(aClass.getInterfaces())
|
||||||
|
.filter(Service.class::isAssignableFrom)
|
||||||
|
.findFirst().
|
||||||
|
orElseThrow(() -> new ClassCastException("Unable to get service interface from class " + aClass.getSimpleName()));
|
||||||
services.put(facade, service);
|
services.put(facade, service);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -117,14 +94,14 @@ public class Application implements Context {
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
e.addSuppressed(ex);
|
e.addSuppressed(ex);
|
||||||
}
|
}
|
||||||
throw new RuntimeException("Failed to start " + facade.getSimpleName(), e);
|
throw new RuntimeException("Failed to start service " + aClass.getSimpleName(), e);
|
||||||
}
|
}
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
throw new RuntimeException("Failed to instantiate " + facade.getSimpleName(), e);
|
throw new RuntimeException("Failed to instantiate service " + aClass.getSimpleName(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new RuntimeException("Failed to find default constructor of " + facade.getSimpleName(), e);
|
throw new RuntimeException("Failed to find default constructor of service " + aClass.getSimpleName(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import ru.kirillius.XCP.Commons.Context;
|
||||||
import tools.jackson.databind.ObjectMapper;
|
import tools.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public final class ConfigManagerImpl implements ConfigManager {
|
public final class ConfigManagerImpl implements ConfigManager {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
@ -25,11 +24,7 @@ public final class ConfigManagerImpl implements ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private File findConfigFile() {
|
private File findConfigFile() {
|
||||||
return new File(context.getLaunchArgs().stream()
|
return new File(context.getLaunchArgs().stream().filter(a -> a.startsWith("--config=")).findFirst().orElse(DEFAULT_CONFIG_PATH));
|
||||||
.filter(a -> a.startsWith("--config="))
|
|
||||||
.findFirst()
|
|
||||||
.map(a -> a.split(Pattern.quote("="))[1])
|
|
||||||
.orElse(DEFAULT_CONFIG_PATH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
|
@ -57,7 +52,7 @@ public final class ConfigManagerImpl implements ConfigManager {
|
||||||
@Override
|
@Override
|
||||||
public void save(Config config) throws IOException {
|
public void save(Config config) throws IOException {
|
||||||
try (var stream = new FileOutputStream(configFile)) {
|
try (var stream = new FileOutputStream(configFile)) {
|
||||||
mapper.writerWithDefaultPrettyPrinter().writeValue(stream, config);
|
mapper.writeValue(stream, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +70,7 @@ public final class ConfigManagerImpl implements ConfigManager {
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private File databaseFile = new File(DEFAULT_DB_PATH);
|
private File databaseFile = new File(DEFAULT_CONFIG_PATH);
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@
|
||||||
<artifactId>database</artifactId>
|
<artifactId>database</artifactId>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Source: https://mvnrepository.com/artifact/com.h2database/h2 -->
|
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.h2database</groupId>
|
<groupId>com.h2database</groupId>
|
||||||
<artifactId>h2</artifactId>
|
<artifactId>h2</artifactId>
|
||||||
<version>2.4.240</version>
|
<version>2.1.214</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core -->
|
<!-- https://mvnrepository.com/artifact/org.hibernate.orm/hibernate-core -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class LogHandlerImpl extends Handler {
|
||||||
builder.append(logRecord.getMessage().trim());
|
builder.append(logRecord.getMessage().trim());
|
||||||
var thrown = logRecord.getThrown();
|
var thrown = logRecord.getThrown();
|
||||||
if (thrown != null) {
|
if (thrown != null) {
|
||||||
builder.append("\n\tThrown ").append(thrown.getClass().getSimpleName()).append(": ").append(thrown.getMessage());
|
builder.append("\nError thrown ").append(thrown.getClass().getSimpleName()).append(":").append(thrown.getMessage());
|
||||||
if (debugging) {
|
if (debugging) {
|
||||||
builder.append("\nStack trace:\n");
|
builder.append("\nStack trace:\n");
|
||||||
|
|
||||||
|
|
@ -47,13 +47,6 @@ public class LogHandlerImpl extends Handler {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
var cause = thrown.getCause();
|
|
||||||
while (cause != null) {
|
|
||||||
builder.append("\n\t\tCaused by ").append(cause.getClass().getSimpleName()).append(": ").append(cause.getMessage());
|
|
||||||
cause = cause.getCause();
|
|
||||||
}
|
|
||||||
builder.append("\n\t(Stack trace is hidden due to --debug flag)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,6 @@ public class LoggerImpl extends java.util.logging.Logger implements Logger {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(Throwable error) {
|
public void error(Throwable error) {
|
||||||
log(Level.SEVERE, getName() + SEPARATOR + "Thrown unhandled exception", error);
|
log(Level.SEVERE, getName() + SEPARATOR + "Thrown error", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
pom.xml
2
pom.xml
|
|
@ -10,14 +10,12 @@
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<modules>
|
<modules>
|
||||||
<module>api</module>
|
<module>api</module>
|
||||||
<module>api-generator</module>
|
|
||||||
<module>database</module>
|
<module>database</module>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>rpc</module>
|
<module>rpc</module>
|
||||||
<module>web-server</module>
|
<module>web-server</module>
|
||||||
<module>app</module>
|
<module>app</module>
|
||||||
<module>logging</module>
|
<module>logging</module>
|
||||||
<module>web-ui</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,4 @@ import java.lang.annotation.Target;
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface JsonRpcMethod {
|
public @interface JsonRpcMethod {
|
||||||
UserRole accessLevel() default UserRole.Admin;
|
UserRole accessLevel() default UserRole.Admin;
|
||||||
|
|
||||||
String description() default "";
|
|
||||||
|
|
||||||
Parameter[] parameters() default {};
|
|
||||||
|
|
||||||
Class<?> returnType() default Void.class;
|
|
||||||
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@interface Parameter {
|
|
||||||
String name();
|
|
||||||
|
|
||||||
String description() default "";
|
|
||||||
|
|
||||||
Class<?> type() default Void.class;
|
|
||||||
|
|
||||||
boolean optional() default false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -133,38 +133,8 @@ public class JsonRpcServlet extends HttpServlet {
|
||||||
mapper.writeValue(httpServletResponse.getWriter(), jsonRpcResponse);
|
mapper.writeValue(httpServletResponse.getWriter(), jsonRpcResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
|
||||||
mapper.writeValue(resp.getWriter(), createErrorResponse(null, JsonRpcErrorCode.INVALID_REQUEST));
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsonRpcResponse createErrorResponse(JsonRpcRequest request, JsonRpcErrorCode code) {
|
private JsonRpcResponse createErrorResponse(JsonRpcRequest request, JsonRpcErrorCode code) {
|
||||||
return JsonRpcResponse.builder().jsonrpc("2.0")
|
return JsonRpcResponse.builder()
|
||||||
.id(request == null ? -1L : request.getId())
|
.id(request == null ? -1L : request.getId())
|
||||||
.error(new JsonRpcError(code))
|
.error(new JsonRpcError(code))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
|
|
@ -21,23 +21,7 @@ import java.util.UUID;
|
||||||
|
|
||||||
public class Auth extends JsonRpcService {
|
public class Auth extends JsonRpcService {
|
||||||
|
|
||||||
@JsonRpcMethod(
|
@JsonRpcMethod(accessLevel = UserRole.Guest)
|
||||||
accessLevel = UserRole.Guest,
|
|
||||||
description = "Authenticates a user using login and password. Returns true if authentication is successful.",
|
|
||||||
parameters = {
|
|
||||||
@JsonRpcMethod.Parameter(
|
|
||||||
name = "login",
|
|
||||||
description = "User's login name",
|
|
||||||
type = String.class
|
|
||||||
),
|
|
||||||
@JsonRpcMethod.Parameter(
|
|
||||||
name = "password",
|
|
||||||
description = "User's password",
|
|
||||||
type = String.class
|
|
||||||
)
|
|
||||||
},
|
|
||||||
returnType = boolean.class
|
|
||||||
)
|
|
||||||
public boolean authenticateByPassword(CallContext call) {
|
public boolean authenticateByPassword(CallContext call) {
|
||||||
var login = requireParam(call, "login", JsonNode::asString);
|
var login = requireParam(call, "login", JsonNode::asString);
|
||||||
var passwd = requireParam(call, "password", JsonNode::asString);
|
var passwd = requireParam(call, "password", JsonNode::asString);
|
||||||
|
|
@ -57,11 +41,7 @@ public class Auth extends JsonRpcService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@JsonRpcMethod(
|
@JsonRpcMethod(accessLevel = UserRole.User)
|
||||||
accessLevel = UserRole.User,
|
|
||||||
description = "Retrieves all API tokens associated with the current user",
|
|
||||||
returnType = ArrayNode.class
|
|
||||||
)
|
|
||||||
public ArrayNode getTokens(CallContext call) throws IOException {
|
public ArrayNode getTokens(CallContext call) throws IOException {
|
||||||
var tokenRepository = call.getContext().getService(RepositoryService.class).getRepository(ApiTokenRepository.class);
|
var tokenRepository = call.getContext().getService(RepositoryService.class).getRepository(ApiTokenRepository.class);
|
||||||
var tokens = JsonNodeFactory.instance.arrayNode();
|
var tokens = JsonNodeFactory.instance.arrayNode();
|
||||||
|
|
@ -77,26 +57,7 @@ public class Auth extends JsonRpcService {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonRpcMethod(accessLevel = UserRole.User)
|
||||||
@JsonRpcMethod(
|
|
||||||
accessLevel = UserRole.User,
|
|
||||||
description = "Generates a new API token and returns its details",
|
|
||||||
parameters = {
|
|
||||||
@JsonRpcMethod.Parameter(
|
|
||||||
name = "permanent",
|
|
||||||
description = "If true, creates a token that never expires",
|
|
||||||
type = boolean.class,
|
|
||||||
optional = true
|
|
||||||
),
|
|
||||||
@JsonRpcMethod.Parameter(
|
|
||||||
name = "name",
|
|
||||||
description = "Display name for the token. If not provided, the User-Agent header will be used",
|
|
||||||
type = String.class,
|
|
||||||
optional = true
|
|
||||||
)
|
|
||||||
},
|
|
||||||
returnType = ObjectNode.class
|
|
||||||
)
|
|
||||||
public ObjectNode generateToken(CallContext call) {
|
public ObjectNode generateToken(CallContext call) {
|
||||||
var repositoryService = call.getContext().getService(RepositoryService.class);
|
var repositoryService = call.getContext().getService(RepositoryService.class);
|
||||||
var tokenRepository = repositoryService.getRepository(ApiTokenRepository.class);
|
var tokenRepository = repositoryService.getRepository(ApiTokenRepository.class);
|
||||||
|
|
@ -118,19 +79,8 @@ public class Auth extends JsonRpcService {
|
||||||
return tokenRepository.serialize(token);
|
return tokenRepository.serialize(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonRpcMethod(
|
@JsonRpcMethod(accessLevel = UserRole.Guest)
|
||||||
accessLevel = UserRole.Guest,
|
public boolean authenticateByToken(CallContext call) throws IllegalAccessException {
|
||||||
description = "Authenticates a user using an API token. Returns true if authentication is successful.",
|
|
||||||
parameters = {
|
|
||||||
@JsonRpcMethod.Parameter(
|
|
||||||
name = "token",
|
|
||||||
description = "API token string for authentication",
|
|
||||||
type = String.class
|
|
||||||
)
|
|
||||||
},
|
|
||||||
returnType = boolean.class
|
|
||||||
)
|
|
||||||
public boolean authenticateByToken(CallContext call) {
|
|
||||||
var repositoryService = call.getContext().getService(RepositoryService.class);
|
var repositoryService = call.getContext().getService(RepositoryService.class);
|
||||||
var tokenRepository = repositoryService.getRepository(ApiTokenRepository.class);
|
var tokenRepository = repositoryService.getRepository(ApiTokenRepository.class);
|
||||||
var token = tokenRepository.get(UUID.fromString(
|
var token = tokenRepository.get(UUID.fromString(
|
||||||
|
|
@ -155,11 +105,7 @@ public class Auth extends JsonRpcService {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonRpcMethod(
|
@JsonRpcMethod(accessLevel = UserRole.User)
|
||||||
accessLevel = UserRole.User,
|
|
||||||
description = "Logs out the current user. Returns true if logout is successful, false if the user is not logged in",
|
|
||||||
returnType = boolean.class
|
|
||||||
)
|
|
||||||
public boolean logout(CallContext call) {
|
public boolean logout(CallContext call) {
|
||||||
ru.kirillius.XCP.Persistence.Entities.User user = null;
|
ru.kirillius.XCP.Persistence.Entities.User user = null;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ public class WebServiceImpl implements WebService {
|
||||||
@Override
|
@Override
|
||||||
public void initialize(Context context) {
|
public void initialize(Context context) {
|
||||||
if (server != null) {
|
if (server != null) {
|
||||||
throw new IllegalStateException("Server is started already");
|
throw new IllegalStateException("Server already started");
|
||||||
}
|
}
|
||||||
var jsonRpc = new JsonRpcServlet(context);
|
var jsonRpc = new JsonRpcServlet(context);
|
||||||
jsonRpc.registerRpcService(
|
jsonRpc.registerRpcService(
|
||||||
|
|
@ -47,11 +47,6 @@ public class WebServiceImpl implements WebService {
|
||||||
}
|
}
|
||||||
|
|
||||||
server.setHandler(servletContext);
|
server.setHandler(servletContext);
|
||||||
try {
|
|
||||||
server.start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to start jetty web server", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>ru.kirillius</groupId>
|
|
||||||
<artifactId>XCP</artifactId>
|
|
||||||
<version>1.0.0.0</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>web-ui</artifactId>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
Loading…
Reference in New Issue