diff --git a/src/main/java/net/frozenorb/apiv3/APIv3.java b/src/main/java/net/frozenorb/apiv3/APIv3.java index 3db5f36..2995871 100644 --- a/src/main/java/net/frozenorb/apiv3/APIv3.java +++ b/src/main/java/net/frozenorb/apiv3/APIv3.java @@ -220,7 +220,8 @@ public final class APIv3 extends AbstractVerticle { ), (a, b) -> { }); database.getCollection("ipIntel").createIndexes(ImmutableList.of( - new IndexModel(new Document("hashedIp", 1)) + new IndexModel(new Document("hashedIp", 1)), + new IndexModel(new Document("location", "2dsphere")) ), (a, b) -> { }); database.getCollection("punishments").createIndexes(ImmutableList.of( diff --git a/src/main/java/net/frozenorb/apiv3/model/IpIntel.java b/src/main/java/net/frozenorb/apiv3/model/IpIntel.java index e3d3f37..4c85887 100644 --- a/src/main/java/net/frozenorb/apiv3/model/IpIntel.java +++ b/src/main/java/net/frozenorb/apiv3/model/IpIntel.java @@ -11,7 +11,9 @@ import io.vertx.core.Future; import lombok.AllArgsConstructor; import lombok.Getter; import net.frozenorb.apiv3.APIv3; +import net.frozenorb.apiv3.maxmind.MaxMindLocation; import net.frozenorb.apiv3.maxmind.MaxMindResult; +import net.frozenorb.apiv3.util.GeoJsonPoint; import net.frozenorb.apiv3.util.MaxMindUtils; import net.frozenorb.apiv3.util.SyncUtils; import org.bson.Document; @@ -30,6 +32,11 @@ public final class IpIntel { @Getter private String hashedIp; @Getter private Instant lastUpdatedAt; @Getter private MaxMindResult result; + @Getter private GeoJsonPoint location; + + public static void findAllNoSort(SingleResultCallback> callback) { + ipIntelCollection.find().into(new LinkedList<>(), SyncUtils.vertxWrap(callback)); + } public static void findAll(SingleResultCallback> callback) { ipIntelCollection.find().sort(new Document("lastSeenAt", -1)).into(new LinkedList<>(), SyncUtils.vertxWrap(callback)); @@ -133,6 +140,11 @@ public final class IpIntel { this.hashedIp = Hashing.sha256().hashString(id + APIv3.getConfig().getProperty("ipHashing.salt"), Charsets.UTF_8).toString(); this.lastUpdatedAt = Instant.now(); this.result = result; + + if (result.getLocation() != null) { + MaxMindLocation location = result.getLocation(); + this.location = new GeoJsonPoint(location.getLongitude(), location.getLatitude()); + } } } \ No newline at end of file diff --git a/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java b/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java index eb863ef..f7117aa 100644 --- a/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java +++ b/src/main/java/net/frozenorb/apiv3/route/GETDumpsType.java @@ -8,18 +8,24 @@ import io.vertx.ext.web.RoutingContext; import net.frozenorb.apiv3.APIv3; import net.frozenorb.apiv3.model.Grant; import net.frozenorb.apiv3.model.IpBan; +import net.frozenorb.apiv3.model.IpIntel; import net.frozenorb.apiv3.model.Punishment; import net.frozenorb.apiv3.util.ErrorUtils; +import net.frozenorb.apiv3.util.GeoJsonPoint; +import java.text.DecimalFormat; import java.util.*; import java.util.concurrent.TimeUnit; public final class GETDumpsType implements Handler { + private static final DecimalFormat coordinateFormat = new DecimalFormat("#.#####"); + private static List banCache = ImmutableList.of(); private static List blacklistCache = ImmutableList.of(); private static List ipBanCache = ImmutableList.of(); private static Map> grantCache = ImmutableMap.of(); + private static Map ipIntelCache = ImmutableMap.of(); static { APIv3.getVertxInstance().setPeriodic(TimeUnit.MINUTES.toMillis(5), (id) -> updateCache()); @@ -98,6 +104,26 @@ public final class GETDumpsType implements Handler { GETDumpsType.ipBanCache = ipBanCache; }); + + IpIntel.findAllNoSort((ipIntel, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Map ipIntelCache = new HashMap<>(); + + for (IpIntel intel : ipIntel) { + GeoJsonPoint point = intel.getLocation(); + + if (point != null) { + String key = coordinateFormat.format(point.getLongitude()) + ":" + coordinateFormat.format(point.getLatitude()); + ipIntelCache.put(key, point); + } + } + + GETDumpsType.ipIntelCache = ipIntelCache; + }); } public void handle(RoutingContext ctx) { @@ -124,8 +150,26 @@ public final class GETDumpsType implements Handler { case "grant": APIv3.respondJson(ctx, 200, grantCache); return; + case "ipintel": + APIv3.respondJson(ctx, 200, ipIntelCache.values()); + return; + case "ipintelformatted": + List> features = new ArrayList<>(ipIntelCache.size()); + + ipIntelCache.values().forEach(point -> { + features.add(ImmutableMap.of( + "type", "Feature", + "geometry", point + )); + }); + + APIv3.respondJson(ctx, 200, ImmutableMap.of( + "type", "FeatureCollection", + "features", features + )); + return; default: - ErrorUtils.respondInvalidInput(ctx, dumpType + " is not a valid type. Not in [ban, blacklist, accessDeniable, ipBan, grant]"); + ErrorUtils.respondInvalidInput(ctx, dumpType + " is not a valid type. Not in [ban, blacklist, accessDeniable, ipBan, grant, ipIntel, ipIntelFormatted]"); } } diff --git a/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java b/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java new file mode 100644 index 0000000..5267000 --- /dev/null +++ b/src/main/java/net/frozenorb/apiv3/util/GeoJsonPoint.java @@ -0,0 +1,22 @@ +package net.frozenorb.apiv3.util; + +public final class GeoJsonPoint { + + private String type = "Point"; + private double[] coordinates; + + private GeoJsonPoint() {} // For Jackson + + public GeoJsonPoint(double longitude, double latitude) { + this.coordinates = new double[] { longitude, latitude}; + } + + public double getLongitude() { + return coordinates[0]; + } + + public double getLatitude() { + return coordinates[1]; + } + +} \ No newline at end of file