feat(api): add internal API foundation for UI
- Create api package with AuthService, InstanceService, LaunchService - Add ApiResponse<T> model for consistent responses - Create LauncherAPI central facade for all services - Update Main.java to use new API for session checking - All services compile successfully
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package me.sashegdev.zernmc.launcher;
|
||||
|
||||
import me.sashegdev.zernmc.launcher.api.LauncherAPI;
|
||||
import me.sashegdev.zernmc.launcher.auth.AuthManager;
|
||||
import me.sashegdev.zernmc.launcher.menu.*;
|
||||
import me.sashegdev.zernmc.launcher.ui.ArrowMenu;
|
||||
@@ -16,6 +17,7 @@ import java.util.List;
|
||||
public class Main {
|
||||
|
||||
private static final String CURRENT_VERSION = Version.getCurrentVersion();
|
||||
private static final LauncherAPI api = new LauncherAPI();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
System.setProperty("org.jline.terminal.disableDeprecatedProviderWarning", "true");
|
||||
@@ -32,11 +34,11 @@ public class Main {
|
||||
|
||||
checkAndAutoUpdateLauncher();
|
||||
|
||||
// === АВТОРИЗАЦИЯ ===
|
||||
// === АВТОРИЗАЦИЯ (используем новый API) ===
|
||||
System.out.println(ZAnsi.cyan("Проверка авторизации..."));
|
||||
boolean sessionRestored = AuthManager.loadSavedSession();
|
||||
var sessionResponse = api.checkSession();
|
||||
|
||||
if (!sessionRestored) {
|
||||
if (!sessionResponse.isSuccess()) {
|
||||
LoginMenu loginMenu = new LoginMenu();
|
||||
boolean loggedIn = loginMenu.show();
|
||||
if (!loggedIn) {
|
||||
@@ -45,7 +47,8 @@ public class Main {
|
||||
System.exit(0);
|
||||
}
|
||||
} else {
|
||||
System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + AuthManager.getUsername() + "!"));
|
||||
var sessionInfo = sessionResponse.getData();
|
||||
System.out.println(ZAnsi.brightGreen("Добро пожаловать обратно, " + sessionInfo.getUsername() + "!"));
|
||||
}
|
||||
|
||||
// === ГЛАВНЫЙ ЦИКЛ ===
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.sashegdev.zernmc.launcher.api;
|
||||
|
||||
public class ApiResponse<T> {
|
||||
private boolean success;
|
||||
private T data;
|
||||
private String error;
|
||||
|
||||
public ApiResponse(boolean success, T data, String error) {
|
||||
this.success = success;
|
||||
this.data = data;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(true, data, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String error) {
|
||||
return new ApiResponse<>(false, null, error);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package me.sashegdev.zernmc.launcher.api;
|
||||
|
||||
import me.sashegdev.zernmc.launcher.api.auth.AuthService;
|
||||
import me.sashegdev.zernmc.launcher.api.instance.InstanceService;
|
||||
import me.sashegdev.zernmc.launcher.api.launch.LaunchService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Центральный фасад для внутреннего API лаунчера.
|
||||
* Используется как единая точка входа для UI и других компонентов.
|
||||
*/
|
||||
public class LauncherAPI {
|
||||
|
||||
private final AuthService authService;
|
||||
private final InstanceService instanceService;
|
||||
private final LaunchService launchService;
|
||||
|
||||
public LauncherAPI() {
|
||||
this.authService = new AuthService();
|
||||
this.instanceService = new InstanceService();
|
||||
this.launchService = new LaunchService();
|
||||
}
|
||||
|
||||
public AuthService auth() {
|
||||
return authService;
|
||||
}
|
||||
|
||||
public InstanceService instances() {
|
||||
return instanceService;
|
||||
}
|
||||
|
||||
public LaunchService launch() {
|
||||
return launchService;
|
||||
}
|
||||
|
||||
// ====================== Удобные методы ======================
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return authService.isLoggedIn();
|
||||
}
|
||||
|
||||
public String getCurrentUsername() {
|
||||
return authService.getCurrentUsername();
|
||||
}
|
||||
|
||||
public ApiResponse<AuthService.SessionInfo> checkSession() {
|
||||
return authService.checkSession();
|
||||
}
|
||||
|
||||
public ApiResponse<AuthService.LoginResult> login(String username, String password) {
|
||||
return authService.login(username, password);
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> logout() {
|
||||
return authService.logout();
|
||||
}
|
||||
|
||||
public ApiResponse<List<InstanceService.InstanceInfo>> getAllInstances() {
|
||||
return instanceService.getAllInstances();
|
||||
}
|
||||
|
||||
public ApiResponse<LaunchService.InstanceInfo> getLaunchInfo(String instanceName) {
|
||||
return launchService.getLaunchInfo(instanceName);
|
||||
}
|
||||
|
||||
public ApiResponse<LaunchService.LaunchInfo> prepareLaunch(String instanceName) {
|
||||
return launchService.prepareLaunch(instanceName);
|
||||
}
|
||||
|
||||
public ApiResponse<LaunchService.ProcessInfo> launch(String instanceName) {
|
||||
return launchService.launch(instanceName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package me.sashegdev.zernmc.launcher.api.auth;
|
||||
|
||||
import me.sashegdev.zernmc.launcher.api.ApiResponse;
|
||||
import me.sashegdev.zernmc.launcher.auth.AuthManager;
|
||||
import me.sashegdev.zernmc.launcher.utils.ZHttpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AuthService {
|
||||
|
||||
public ApiResponse<LoginResult> login(String username, String password) {
|
||||
try {
|
||||
AuthManager.AuthResult result = AuthManager.login(username, password);
|
||||
if (result.success) {
|
||||
LoginResult loginResult = new LoginResult(AuthManager.getUsername(), AuthManager.getAccessToken());
|
||||
return ApiResponse.success(loginResult);
|
||||
}
|
||||
return ApiResponse.error(result.error != null ? result.error : "Неверный логин или пароль");
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка авторизации: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> logout() {
|
||||
try {
|
||||
AuthManager.logout();
|
||||
return ApiResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка при выходе: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<SessionInfo> checkSession() {
|
||||
try {
|
||||
boolean restored = AuthManager.loadSavedSession();
|
||||
if (restored) {
|
||||
SessionInfo info = new SessionInfo(
|
||||
AuthManager.getUsername(),
|
||||
AuthManager.getAccessToken(),
|
||||
AuthManager.hasActivePass()
|
||||
);
|
||||
return ApiResponse.success(info);
|
||||
}
|
||||
return ApiResponse.error("Сессия не найдена");
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка проверки сессии: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> activatePass(String passCode) {
|
||||
try {
|
||||
String response = post("/auth/pass/activate",
|
||||
"{\"code\":\"" + passCode + "\"}");
|
||||
return ApiResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка активации проходки: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String post(String endpoint, String jsonBody) throws Exception {
|
||||
String fullUrl = ZHttpClient.getBaseUrl() + endpoint;
|
||||
java.net.URL url = new java.net.URL(fullUrl);
|
||||
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
|
||||
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setRequestProperty("User-Agent", "ZernMC-Launcher/1.0");
|
||||
|
||||
if (AuthManager.getAccessToken() != null && !AuthManager.getAccessToken().equals("0")) {
|
||||
conn.setRequestProperty("Authorization", "Bearer " + AuthManager.getAccessToken());
|
||||
}
|
||||
|
||||
conn.setDoOutput(true);
|
||||
|
||||
try (var os = conn.getOutputStream()) {
|
||||
byte[] input = jsonBody.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
os.write(input);
|
||||
}
|
||||
|
||||
int statusCode = conn.getResponseCode();
|
||||
var is = (statusCode >= 200 && statusCode < 300) ? conn.getInputStream() : conn.getErrorStream();
|
||||
|
||||
String responseBody;
|
||||
try (var scanner = new java.util.Scanner(is, java.nio.charset.StandardCharsets.UTF_8.name())) {
|
||||
responseBody = scanner.useDelimiter("\\A").hasNext() ? scanner.next() : "";
|
||||
}
|
||||
|
||||
conn.disconnect();
|
||||
|
||||
if (statusCode != 200) {
|
||||
throw new IOException("HTTP " + statusCode + ": " + responseBody);
|
||||
}
|
||||
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return AuthManager.isLoggedIn();
|
||||
}
|
||||
|
||||
public String getCurrentUsername() {
|
||||
return AuthManager.getUsername();
|
||||
}
|
||||
|
||||
public static class LoginResult {
|
||||
private String username;
|
||||
private String token;
|
||||
|
||||
public LoginResult(String username, String token) {
|
||||
this.username = username;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getUsername() { return username; }
|
||||
public String getToken() { return token; }
|
||||
}
|
||||
|
||||
public static class SessionInfo {
|
||||
private String username;
|
||||
private String token;
|
||||
private boolean passActive;
|
||||
|
||||
public SessionInfo(String username, String token, boolean passActive) {
|
||||
this.username = username;
|
||||
this.token = token;
|
||||
this.passActive = passActive;
|
||||
}
|
||||
|
||||
public String getUsername() { return username; }
|
||||
public String getToken() { return token; }
|
||||
public boolean isPassActive() { return passActive; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package me.sashegdev.zernmc.launcher.api.instance;
|
||||
|
||||
import me.sashegdev.zernmc.launcher.api.ApiResponse;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.InstanceManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InstanceService {
|
||||
|
||||
public ApiResponse<List<InstanceInfo>> getAllInstances() {
|
||||
try {
|
||||
List<Instance> instances = InstanceManager.getAllInstances();
|
||||
List<InstanceInfo> infoList = instances.stream()
|
||||
.map(this::toInstanceInfo)
|
||||
.collect(Collectors.toList());
|
||||
return ApiResponse.success(infoList);
|
||||
} catch (IOException e) {
|
||||
return ApiResponse.error("Ошибка получения списка сборок: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<InstanceInfo> getInstance(String name) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(name);
|
||||
if (instance == null) {
|
||||
return ApiResponse.error("Сборка не найдена: " + name);
|
||||
}
|
||||
return ApiResponse.success(toInstanceInfo(instance));
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка получения сборки: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<InstanceInfo> createInstance(String name) {
|
||||
try {
|
||||
boolean created = InstanceManager.createInstanceFolder(name);
|
||||
if (!created) {
|
||||
return ApiResponse.error("Сборка с таким именем уже существует: " + name);
|
||||
}
|
||||
Instance instance = InstanceManager.getInstance(name);
|
||||
return ApiResponse.success(toInstanceInfo(instance));
|
||||
} catch (IOException e) {
|
||||
return ApiResponse.error("Ошибка создания сборки: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> deleteInstance(String name) {
|
||||
try {
|
||||
boolean deleted = InstanceManager.deleteInstance(name);
|
||||
if (!deleted) {
|
||||
return ApiResponse.error("Не удалось удалить сборку: " + name);
|
||||
}
|
||||
return ApiResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка удаления сборки: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> isInstanceExists(String name) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(name);
|
||||
return ApiResponse.success(instance != null);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка проверки сборки: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private InstanceInfo toInstanceInfo(Instance instance) {
|
||||
return new InstanceInfo(
|
||||
instance.getName(),
|
||||
instance.getPath().toString(),
|
||||
instance.getMinecraftVersion(),
|
||||
instance.getLoaderType()
|
||||
);
|
||||
}
|
||||
|
||||
public static class InstanceInfo {
|
||||
private String name;
|
||||
private String path;
|
||||
private String version;
|
||||
private String loaderType;
|
||||
|
||||
public InstanceInfo(String name, String path, String version, String loaderType) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.version = version;
|
||||
this.loaderType = loaderType;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public String getPath() { return path; }
|
||||
public String getVersion() { return version; }
|
||||
public String getLoaderType() { return loaderType; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package me.sashegdev.zernmc.launcher.api.launch;
|
||||
|
||||
import me.sashegdev.zernmc.launcher.api.ApiResponse;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.Instance;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.InstanceManager;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.launch.LaunchCommandBuilder;
|
||||
import me.sashegdev.zernmc.launcher.minecraft.model.LaunchOptions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class LaunchService {
|
||||
|
||||
public ApiResponse<LaunchInfo> prepareLaunch(String instanceName) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(instanceName);
|
||||
if (instance == null) {
|
||||
return ApiResponse.error("Сборка не найдена: " + instanceName);
|
||||
}
|
||||
|
||||
LaunchCommandBuilder builder = new LaunchCommandBuilder(instance);
|
||||
LaunchOptions options = new LaunchOptions();
|
||||
|
||||
List<String> command = builder.build(options);
|
||||
|
||||
LaunchInfo info = new LaunchInfo(
|
||||
instanceName,
|
||||
command,
|
||||
instance.getPath().toString()
|
||||
);
|
||||
return ApiResponse.success(info);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка подготовки запуска: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<ProcessInfo> launch(String instanceName) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(instanceName);
|
||||
if (instance == null) {
|
||||
return ApiResponse.error("Сборка не найдена: " + instanceName);
|
||||
}
|
||||
|
||||
LaunchCommandBuilder builder = new LaunchCommandBuilder(instance);
|
||||
LaunchOptions options = new LaunchOptions();
|
||||
|
||||
List<String> command = builder.build(options);
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(instance.getPath().toFile());
|
||||
processBuilder.inheritIO();
|
||||
|
||||
Process process = processBuilder.start();
|
||||
|
||||
ProcessInfo info = new ProcessInfo(
|
||||
instanceName,
|
||||
process.pid(),
|
||||
"RUNNING"
|
||||
);
|
||||
return ApiResponse.success(info);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка запуска: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<Boolean> isReady(String instanceName) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(instanceName);
|
||||
if (instance == null) {
|
||||
return ApiResponse.error("Сборка не найдена: " + instanceName);
|
||||
}
|
||||
|
||||
Path versionJson = instance.getPath().resolve("version.json");
|
||||
boolean hasVersionJson = versionJson.toFile().exists();
|
||||
|
||||
return ApiResponse.success(hasVersionJson);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка проверки готовности: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResponse<InstanceInfo> getLaunchInfo(String instanceName) {
|
||||
try {
|
||||
Instance instance = InstanceManager.getInstance(instanceName);
|
||||
if (instance == null) {
|
||||
return ApiResponse.error("Сборка не найдена: " + instanceName);
|
||||
}
|
||||
|
||||
InstanceInfo info = new InstanceInfo(
|
||||
instance.getName(),
|
||||
instance.getMinecraftVersion(),
|
||||
instance.getLoaderType(),
|
||||
instance.getLoaderVersion(),
|
||||
instance.getAssetIndex()
|
||||
);
|
||||
return ApiResponse.success(info);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.error("Ошибка получения информации: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static class LaunchInfo {
|
||||
private String instanceName;
|
||||
private List<String> command;
|
||||
private String workingDirectory;
|
||||
|
||||
public LaunchInfo(String instanceName, List<String> command, String workingDirectory) {
|
||||
this.instanceName = instanceName;
|
||||
this.command = command;
|
||||
this.workingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
public String getInstanceName() { return instanceName; }
|
||||
public List<String> getCommand() { return command; }
|
||||
public String getWorkingDirectory() { return workingDirectory; }
|
||||
}
|
||||
|
||||
public static class ProcessInfo {
|
||||
private String instanceName;
|
||||
private long pid;
|
||||
private String status;
|
||||
|
||||
public ProcessInfo(String instanceName, long pid, String status) {
|
||||
this.instanceName = instanceName;
|
||||
this.pid = pid;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getInstanceName() { return instanceName; }
|
||||
public long getPid() { return pid; }
|
||||
public String getStatus() { return status; }
|
||||
}
|
||||
|
||||
public static class InstanceInfo {
|
||||
private String name;
|
||||
private String minecraftVersion;
|
||||
private String loaderType;
|
||||
private String loaderVersion;
|
||||
private String assetIndex;
|
||||
|
||||
public InstanceInfo(String name, String minecraftVersion, String loaderType,
|
||||
String loaderVersion, String assetIndex) {
|
||||
this.name = name;
|
||||
this.minecraftVersion = minecraftVersion;
|
||||
this.loaderType = loaderType;
|
||||
this.loaderVersion = loaderVersion;
|
||||
this.assetIndex = assetIndex;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
public String getMinecraftVersion() { return minecraftVersion; }
|
||||
public String getLoaderType() { return loaderType; }
|
||||
public String getLoaderVersion() { return loaderVersion; }
|
||||
public String getAssetIndex() { return assetIndex; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user