Compare commits

..

No commits in common. "3f022de86205fefc5d79cc978390b5535ef54879" and "898a170d7b9ff403e5f18be6ae24d583fd306aea" have entirely different histories.

19 changed files with 61 additions and 384 deletions

2
.gitignore vendored
View File

@ -38,5 +38,3 @@ build/
.DS_Store .DS_Store
/.idea/ /.idea/
/.mvn/ /.mvn/
xcp.conf
xcpdata.mv.db

View File

@ -1,48 +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-spec-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-spec</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>ru.kirillius.XCP.ApiGenerator.SpecGenerator</mainClass>
<arguments>
<argument>rpc/src/main/java</argument>
<argument>ru.kirillius.XCP.RPC.Services</argument>
<argument>target/generated-sources/api.spec.json</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,125 +0,0 @@
package ru.kirillius.XCP.ApiGenerator;
import lombok.SneakyThrows;
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcMethod;
import ru.kirillius.XCP.RPC.JSONRPC.JsonRpcService;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode;
import java.io.*;
import java.util.Collection;
import java.util.Objects;
import java.util.regex.Pattern;
public class SpecGenerator {
/**
* Args:
* 1 - path to find classes
* 2 - search in package
* 3 - output spec file path
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
var generator = new SpecGenerator(
new File(args[0]),
args[1],
new File(args[2])
);
generator.generate();
generator.writeSpecs();
}
private final File scanDir;
private final String packageName;
private final File outputFile;
public SpecGenerator(File scanDir, String packageName, File outputFile) {
this.scanDir = scanDir;
this.packageName = packageName;
this.outputFile = outputFile;
}
private final ObjectNode specs = JsonNodeFactory.instance.objectNode();
@SneakyThrows
public void generate() {
specs.removeAll();
var scanPath = new File(scanDir, packageName.replaceAll(Pattern.quote("."), "/"));
for (var file : Objects.requireNonNull(scanPath.listFiles())) {
if (!file.getName().endsWith(".java") || file.getName().contains("$")) {
continue;
}
var className = packageName + '.' + file.getName();
className = className.substring(0, className.length() - ".java".length());
var cls = Class.forName(className);
if (!JsonRpcService.class.isAssignableFrom(cls)) {
continue;
}
var classSpecs = specs.putObject(cls.getSimpleName());
classSpecs.put("name", cls.getSimpleName());
var methods = classSpecs.putArray("methods");
for (var method : cls.getDeclaredMethods()) {
var descriptor = method.getAnnotation(JsonRpcMethod.class);
if (descriptor == null) {
continue;
}
var methodSpecs = methods.addObject();
methodSpecs.put("name", method.getName());
methodSpecs.put("description", descriptor.description());
methodSpecs.put("return", getTypeName(descriptor.returnType()));
methodSpecs.put("accessLevel", descriptor.accessLevel().name());
var params = methodSpecs.putArray("params");
for (var parameter : descriptor.parameters()) {
var paramSpecs = params.addObject();
paramSpecs.put("name", parameter.name());
paramSpecs.put("type", getTypeName(parameter.type()));
paramSpecs.put("description", parameter.description());
paramSpecs.put("optional", parameter.optional());
}
methodSpecs.put("return", getTypeName(descriptor.returnType()));
}
}
}
public void writeSpecs() throws IOException {
outputFile.getParentFile().mkdirs();
try (var stream = new FileOutputStream(outputFile)) {
var mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter().writeValue(stream, specs);
}
}
private static String getTypeName(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";
if (type.isArray() || Collection.class.isAssignableFrom(type) || type == ArrayNode.class) {
return "Array";
}
if (JsonNode.class.isAssignableFrom(type)) {
return "Object";
}
return type.getSimpleName();
}
}

View File

@ -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);
} }
}); });
} }

View File

@ -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

View File

@ -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>

View File

@ -8,7 +8,7 @@ import org.hibernate.annotations.UuidGenerator;
import ru.kirillius.XCP.Commons.StreamHandler; import ru.kirillius.XCP.Commons.StreamHandler;
import ru.kirillius.XCP.Persistence.Entities.Group; import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.*; import ru.kirillius.XCP.Persistence.*;
import tools.jackson.databind.node.JsonNodeFactory; import ru.kirillius.XCP.Serialization.SerializationUtils;
import tools.jackson.databind.node.ObjectNode; import tools.jackson.databind.node.ObjectNode;
import java.io.IOException; import java.io.IOException;
@ -157,7 +157,7 @@ public class GroupRepositoryImpl extends AbstractNodeRepository<Group> implement
@JsonProperty @JsonProperty
@Getter @Getter
@Setter @Setter
private ObjectNode properties = JsonNodeFactory.instance.objectNode(); private ObjectNode properties = SerializationUtils.EmptyObject();
@JsonIgnore @JsonIgnore
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)

View File

@ -11,9 +11,9 @@ import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.Entities.Input; import ru.kirillius.XCP.Persistence.Entities.Input;
import ru.kirillius.XCP.Persistence.*; import ru.kirillius.XCP.Persistence.*;
import ru.kirillius.XCP.Serialization.PollSettingsConverter; import ru.kirillius.XCP.Serialization.PollSettingsConverter;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter; import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
import tools.jackson.databind.annotation.JsonDeserialize; import tools.jackson.databind.annotation.JsonDeserialize;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode; import tools.jackson.databind.node.ObjectNode;
import java.util.HashSet; import java.util.HashSet;
@ -110,7 +110,7 @@ public class InputRepositoryImpl extends AbstractNodeRepository<Input> implement
@JsonProperty @JsonProperty
@Getter @Getter
@Setter @Setter
private ObjectNode properties = JsonNodeFactory.instance.objectNode(); private ObjectNode properties = SerializationUtils.EmptyObject();
@JsonIgnore @JsonIgnore
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)

View File

@ -9,8 +9,8 @@ import ru.kirillius.XCP.Data.ValueTransformationChain;
import ru.kirillius.XCP.Persistence.Entities.Group; import ru.kirillius.XCP.Persistence.Entities.Group;
import ru.kirillius.XCP.Persistence.Entities.Output; import ru.kirillius.XCP.Persistence.Entities.Output;
import ru.kirillius.XCP.Persistence.*; import ru.kirillius.XCP.Persistence.*;
import ru.kirillius.XCP.Serialization.SerializationUtils;
import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter; import ru.kirillius.XCP.Serialization.ValueTransformationChainConverter;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.node.ObjectNode; import tools.jackson.databind.node.ObjectNode;
import java.util.HashSet; import java.util.HashSet;
@ -106,7 +106,7 @@ public class OutputRepositoryImpl extends AbstractNodeRepository<Output> impleme
@JsonProperty @JsonProperty
@Getter @Getter
@Setter @Setter
private ObjectNode properties = JsonNodeFactory.instance.objectNode(); private ObjectNode properties = SerializationUtils.EmptyObject();
@JsonIgnore @JsonIgnore
@ManyToMany(fetch = FetchType.EAGER) @ManyToMany(fetch = FetchType.EAGER)

View File

@ -12,7 +12,7 @@ import ru.kirillius.XCP.Persistence.Entities.User;
import ru.kirillius.XCP.Persistence.EntityImplementation; import ru.kirillius.XCP.Persistence.EntityImplementation;
import ru.kirillius.XCP.Persistence.RepositoryServiceImpl; import ru.kirillius.XCP.Persistence.RepositoryServiceImpl;
import ru.kirillius.XCP.Security.UserRole; import ru.kirillius.XCP.Security.UserRole;
import tools.jackson.databind.node.JsonNodeFactory; import ru.kirillius.XCP.Serialization.SerializationUtils;
import tools.jackson.databind.node.ObjectNode; import tools.jackson.databind.node.ObjectNode;
import java.io.IOException; import java.io.IOException;
@ -80,7 +80,7 @@ public class UserRepositoryImpl extends AbstractRepository<User> implements User
@Getter @Getter
@Setter @Setter
@JsonProperty @JsonProperty
private ObjectNode values = JsonNodeFactory.instance.objectNode(); private ObjectNode values = SerializationUtils.EmptyObject();
@Override @Override
public void setPassword(String password) { public void setPassword(String password) {

View File

@ -0,0 +1,17 @@
package ru.kirillius.XCP.Serialization;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
public final class SerializationUtils {
public final static ObjectMapper mapper = new ObjectMapper();
public static ObjectNode EmptyObject() {
return mapper.createObjectNode();
}
public static ArrayNode EmptyArray() {
return mapper.createArrayNode();
}
}

View File

@ -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();

View File

@ -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);
} }
} }

View File

@ -10,14 +10,12 @@
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<module>api</module> <module>api</module>
<module>api-spec-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>

View File

@ -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;
}
} }

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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>