More stuff

This commit is contained in:
Colin McDonald 2016-05-05 16:38:50 -04:00
parent 27d139f0f6
commit 53c7222107
17 changed files with 103 additions and 29 deletions

View File

@ -1,3 +1,5 @@
general.id=main
general.releaseStage=production
mongo.address=ds055505.mongolab.com
mongo.port=55505
mongo.database=minehqapi
@ -5,11 +7,14 @@ mongo.username=test
mongo.password=test
redis.address=localhost
redis.port=6379
http.address=
http.address=0.0.0.0
http.port=80
http.workerThreads=6
twillio.accountSID=AC9e2f88c5690134d29a56f698de3cd740
twillio.authToken=982592505a171d3be6b0722f5ecacc0e
mandrill.apiKey=0OYtwymqJP6oqvszeJu0vQ
librato.email=cmcdonald.main@gmail.com
librato.apiKey=a818c3eca8a59d6d9cf76dc9f0d237c6aa97f257c482ce3363cf55a5431bc153
bugsnag.apiKey=
auth.permittedUserRanks=developer,owner
auth.websiteApiKey=RVbp4hY6sCFVaf

View File

@ -1,17 +1,17 @@
package net.frozenorb.apiv3;
import com.bugsnag.Client;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.librato.metrics.LibratoReporter;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import lombok.Getter;
import net.frozenorb.apiv3.filters.ActorAttributeFilter;
import net.frozenorb.apiv3.filters.AuthorizationFilter;
import net.frozenorb.apiv3.filters.ContentTypeFilter;
import net.frozenorb.apiv3.models.NotificationTemplate;
import net.frozenorb.apiv3.filters.*;
import net.frozenorb.apiv3.routes.GETDump;
import net.frozenorb.apiv3.routes.GETWhoAmI;
import net.frozenorb.apiv3.routes.NotFound;
@ -41,7 +41,6 @@ import net.frozenorb.apiv3.routes.users.*;
import net.frozenorb.apiv3.serialization.FollowAnnotationExclusionStrategy;
import net.frozenorb.apiv3.serialization.ObjectIdTypeAdapter;
import net.frozenorb.apiv3.unsorted.LoggingExceptionHandler;
import net.frozenorb.apiv3.unsorted.Notification;
import org.bson.types.ObjectId;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
@ -52,6 +51,7 @@ import redis.clients.jedis.JedisPool;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import static spark.Spark.*;
@ -60,6 +60,8 @@ public final class APIv3 {
@Getter private static Datastore datastore;
@Getter private static Properties config = new Properties();
@Getter private static JedisPool redisPool;
@Getter private static MetricRegistry metrics = new MetricRegistry();
@Getter private static Client bugsnagClient;
@Getter private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(ObjectId.class, new ObjectIdTypeAdapter())
.setExclusionStrategies(new FollowAnnotationExclusionStrategy())
@ -71,6 +73,8 @@ public final class APIv3 {
setupConfig();
setupDatabase();
setupRedis();
setupMetrics();
setupBugsnag();
setupHttp();
}
@ -110,6 +114,30 @@ public final class APIv3 {
);
}
private void setupMetrics() {
Gauge memoryUsageGauge = () -> Runtime.getRuntime().totalMemory() / (1024 * 1024);
Gauge memoryMaxGauge = () -> Runtime.getRuntime().maxMemory() / (1024 * 1024);
metrics.register(MetricRegistry.name("apiv3", "memory", "usage"), memoryUsageGauge);
metrics.register(MetricRegistry.name("apiv3", "memory", "max"), memoryMaxGauge);
LibratoReporter.enable(
LibratoReporter.builder(
metrics,
config.getProperty("librato.email"),
config.getProperty("librato.apiKey"),
config.getProperty("general.id")
),
1,
TimeUnit.MINUTES
);
}
private void setupBugsnag() {
bugsnagClient = new Client(config.getProperty("bugsnag.apiKey"));
bugsnagClient.setReleaseStage(config.getProperty("general.releaseStage"));
}
private void setupHttp() {
ipAddress(config.getProperty("http.address"));
port(Integer.parseInt(config.getProperty("http.port")));
@ -117,6 +145,8 @@ public final class APIv3 {
before(new ContentTypeFilter());
before(new ActorAttributeFilter());
before(new AuthorizationFilter());
before(new MetricsBeforeFilter());
after(new MetricsAfterFilter());
exception(Exception.class, new LoggingExceptionHandler());
// TODO: The commented out routes

View File

@ -6,7 +6,6 @@ import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.models.AuditLogEntry;
import net.frozenorb.apiv3.models.User;
import org.bson.Document;
import java.util.Map;

View File

@ -18,7 +18,7 @@ public final class ActorAttributeFilter implements Filter {
String mhqAuthHeader = req.headers("MHQ-Authorization");
if (authHeader != null) {
req.attribute("actor", processBasicAuthorization(authHeader));
req.attribute("actor", processBasicAuthorization(authHeader, res));
} else if (mhqAuthHeader != null) {
req.attribute("actor", processMHQAuthorization(mhqAuthHeader));
} else {
@ -27,7 +27,7 @@ public final class ActorAttributeFilter implements Filter {
}
@SuppressWarnings("deprecation") // We purposely get the User by their last username.
private Actor processBasicAuthorization(String authHeader) {
private Actor processBasicAuthorization(String authHeader, Response res) {
String encodedHeader = authHeader.substring("Basic ".length());
String[] credentials = Base64.base64Decode(encodedHeader).split(":");
@ -40,6 +40,7 @@ public final class ActorAttributeFilter implements Filter {
}
}
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\"");
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Failed to authorize.")));
return null;
}

View File

@ -14,7 +14,8 @@ public final class AuthorizationFilter implements Filter {
Actor actor = req.attribute("actor");
if (!actor.isAuthorized()) {
Spark.halt(APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user. You're currently authorized as " + actor.getName())));
res.header("WWW-Authenticate", "Basic realm=\"MineHQ\"");
Spark.halt(401, APIv3.getGson().toJson(ErrorUtils.error("Unauthorized access: Please authenticate as either a server, the website, or an authorized user. You're currently authorized as " + actor.getName())));
}
}

View File

@ -0,0 +1,25 @@
package net.frozenorb.apiv3.filters;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import net.frozenorb.apiv3.APIv3;
import spark.Filter;
import spark.Request;
import spark.Response;
public final class MetricsAfterFilter implements Filter {
private Histogram responseLengthMetric = APIv3.getMetrics().histogram(MetricRegistry.name("apiv3", "http", "responseLength"));
public void handle(Request req, Response res) {
responseLengthMetric.update(req.contentLength());
Timer.Context timerMetricActorType = req.attribute("timerMetric.actorType");
Timer.Context timerMetricGlobal = req.attribute("timerMetric.global");
timerMetricActorType.stop();
timerMetricGlobal.stop();
}
}

View File

@ -0,0 +1,24 @@
package net.frozenorb.apiv3.filters;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor;
import spark.Filter;
import spark.Request;
import spark.Response;
public final class MetricsBeforeFilter implements Filter {
private Timer responseTimesGlobalMetric = APIv3.getMetrics().timer(MetricRegistry.name(getClass(), "responseTimes", "global"));
public void handle(Request req, Response res) {
Actor actor = req.attribute("actor");
String metricName = MetricRegistry.name(getClass(), "responseTimes", actor.getType().name());
Timer responseTimesActorTypeMetric = APIv3.getMetrics().timer(metricName);
req.attribute("timerMetric.global", responseTimesGlobalMetric.time());
req.attribute("timerMetric.actorType", responseTimesActorTypeMetric.time());
}
}

View File

@ -3,8 +3,6 @@ package net.frozenorb.apiv3.models;
import com.google.common.collect.Collections2;
import lombok.Getter;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType;
import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;

View File

@ -1,6 +1,5 @@
package net.frozenorb.apiv3.models;
import com.google.common.collect.ImmutableSet;
import lombok.Getter;
import lombok.Setter;
import net.frozenorb.apiv3.APIv3;

View File

@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import lombok.Setter;
import net.frozenorb.apiv3.APIv3;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;

View File

@ -1,11 +1,6 @@
package net.frozenorb.apiv3.routes.chatFilterList;
import com.google.common.collect.ImmutableSet;
import net.frozenorb.apiv3.actors.Actor;
import net.frozenorb.apiv3.actors.ActorType;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.ServerGroup;
import net.frozenorb.apiv3.utils.ErrorUtils;
import spark.Request;
import spark.Response;
import spark.Route;

View File

@ -7,7 +7,6 @@ import net.frozenorb.apiv3.models.Grant;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.utils.ErrorUtils;
import org.bson.Document;
import spark.Request;
import spark.Response;
import spark.Route;

View File

@ -7,7 +7,6 @@ import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.utils.ErrorUtils;
import org.bson.Document;
import spark.Request;
import spark.Response;
import spark.Route;

View File

@ -2,7 +2,6 @@ package net.frozenorb.apiv3.routes.punishments;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.Punishment;
import net.frozenorb.apiv3.models.Server;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.unsorted.Permissions;
import net.frozenorb.apiv3.utils.ErrorUtils;

View File

@ -3,14 +3,10 @@ package net.frozenorb.apiv3.routes.users;
import com.google.common.collect.ImmutableMap;
import net.frozenorb.apiv3.models.User;
import net.frozenorb.apiv3.utils.ErrorUtils;
import net.frozenorb.apiv3.utils.IPUtils;
import net.frozenorb.apiv3.utils.TOTPUtils;
import spark.Request;
import spark.Response;
import spark.Route;
import java.util.concurrent.TimeUnit;
public final class GETUserVerifyPassword implements Route {
public Object handle(Request req, Response res) {

View File

@ -1,5 +1,6 @@
package net.frozenorb.apiv3.unsorted;
import com.codahale.metrics.Timer;
import lombok.extern.slf4j.Slf4j;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.utils.ErrorUtils;
@ -12,6 +13,12 @@ import spark.Response;
public final class LoggingExceptionHandler implements ExceptionHandler {
public void handle(Exception ex, Request req, Response res) {
Timer.Context timerMetricActorType = req.attribute("timerMetric.actorType");
Timer.Context timerMetricGlobal = req.attribute("timerMetric.global");
timerMetricActorType.stop();
timerMetricGlobal.stop();
String code = new ObjectId().toHexString();
log.error(code + ":", ex);

View File

@ -7,8 +7,6 @@ import com.cribbstechnologies.clients.mandrill.model.MandrillRecipient;
import com.cribbstechnologies.clients.mandrill.request.MandrillMessagesRequest;
import com.twilio.sdk.TwilioRestException;
import com.twilio.sdk.resource.factory.MessageFactory;
import com.twilio.sdk.resource.instance.Message;
import net.frozenorb.apiv3.APIv3;
import net.frozenorb.apiv3.models.NotificationTemplate;
import net.frozenorb.apiv3.utils.MandrillUtils;
import net.frozenorb.apiv3.utils.TwillioUtils;