fix(velocity): 保存最近一次服务器列表,控制面不可用时重启代理仍能路由登录

This commit is contained in:
Lemon-miaow committed 2026-09-25 22:58:53 +08:00
1 parent 7bf81a0921
commit 0fb43232b5
8 files changed
+430 -21

No files matched your search

@@ -7,7 +7,6 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -57,17 +56,7 @@ public final class FelisApiClient {
/** listServers returns the lifecycle view of every MinecraftServer (GET /servers). */
public List<ServerView> listServers() throws LinkException {
Map<?, ?> obj = getObject("/api/v1/servers", 200);
Object arr = obj.get("servers");
List<ServerView> out = new ArrayList<>();
if (arr instanceof List) {
for (Object e : (List<?>) arr) {
if (e instanceof Map) {
out.add(ServerView.fromJson((Map<?, ?>) e));
}
}
}
return out;
return ServerView.listFrom(getObject("/api/v1/servers", 200));
}
/** serverStatus reads one server's current lifecycle view (internal status). */
@@ -1,5 +1,8 @@
package best.lolicon.felis.link;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
@@ -62,6 +65,66 @@ public final class ServerView {
intval(o, "playersMax"));
}
/**
* listFromJson reads the {@code {"servers":[...]}} envelope {@code GET /servers}
* answers with, which is also the shape of the proxy's saved server list. Entries
* that are not objects are skipped; text that is not such an envelope throws
* IllegalArgumentException.
*/
public static List<ServerView> listFromJson(String text) {
Object root = Json.parse(text);
if (!(root instanceof Map)) {
throw new IllegalArgumentException("server list: not a JSON object");
}
return listFrom((Map<?, ?>) root);
}
static List<ServerView> listFrom(Map<?, ?> envelope) {
Object arr = envelope.get("servers");
List<ServerView> out = new ArrayList<>();
if (arr instanceof List) {
for (Object e : (List<?>) arr) {
if (e instanceof Map) {
out.add(fromJson((Map<?, ?>) e));
}
}
}
return out;
}
/** listToJson renders views in the envelope {@link #listFromJson(String)} reads back. */
public static String listToJson(Collection<ServerView> views) {
StringBuilder b = new StringBuilder("{\"servers\":[");
boolean first = true;
for (ServerView v : views) {
if (!first) {
b.append(',');
}
first = false;
b.append('{');
field(b, "name", v.name, true);
field(b, "subdomain", v.subdomain, false);
field(b, "phase", v.phase, false);
b.append(",\"ready\":").append(v.ready);
field(b, "autostartPolicy", v.autostartPolicy, false);
field(b, "desiredState", v.desiredState, false);
field(b, "endpointMode", v.endpointMode, false);
field(b, "endpointAddress", v.endpointAddress, false);
b.append(",\"playersOnline\":").append(v.playersOnline);
b.append(",\"playersMax\":").append(v.playersMax);
b.append('}');
}
return b.append("]}").toString();
}
// field appends "key":"value", or "key":null for an absent value.
private static void field(StringBuilder b, String key, String value, boolean first) {
if (!first) {
b.append(',');
}
b.append(Json.quote(key)).append(':').append(value == null ? "null" : Json.quote(value));
}
public String name() {
return name;
}