diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index fbbc43b..f9aa9aa 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -76,6 +76,8 @@ import net.frozenorb.apiv3.route.ipBans.GETIpBansId; import net.frozenorb.apiv3.route.ipBans.POSTIpBans; import net.frozenorb.apiv3.route.ipIntel.GETIpInteld; import net.frozenorb.apiv3.route.ipLog.GETIpLogId; +import net.frozenorb.apiv3.route.lookup.GETLookupByName; +import net.frozenorb.apiv3.route.lookup.GETLookupByUuid; import net.frozenorb.apiv3.route.notificationTemplates.DELETENotificationTemplatesId; import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplates; import net.frozenorb.apiv3.route.notificationTemplates.GETNotificationTemplatesId; @@ -356,6 +358,9 @@ public final class APIv3 extends AbstractVerticle { http.get("/ipLog/:id").handler(new GETIpLogId()); + http.get("/lookup/byName").blockingHandler(new GETLookupByName()); + http.get("/lookup/byUuid").blockingHandler(new GETLookupByUuid()); + http.get("/notificationTemplates/:notificationTemplateId").handler(new GETNotificationTemplatesId()); http.get("/notificationTemplates").handler(new GETNotificationTemplates()); http.post("/notificationTemplates").blockingHandler(new POSTNotificationTemplates(), false); diff --git a/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByName.java b/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByName.java new file mode 100644 index 0000000..a9a5d3b --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByName.java @@ -0,0 +1,51 @@ +package net.frozenorb.apiv3.route.lookup; + +import com.mongodb.async.client.MongoCollection; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.util.ErrorUtils; +import net.frozenorb.apiv3.util.SyncUtils; +import net.frozenorb.apiv3.util.UuidUtils; +import org.bson.Document; + +import java.util.*; + +public class GETLookupByName implements Handler { + + private static final MongoCollection usersCollection = APIv3.getDatabase().getCollection("users"); + + public void handle(RoutingContext ctx) { + List rawNames = ctx.request().params().getAll("name"); + // because we accept names in any case, we store the lower case -> + // how we were given the name, so we can return it to clients properly + Map originalCase = new HashMap<>(); + + for (String rawName : rawNames) { + originalCase.put(rawName.toLowerCase(), rawName); + } + + Document query = new Document("lastUsernameLower", new Document("$in", originalCase.keySet())); + Document project = new Document("lastUsernameLower", 1); // includes _id automatically + List into = new ArrayList<>(); + + usersCollection.find(query).projection(project).into(into, SyncUtils.vertxWrap((users, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + return; + } + + Map result = new HashMap<>(); + + users.forEach(doc -> { + String lowerName = doc.getString("lastUsernameLower"); + String clientUuid = originalCase.get(lowerName); + + result.put(clientUuid, doc.getString("_id")); + }); + + APIv3.respondJson(ctx, 200, result); + })); + } + +} \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByUuid.java b/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByUuid.java new file mode 100644 index 0000000..8f01528 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/route/lookup/GETLookupByUuid.java @@ -0,0 +1,60 @@ +package net.frozenorb.apiv3.route.lookup; + +import com.mongodb.async.client.MongoCollection; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.util.ErrorUtils; +import net.frozenorb.apiv3.util.SyncUtils; +import net.frozenorb.apiv3.util.UuidUtils; +import org.bson.Document; + +import java.util.*; + +public final class GETLookupByUuid implements Handler { + + private static final MongoCollection usersCollection = APIv3.getDatabase().getCollection("users"); + + public void handle(RoutingContext ctx) { + List rawUuids = ctx.request().params().getAll("uuid"); + // because we accept uuids with/without dashes, we store the actual uuid -> + // how we were given the uuid, so we can return it to clients properly + Map originalInputs = new HashMap<>(); + + for (String rawUuid : rawUuids) { + try { + UUID parsedUuid = UuidUtils.parseUuid(rawUuid); + + if (UuidUtils.isAcceptableUuid(parsedUuid)) { + originalInputs.put(parsedUuid.toString(), rawUuid); + } + } catch (IllegalArgumentException ignored) { + // that player will just be absent from the result, + // identical to how Mojang does it + } + } + + Document query = new Document("_id", new Document("$in", originalInputs.keySet())); + Document project = new Document("lastUsername", 1); + List into = new ArrayList<>(); + + usersCollection.find(query).projection(project).into(into, SyncUtils.vertxWrap((users, error) -> { + if (error != null) { + ErrorUtils.respondInternalError(ctx, error); + return; + } + + Map result = new HashMap<>(); + + users.forEach(doc -> { + String properUuid = doc.getString("_id"); + String clientUuid = originalInputs.get(properUuid); + + result.put(clientUuid, doc.getString("lastUsername")); + }); + + APIv3.respondJson(ctx, 200, result); + })); + } + +} \ No newline at end of file