diff --git a/.github/workflows/maven-publish.yaml b/.github/workflows/maven-publish.yaml
index 60628b97..00402167 100644
--- a/.github/workflows/maven-publish.yaml
+++ b/.github/workflows/maven-publish.yaml
@@ -62,4 +62,14 @@ jobs:
run: |
docker build ./webserver --tag ahmad45123/workup:webserver
docker push ahmad45123/workup:webserver
-
\ No newline at end of file
+
+ - name: Build and push Autoscaler Image
+ run: |
+ docker build ./autoscaler/docker-swarm-autoscaler --tag ahmad45123/workup:autoscaler
+ docker push ahmad45123/workup:autoscaler
+
+ - name: Build and push Mediaserver Image
+ run: |
+ docker build ./mediaserver --tag ahmad45123/workup:mediaserver
+ docker push ahmad45123/workup:mediaserver
+
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index f20e8c93..a2d709a9 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -22,11 +22,11 @@
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index 97e8c0e5..05cc42ad 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -8,6 +8,5 @@
-
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 844a16ee..239fcf5c 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ build:
docker build ./services/users --tag workup:service_users
docker build ./services/contracts --tag workup:service_contracts
docker build ./webserver --tag workup:webserver
+ docker build ./mediaserver --tag workup:mediaserver
up:
docker stack deploy -c compose.yaml -c compose.override.yaml workup
diff --git a/autoscaler/README.md b/autoscaler/README.md
new file mode 100644
index 00000000..b75dad7f
--- /dev/null
+++ b/autoscaler/README.md
@@ -0,0 +1,43 @@
+# docker-swarm-autoscaler
+
+## Current Release: 0.1.0
+
+This project is intended to bring auto service staling to Docker Swarm. This script uses prometheus paired with cadvisor metrics to determine cpu usage. It then uses a manager node to determine if a service wants to be autoscaled and uses a manager node to scale the service.
+
+Currently the project only uses cpu to autoscale. If cpu usage reaches 85% the service will scale up, if it reaches 25% it will scale down.
+
+## Usage
+1. You can deploy prometheus, cadvisor, and docker-swarm-autoscaler by running `docker stack deploy -c swarm-autoscaler-stack.yml autoscaler` from the root of this repo.
+ * You can also utilize an already deploy prometheus and cadvisor by specifying the `PROMETHEUS_URL` in docker-swarm-autoscaler environment. `swarm-autoscaler-stack.yml` shows an example of this.
+ * docker-swarm-autoscale needs a placement contstraint to deploy to a manager. `swarm-autoscaler-stack.yml` shows an example of this.
+2. For services you want to autoscale you will need a deploy label `swarm.autoscaler=true`.
+
+```
+deploy:
+ labels:
+ - "swarm.autoscaler=true"
+```
+
+This is best paired with resource constraints limits. This is also under the deploy key.
+
+```
+deploy:
+ resources:
+ reservations:
+ cpus: '0.25'
+ memory: 512M
+ limits:
+ cpus: '0.50'
+```
+
+## Configuration
+| Setting | Value | Description |
+| --- | --- | --- |
+| `swarm.autoscaler` | `true` | Required. This enables autoscaling for a service. Anything other than `true` will not enable it |
+| `swarm.autoscaler.minimum` | Integer | Optional. This is the minimum number of replicas wanted for a service. The autoscaler will not downscale below this number |
+| `swarm.autoscaler.maximum` | Integer | Optional. This is the maximum number of replicas wanted for a service. The autoscaler will not scale up past this number |
+
+## Test
+You can deploy a test app with the following commands below. Helloworld is initially only 1 replica. The autoscaler will scale to the minimum 3 replicas.
+1. `docker stack deploy -c swarm-autoscaler-stack.yml autoscaler`
+2. `docker stack deploy -c helloworld.yml hello`
\ No newline at end of file
diff --git a/autoscaler/docker-swarm-autoscaler/Dockerfile b/autoscaler/docker-swarm-autoscaler/Dockerfile
new file mode 100644
index 00000000..e9812056
--- /dev/null
+++ b/autoscaler/docker-swarm-autoscaler/Dockerfile
@@ -0,0 +1,26 @@
+FROM ubuntu:xenial
+
+RUN apt-get update -qq \
+ && apt-get install -y -qq \
+ jq \
+ apt-transport-https \
+ ca-certificates \
+ curl \
+ software-properties-common \
+ dnsutils \
+ && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
+ && add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" \
+ && apt-get update -qq \
+ && apt-get install -y -qq \
+ docker-ce=5:19.03.5* \
+ && apt-get -qq clean \
+ && apt-get autoremove -y \
+ && rm -rf \
+ /var/lib/apt/lists/* \
+ /tmp/* \
+ /var/tmp/*
+
+COPY auto-scale.sh /auto-scale.sh
+RUN chmod a+x /auto-scale.sh
+
+CMD ["/auto-scale.sh"]
\ No newline at end of file
diff --git a/autoscaler/docker-swarm-autoscaler/auto-scale.sh b/autoscaler/docker-swarm-autoscaler/auto-scale.sh
new file mode 100644
index 00000000..6f93d647
--- /dev/null
+++ b/autoscaler/docker-swarm-autoscaler/auto-scale.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+LOOP=${LOOP:='yes'}
+CPU_PERCENTAGE_UPPER_LIMIT=60
+CPU_PERCENTAGE_LOWER_LIMIT=25
+PROMETHEUS_API="api/v1/query?query="
+PROMETHEUS_QUERY="sum(rate(container_cpu_usage_seconds_total%7Bcontainer_label_com_docker_swarm_task_name%3D~%27.%2B%27%7D%5B5m%5D))BY(container_label_com_docker_swarm_service_name%2Cinstance)*100"
+
+get_high_cpu_services () {
+ local prometheus_results="${1}"
+ local services=""
+ for service in $(printf "%s$prometheus_results" | jq ".data.result[] | select( all(.value[1]|tonumber; . > $CPU_PERCENTAGE_UPPER_LIMIT) ) | .metric.container_label_com_docker_swarm_service_name" | sed 's/"//g' | sort | uniq); do
+ services="$services $service"
+ done
+ echo $services
+}
+
+get_all_services () {
+ local prometheus_results="${1}"
+ local services=""
+ for service in $(printf "%s$prometheus_results" | jq ".data.result[].metric.container_label_com_docker_swarm_service_name" | sed 's/"//g' | sort | uniq); do
+ services="$services $service"
+ done
+}
+
+get_low_cpu_services () {
+ local prometheus_results="${1}"
+ local services=""
+ for service in $(printf "%s$prometheus_results" | jq ".data.result[] | select( all(.value[1]|tonumber; . < $CPU_PERCENTAGE_LOWER_LIMIT) ) | .metric.container_label_com_docker_swarm_service_name" | sed 's/"//g' | sort | uniq); do
+ services="$services $service"
+ done
+
+ echo $services
+}
+
+default_scale () {
+ service_name=$1
+ auto_scale_label=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler"]')
+ replica_minimum=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler.minimum"]' | sed 's/\"//g')
+ replica_maximum=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler.maximum"]' | sed 's/\"//g')
+ if [[ "${auto_scale_label}" == "\"true\"" ]]; then
+ echo Service $service has an autoscale label.
+ current_replicas=$(docker service inspect $service_name | jq ".[].Spec.Mode.Replicated | .Replicas")
+ if [[ $replica_minimum -gt $current_replicas ]]; then
+ echo Service $service_name is below the minimum. Scaling to the minimum of $replica_minimum
+ docker service scale $service_name=$replica_minimum
+ elif [[ $current_replicas -gt $replica_maximum ]]; then
+ echo Service $service_name is above the maximum. Scaling to the maximum of $replica_maximum
+ docker service scale $service_name=$replica_maximum
+ fi
+ else
+ echo Service $service does not have an autoscale label.
+ fi
+
+}
+
+scale_down () {
+ service_name=$1
+ auto_scale_label=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler"]')
+ replica_minimum=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler.minimum"]' | sed 's/\"//g')
+ if [[ "${auto_scale_label}" == "\"true\"" ]]; then
+ current_replicas=$(docker service inspect $service_name | jq ".[].Spec.Mode.Replicated | .Replicas")
+ new_replicas=$(expr $current_replicas - 1)
+ if [[ $replica_minimum -le $new_replicas ]]; then
+ echo Scaling down the service $service_name to $new_replicas
+ docker service scale $service_name=$new_replicas
+ elif [[ $current_replicas -eq $replica_minimum ]]; then
+ echo Service $service_name has the minumum number of replicas.
+ fi
+ fi
+
+}
+
+scale_up () {
+ service_name=$1
+ auto_scale_label=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler"]')
+ replica_maximum=$(docker service inspect $service_name | jq '.[].Spec.Labels["swarm.autoscaler.maximum"]' | sed 's/\"//g')
+ if [[ "${auto_scale_label}" == "\"true\"" ]]; then
+ current_replicas=$(docker service inspect $service_name | jq ".[].Spec.Mode.Replicated | .Replicas")
+ new_replicas=$(expr $current_replicas + 1)
+ if [[ $current_replicas -eq $replica_maximum ]]; then
+ echo Service $service already has the maximum of $replica_maximum replicas
+ elif [[ $replica_maximum -ge $new_replicas ]]; then
+ echo Scaling up the service $service_name to $new_replicas
+ docker service scale $service_name=$new_replicas
+ fi
+ fi
+}
+
+main () {
+ prometheus_initial_results=$(curl --silent "${PROMETHEUS_URL}/${PROMETHEUS_API}${PROMETHEUS_QUERY}" | jq .)
+ echo Prometheus results
+ echo $prometheus_initial_results
+ for service in $(get_all_services "${prometheus_initial_results}"); do
+ default_scale $service
+ done
+ echo Checking for high cpu services
+ for service in $(get_high_cpu_services "${prometheus_initial_results}"); do
+ echo Service $service is above $CPU_PERCENTAGE_UPPER_LIMIT percent cpu usage.
+ scale_up $service
+ done
+ echo Checking for low cpu services
+ for service in $(get_low_cpu_services "${prometheus_initial_results}"); do
+ echo Service $service is below $CPU_PERCENTAGE_LOWER_LIMIT percent cpu usage.
+ scale_down $service
+ done
+}
+
+main
+while [[ $LOOP == 'yes' ]]; do
+ echo Waiting 5 seconds for the next test
+ sleep 5s
+ main
+done
diff --git a/autoscaler/prometheus.yml b/autoscaler/prometheus.yml
new file mode 100644
index 00000000..1d8988f0
--- /dev/null
+++ b/autoscaler/prometheus.yml
@@ -0,0 +1,18 @@
+global:
+ scrape_interval: 5s
+ evaluation_interval: 5s
+
+scrape_configs:
+ - job_name: 'prometheus'
+ dns_sd_configs:
+ - names:
+ - 'tasks.prometheus'
+ type: 'A'
+ port: 9090
+
+ - job_name: 'cadvisor'
+ dns_sd_configs:
+ - names:
+ - 'tasks.cadvisor'
+ type: 'A'
+ port: 8080
diff --git a/autoscaler/swarm-autoscaler-stack.yml b/autoscaler/swarm-autoscaler-stack.yml
new file mode 100755
index 00000000..0c1010ab
--- /dev/null
+++ b/autoscaler/swarm-autoscaler-stack.yml
@@ -0,0 +1,73 @@
+version: "3.7"
+
+networks:
+ autoscale:
+
+configs:
+ prometheus_config:
+ file: ./prometheus.yml
+
+services:
+ docker-swarm-autoscaler:
+ image: ahmad45123/workup:autoscaler
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ environment:
+ - PROMETHEUS_URL=http://prometheus:9090
+ networks:
+ - autoscale
+ deploy:
+ mode: replicated
+ replicas: 1
+ placement:
+ constraints:
+ - node.role == manager
+ resources:
+ limits:
+ cpus: '0.10'
+ memory: 128M
+ reservations:
+ cpus: '0.10'
+ memory: 64M
+ cadvisor:
+ image: gcr.io/cadvisor/cadvisor
+ networks:
+ - autoscale
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ - /:/rootfs:ro
+ - /var/run:/var/run:ro
+ - /sys:/sys:ro
+ - /var/lib/docker/:/var/lib/docker:ro
+ - /dev/disk/:/dev/disk:ro
+ deploy:
+ mode: global
+ resources:
+ limits:
+ cpus: '0.10'
+ memory: 128M
+ reservations:
+ cpus: '0.10'
+ memory: 64M
+
+ prometheus:
+ image: prom/prometheus:v2.12.0
+ networks:
+ - autoscale
+ command: ["--storage.tsdb.retention.size=1GB", "--config.file=/etc/prometheus/prometheus.yml", "--web.console.libraries=/etc/prometheus/console_libraries", "--web.console.templates=/etc/prometheus/consoles", "--web.enable-lifecycle"]
+ configs:
+ - source: prometheus_config
+ target: /etc/prometheus/prometheus.yml
+ deploy:
+ mode: replicated
+ replicas: 1
+ placement:
+ constraints:
+ - node.role == manager
+ resources:
+ limits:
+ cpus: '0.50'
+ memory: 1024M
+ reservations:
+ cpus: '0.50'
+ memory: 128M
diff --git a/compose.override.yaml b/compose.override.yaml
index 4d005919..b70f434b 100644
--- a/compose.override.yaml
+++ b/compose.override.yaml
@@ -1,4 +1,3 @@
-
version: '3.7'
services:
service_jobs:
@@ -15,3 +14,6 @@ services:
service_webserver:
image: workup:webserver
+
+ service_mediaserver:
+ image: workup:mediaserver
diff --git a/compose.yaml b/compose.yaml
index 25310f90..0700244d 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -8,15 +8,30 @@ services:
image: ahmad45123/workup:webserver
depends_on:
- service_mq
+ - service_mediaserver
ports:
- - "8080:8080"
+ - "80:8080"
networks:
- frontend
env_file:
- ./webserver/.env
+ deploy:
+ labels:
+ swarm.autoscaler: 'true'
+ swarm.autoscaler.minimum: '1'
+ swarm.autoscaler.maximum: '4'
+
+ service_mediaserver:
+ image: ahmad45123/workup:mediaserver
+ ports:
+ - "8080:8080"
+ env_file:
+ - ./mediaserver/.env
service_mq:
image: rabbitmq:3.13-management
+ ports:
+ - "5672:5672" # hacky method.. dont ever do this :(
healthcheck:
test: rabbitmq-diagnostics -q ping
interval: 30s
@@ -25,7 +40,7 @@ services:
networks:
- default
- frontend
-
+
service_redis:
image: redis:7.2.4
healthcheck:
@@ -47,11 +62,14 @@ services:
- jobs
env_file:
- ./services/jobs/.env
+ deploy:
+ labels:
+ swarm.autoscaler: 'true'
+ swarm.autoscaler.minimum: '1'
+ swarm.autoscaler.maximum: '3'
jobs_db:
- image: cassandra:4.0.7
- volumes:
- - ./services/jobs/cassandra-config/cassandra.yaml:/etc/cassandra/cassandra.yaml
+ image: ahmad45123/workup:sasicassandra
healthcheck:
test: [ "CMD", "cqlsh", "-e", "describe keyspaces" ]
interval: 20s
@@ -72,6 +90,11 @@ services:
- payments
env_file:
- ./services/payments/.env
+ deploy:
+ labels:
+ swarm.autoscaler: 'true'
+ swarm.autoscaler.minimum: '1'
+ swarm.autoscaler.maximum: '3'
payments_db:
image: postgres:12.18
@@ -80,7 +103,7 @@ services:
POSTGRES_USER: payments_user
POSTGRES_DB: payments_database
healthcheck:
- test: ["CMD", "pg_isready"]
+ test: [ "CMD", "pg_isready" ]
interval: 20s
timeout: 10s
retries: 10
@@ -98,6 +121,11 @@ services:
- contracts
env_file:
- ./services/contracts/.env
+ deploy:
+ labels:
+ swarm.autoscaler: 'true'
+ swarm.autoscaler.minimum: '1'
+ swarm.autoscaler.maximum: '3'
contracts_db:
image: cassandra:4.0.7
@@ -120,6 +148,11 @@ services:
- users
env_file:
- ./services/users/.env
+ deploy:
+ labels:
+ swarm.autoscaler: 'true'
+ swarm.autoscaler.minimum: '1'
+ swarm.autoscaler.maximum: '3'
users_db:
image: mongo:7.0
diff --git a/controller/.env b/controller/.env
new file mode 100644
index 00000000..3fedf8df
--- /dev/null
+++ b/controller/.env
@@ -0,0 +1,4 @@
+JOBS_MQ_URL=service_mq
+JOBS_MQ_USER=guest
+JOBS_MQ_PASSWORD=guest
+JOBS_MQ_PORT=guest
\ No newline at end of file
diff --git a/controller/Dockerfile b/controller/Dockerfile
new file mode 100644
index 00000000..6730ed34
--- /dev/null
+++ b/controller/Dockerfile
@@ -0,0 +1,5 @@
+FROM eclipse-temurin:21-jre-alpine
+VOLUME /tmp
+ARG JAR_FILE=target/*.jar
+COPY ${JAR_FILE} controller.jar
+ENTRYPOINT ["tail", "-f", "/dev/null"]
\ No newline at end of file
diff --git a/controller/pom.xml b/controller/pom.xml
index c2035f0c..f5ccdbdc 100644
--- a/controller/pom.xml
+++ b/controller/pom.xml
@@ -20,16 +20,57 @@
org.springframework.bootspring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ com.workup
+ jobs
+ ${project.version}
+
+
+ org.testcontainers
+ cassandra
+
+
+ org.springframework.boot
+ spring-boot-starter-data-cassandra
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
-
org.springframework.bootspring-boot-starter-test
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+ testorg.springframework.bootspring-boot-starter-amqp
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ javassist
+ javassist
+ 3.12.1.GAcom.workup
@@ -41,6 +82,10 @@
cliche110413
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
diff --git a/controller/src/main/java/com/workup/controller/CLIHandler.java b/controller/src/main/java/com/workup/controller/CLIHandler.java
index 69be21e8..9e0200fe 100644
--- a/controller/src/main/java/com/workup/controller/CLIHandler.java
+++ b/controller/src/main/java/com/workup/controller/CLIHandler.java
@@ -2,10 +2,15 @@
import asg.cliche.CLIException;
import asg.cliche.Command;
-import com.workup.shared.commands.controller.SetMaxThreadsRequest;
+import com.workup.shared.commands.controller.*;
+import com.workup.shared.commands.jobs.requests.CreateJobRequest;
import com.workup.shared.enums.ControllerQueueNames;
+import com.workup.shared.enums.ServiceQueueNames;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import javassist.*;
+import org.apache.logging.log4j.Level;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,7 +26,7 @@ public class CLIHandler {
}
@Command(description = "Set the maximum number of threads for a specific app")
- public String maxthreads(String app, int maxThreads) throws CLIException {
+ public String maxThreads(String app, int maxThreads) throws CLIException {
app = app.toLowerCase();
if (!appQueueMap.containsKey(app)) {
return "Error: app can only be jobs, users, contracts or payments!";
@@ -33,22 +38,44 @@ public String maxthreads(String app, int maxThreads) throws CLIException {
appQueueMap.get(app),
"",
SetMaxThreadsRequest.builder().withMaxThreads(maxThreads).build());
- return "MaxThreads";
+ return "Command sent";
}
@Command(description = "Set the maximum number of DB connections for a specific app")
- public String maxdb(String app, int appNum, String maxDBConn) {
- return "maxdb";
+ public String maxdb(String app, int maxDBConn) {
+ app = app.toLowerCase();
+ if (!appQueueMap.containsKey(app)) {
+ return "Error: app can only be jobs, users, contracts or payments!";
+ }
+ if (maxDBConn > 100 || maxDBConn < 1) {
+ return "Error: Max threads must have a value between 1 and 100";
+ }
+ rabbitTemplate.convertAndSend(
+ appQueueMap.get(app),
+ "",
+ SetMaxDBConnectionsRequest.builder().withMaxDBConnections(maxDBConn).build());
+ return "Command Sent!";
}
@Command(description = "starts a specific app")
- public String start(String app, int appNum) {
- return "start";
+ public String start(String app) {
+ app = app.toLowerCase();
+ if (!appQueueMap.containsKey(app)) {
+ return "Error: app can only be jobs, users, contracts or payments!";
+ }
+
+ rabbitTemplate.convertAndSend(appQueueMap.get(app), "", ContinueRequest.builder().build());
+ return "Command sent";
}
@Command(description = "stops a specific app")
- public String freeze(String app, int appNum) {
- return "freeze";
+ public String freeze(String app) {
+ app = app.toLowerCase();
+ if (!appQueueMap.containsKey(app)) {
+ return "Error: app can only be jobs, users, contracts or payments!";
+ }
+ rabbitTemplate.convertAndSend(appQueueMap.get(app), "", FreezeRequest.builder().build());
+ return "Command sent";
}
@Command(description = "stops a specific app")
@@ -57,8 +84,22 @@ public String setmq(String app, int appNum) {
}
@Command(description = "stops a specific app")
- public String setErrorReportingLevel(String app, int appNum) {
- return "error level";
+ public String setLoggingLevel(String app, String level) {
+ app = app.toLowerCase();
+ if (!appQueueMap.containsKey(app)) {
+ return "Error: app can only be jobs, users, contracts or payments!";
+ }
+ // To throw an error in case an invalid level is provided :)
+ Level.valueOf(level);
+ rabbitTemplate.convertAndSend(
+ appQueueMap.get(app), "", SetLoggingLevelRequest.builder().withLevel(level).build());
+ return "Command sent!!";
+ }
+
+ @Command(description = "test")
+ public void test() {
+ CreateJobRequest request = CreateJobRequest.builder().withTitle("Ziko").build();
+ rabbitTemplate.convertSendAndReceive(ServiceQueueNames.JOBS, request);
}
@Command(description = "Creates a new command")
@@ -67,8 +108,37 @@ public String addcommand(String app, String commandName, String className) {
}
@Command(description = "Updates an existing command")
- public String updatecommand(String app, String commandName, String className) {
- return "Update Command";
+ public String updateCommand(String app, String commandName, String className) throws Exception {
+ app = app.toLowerCase();
+ if (!appQueueMap.containsKey(app)) {
+ return "Error: app can only be jobs, users, contracts or payments!";
+ }
+ try {
+ byte[] byteArray = getByteCode(commandName, className);
+ rabbitTemplate.convertAndSend(
+ appQueueMap.get(app),
+ "",
+ UpdateCommandRequest.builder()
+ .withCommandName(commandName)
+ .withByteCode(byteArray)
+ .build());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return "Command sent!!";
+ }
+
+ private byte[] getByteCode(String commandName, String className)
+ throws InstantiationException,
+ IllegalAccessException,
+ NotFoundException,
+ IOException,
+ CannotCompileException {
+ ClassPool pool = ClassPool.getDefault();
+ pool.insertClassPath(new ClassClassPath(ControllerApplication.class));
+ CtClass ctClass = pool.get(className);
+ // That's the compiled class byte code
+ return ctClass.toBytecode();
}
@Command(description = "Deletes an existing command")
diff --git a/mediaserver/.dockerignore b/mediaserver/.dockerignore
new file mode 100644
index 00000000..3ee47320
--- /dev/null
+++ b/mediaserver/.dockerignore
@@ -0,0 +1,3 @@
+Dockerfile
+node_modules
+npm-debug.log
\ No newline at end of file
diff --git a/mediaserver/.env b/mediaserver/.env
new file mode 100644
index 00000000..1724b3dc
--- /dev/null
+++ b/mediaserver/.env
@@ -0,0 +1,3 @@
+UPLOAD_USER=user
+UPLOAD_PASSWORD=password
+DISCOGS_API_KEY=123
diff --git a/mediaserver/.gitignore b/mediaserver/.gitignore
new file mode 100644
index 00000000..da7520d7
--- /dev/null
+++ b/mediaserver/.gitignore
@@ -0,0 +1,4 @@
+/node_modules
+.env
+/media/static/icons/*
+/media/static/resume/*
\ No newline at end of file
diff --git a/mediaserver/Dockerfile b/mediaserver/Dockerfile
new file mode 100644
index 00000000..51501359
--- /dev/null
+++ b/mediaserver/Dockerfile
@@ -0,0 +1,26 @@
+# Use latest slim node
+FROM node:current-slim
+
+# Set the working directory
+WORKDIR /www
+
+# Copy package.json and package-lock.json to the working directory
+COPY package*.json ./
+
+# Install modules for production
+RUN npm install --production
+
+# Copy entire bundle to the working directory
+COPY . .
+
+# Create a mount point marked as containing externally mounted volumes.
+# Prevents the container's size from increasing with the addition of more static media assets.
+# Can be overridden by binding a host directory at runtime.
+VOLUME /www/media/static
+
+# Override and expose port 8080
+ENV PORT 8080
+EXPOSE 8080
+
+# Start the server
+CMD ["npm", "run", "start"]
\ No newline at end of file
diff --git a/mediaserver/README.md b/mediaserver/README.md
new file mode 100644
index 00000000..2fefae44
--- /dev/null
+++ b/mediaserver/README.md
@@ -0,0 +1,112 @@
+# `media-server`
+
+A media server that serves static resources.
+
+## ENV
+
+- `UPLOAD_USER`
+- `UPLOAD_PASSWORD`
+- `DISCOGS_API_KEY`
+
+
+## Static Endpoints
+
+For all static resources, this server will attempt to return a relevant resource, or else if the resource does not exist, it will return a default 'placeholder' resource. This prevents clients from having no resource to display at all; clients can make use of this media server's 'describe' endpoint to learn about what resources are available.
+
+### `GET` /static/icons/:icon.png
+
+Returns an icon based on the filename.
+
+`:icon` can be any icon filename.
+
+Example: return an icon with filename 'accept.png'.
+
+```bash
+curl --request GET \
+ --url http://path_to_server/static/icons/accept.png
+```
+
+### `GET` /static/resume/:resume.pdf
+
+Returns a resume based on the filename.
+
+`:resume` can be any resume filename.
+
+Example: return a resume with filename 'resume.pdf'.
+
+```bash
+curl --request GET \
+ --url http://path_to_server/static/resume/resume.pdf
+```
+
+
+## Describe
+
+### `GET` /describe
+
+Returns a JSON representation of the media groups.
+
+Example: return JSON containing of all groups present.
+
+```bash
+curl --request GET \
+ --url http://localhost:8910/describe
+```
+
+```json
+{
+ "success": true,
+ "path": "/static/",
+ "groups": ["icons", "resume"]
+}
+```
+
+### `GET` /describe/:group
+
+Returns a JSON representation of all the files current present for a given group.
+
+`:group` can be any valid group.
+
+Example: return JSON containing all the media resources for a the exec resource group.
+
+```bash
+curl --request GET \
+ --url http://localhost:8910/describe/exec
+```
+
+```json
+{
+ "success": true,
+ "path": "/static/exec/",
+ "mimeType": "image/jpeg",
+ "files": []
+}
+```
+
+## Upload
+
+Upload and convert media to any of the given static resource group.
+
+All upload routes are protected by basic HTTP auth. The credentials are defined by ENV variables `UPLOAD_USER` and `UPLOAD_PASSWORD`.
+
+### `POST` /upload/:group
+
+POST a resource to a given group, assigning that resource a given filename.
+
+`:group` can be any valid group.
+
+```bash
+curl --location 'http://path-to-server/upload/resume/' \
+--header 'Authorization: Basic dXNlcjpwYXNz' \
+--form 'resource=@"/C:/Users/ibrah/Downloads/Ibrahim_Abou_Elenein_Resume.pdf"' \
+--form 'filename="aboueleyes-reume-2"'
+```
+
+```json
+{
+ "success": true,
+ "path": "/static/resume/aboueleyes-reume-2.pdf"
+}
+```
+
+A resource at `http://path_to_server/static/resume/aboueleyes-reume-2.pdf` will now be available.
diff --git a/mediaserver/index.js b/mediaserver/index.js
new file mode 100644
index 00000000..58767da9
--- /dev/null
+++ b/mediaserver/index.js
@@ -0,0 +1,20 @@
+/**
+ * Load env variables.
+ */
+require("dotenv").config();
+
+/**
+ * Load the server config constants.
+ */
+const config = require("./src/config");
+
+/**
+ * Initiate the app.
+ */
+const app = require("./src/app");
+
+/**
+ * Define port, and listen...
+ */
+const port = config.PORT;
+app.listen(port, () => console.log("listening on port " + port));
diff --git a/mediaserver/media/defaults/raw-banner.gif b/mediaserver/media/defaults/raw-banner.gif
new file mode 100644
index 00000000..c7d18b48
Binary files /dev/null and b/mediaserver/media/defaults/raw-banner.gif differ
diff --git a/mediaserver/media/defaults/raw-banner.jpg b/mediaserver/media/defaults/raw-banner.jpg
new file mode 100644
index 00000000..cdffccdd
Binary files /dev/null and b/mediaserver/media/defaults/raw-banner.jpg differ
diff --git a/mediaserver/media/defaults/raw-banner.png b/mediaserver/media/defaults/raw-banner.png
new file mode 100644
index 00000000..6d3de496
Binary files /dev/null and b/mediaserver/media/defaults/raw-banner.png differ
diff --git a/mediaserver/media/defaults/raw-icon.gif b/mediaserver/media/defaults/raw-icon.gif
new file mode 100644
index 00000000..b4194b77
Binary files /dev/null and b/mediaserver/media/defaults/raw-icon.gif differ
diff --git a/mediaserver/media/defaults/raw-icon.jpg b/mediaserver/media/defaults/raw-icon.jpg
new file mode 100644
index 00000000..1d8f6b4e
Binary files /dev/null and b/mediaserver/media/defaults/raw-icon.jpg differ
diff --git a/mediaserver/media/defaults/raw-icon.png b/mediaserver/media/defaults/raw-icon.png
new file mode 100644
index 00000000..da1cb022
Binary files /dev/null and b/mediaserver/media/defaults/raw-icon.png differ
diff --git a/mediaserver/media/defaults/raw-logo-only.gif b/mediaserver/media/defaults/raw-logo-only.gif
new file mode 100644
index 00000000..ae7f356e
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo-only.gif differ
diff --git a/mediaserver/media/defaults/raw-logo-only.jpg b/mediaserver/media/defaults/raw-logo-only.jpg
new file mode 100644
index 00000000..77aa47d0
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo-only.jpg differ
diff --git a/mediaserver/media/defaults/raw-logo-only.png b/mediaserver/media/defaults/raw-logo-only.png
new file mode 100644
index 00000000..a210f7b1
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo-only.png differ
diff --git a/mediaserver/media/defaults/raw-logo.gif b/mediaserver/media/defaults/raw-logo.gif
new file mode 100644
index 00000000..1aafd6dc
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo.gif differ
diff --git a/mediaserver/media/defaults/raw-logo.jpg b/mediaserver/media/defaults/raw-logo.jpg
new file mode 100644
index 00000000..56fa6fb5
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo.jpg differ
diff --git a/mediaserver/media/defaults/raw-logo.png b/mediaserver/media/defaults/raw-logo.png
new file mode 100644
index 00000000..1bc1c934
Binary files /dev/null and b/mediaserver/media/defaults/raw-logo.png differ
diff --git a/mediaserver/media/static/icons/aboueleyes.png b/mediaserver/media/static/icons/aboueleyes.png
new file mode 100644
index 00000000..7a6022ad
Binary files /dev/null and b/mediaserver/media/static/icons/aboueleyes.png differ
diff --git a/mediaserver/media/static/resume/aboueleyes-reume-2.pdf b/mediaserver/media/static/resume/aboueleyes-reume-2.pdf
new file mode 100644
index 00000000..52faa0e7
Binary files /dev/null and b/mediaserver/media/static/resume/aboueleyes-reume-2.pdf differ
diff --git a/mediaserver/media/static/resume/aboueleyes-reume.pdf b/mediaserver/media/static/resume/aboueleyes-reume.pdf
new file mode 100644
index 00000000..32e5d7f5
Binary files /dev/null and b/mediaserver/media/static/resume/aboueleyes-reume.pdf differ
diff --git a/mediaserver/package-lock.json b/mediaserver/package-lock.json
new file mode 100644
index 00000000..288f7d7b
--- /dev/null
+++ b/mediaserver/package-lock.json
@@ -0,0 +1,2765 @@
+{
+ "name": "media-server",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "media-server",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^0.18.1",
+ "cron": "^1.7.0",
+ "dotenv": "^7.0.0",
+ "fs-extra": "^7.0.1",
+ "jimp": "^0.6.4",
+ "koa": "^2.7.0",
+ "koa-basic-auth": "^4.0.0",
+ "koa-body": "^4.1.1",
+ "koa-compress": "^3.0.0",
+ "koa-mount": "^4.0.0",
+ "koa-router": "^7.4.0",
+ "koa-sendfile": "^3.0.0",
+ "koa-static": "^5.0.0"
+ }
+ },
+ "node_modules/@jimp/bmp": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz",
+ "integrity": "sha512-uxVgSkI62uAzk5ZazYHEHBehow590WAkLKmDXLzkr/XP/Hv2Fx1T4DKwJ/15IY5ktq5VAhAUWGXTyd8KWFsx7w==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "bmp-js": "^0.1.0",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/core": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.6.8.tgz",
+ "integrity": "sha512-JOFqBBcSNiDiMZJFr6OJqC6viXj5NVBQISua0eacoYvo4YJtTajOIxC4MqWyUmGrDpRMZBR8QhSsIOwsFrdROA==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "any-base": "^1.1.0",
+ "buffer": "^5.2.0",
+ "core-js": "^2.5.7",
+ "exif-parser": "^0.1.12",
+ "file-type": "^9.0.0",
+ "load-bmfont": "^1.3.1",
+ "mkdirp": "0.5.1",
+ "phin": "^2.9.1",
+ "pixelmatch": "^4.0.2",
+ "tinycolor2": "^1.4.1"
+ }
+ },
+ "node_modules/@jimp/core/node_modules/file-type": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz",
+ "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@jimp/custom": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.8.tgz",
+ "integrity": "sha512-FrYlzZRVXP2vuVwd7Nc2dlK+iZk4g6IaT1Ib8Z6vU5Kkwlt83FJIPJ2UUFABf3bF5big0wkk8ZUihWxE4Nzdng==",
+ "dependencies": {
+ "@jimp/core": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "node_modules/@jimp/gif": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.8.tgz",
+ "integrity": "sha512-yyOlujjQcgz9zkjM5ihZDEppn9d1brJ7jQHP5rAKmqep0G7FU1D0AKcV+Ql18RhuI/CgWs10wAVcrQpmLnu4Yw==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "omggif": "^1.0.9"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/jpeg": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.8.tgz",
+ "integrity": "sha512-rGtXbYpFXAn471qLpTGvhbBMNHJo5KiufN+vC5AWyufntmkt5f0Ox2Cx4ijuBMDtirZchxbMLtrfGjznS4L/ew==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "jpeg-js": "^0.3.4"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-blit": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz",
+ "integrity": "sha512-7Tl6YpKTSpvwQbnGNhsfX2zyl3jRVVopd276Y2hF2zpDz9Bycow7NdfNU/4Nx1jaf96X6uWOtSVINcQ7rGd47w==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-blur": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz",
+ "integrity": "sha512-NpZCMKxXHLDQsX9zPlWtpMA660DQStY6/z8ZetyxCDbqrLe9YCXpeR4MNhdJdABIiwTm1W5FyFF4kp81PHJx3Q==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-color": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.8.tgz",
+ "integrity": "sha512-jjFyU0zNmGOH2rjzHuOMU4kaia0oo82s/7UYfn5h7OUkmUZTd6Do3ZSK1PiXA7KR+s4B76/Omm6Doh/0SGb7BQ==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "tinycolor2": "^1.4.1"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-contain": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz",
+ "integrity": "sha512-p/P2wCXhAzbmEgXvGsvmxLmbz45feF6VpR4m9suPSOr8PC/i/XvTklTqYEUidYYAft4vHgsYJdS74HKSMnH8lw==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-blit": ">=0.3.5",
+ "@jimp/plugin-resize": ">=0.3.5",
+ "@jimp/plugin-scale": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-cover": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz",
+ "integrity": "sha512-2PvWgk+PJfRsfWDI1G8Fpjrsu0ZlpNyZxO2+fqWlVo6y/y2gP4v08FqvbkcqSjNlOu2IDWIFXpgyU0sTINWZLg==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-crop": ">=0.3.5",
+ "@jimp/plugin-resize": ">=0.3.5",
+ "@jimp/plugin-scale": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-crop": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz",
+ "integrity": "sha512-CbrcpWE2xxPK1n/JoTXzhRUhP4mO07mTWaSavenCg664oQl/9XCtL+A0FekuNHzIvn4myEqvkiTwN7FsbunS/Q==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-displace": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz",
+ "integrity": "sha512-RmV2bPxoPE6mrPxtYSPtHxm2cGwBQr5a2p+9gH6SPy+eUMrbGjbvjwKNfXWUYD0leML+Pt5XOmAS9pIROmuruQ==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-dither": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz",
+ "integrity": "sha512-x6V/qjxe+xypjpQm7GbiMNqci1EW5UizrcebOhHr8AHijOEqHd2hjXh5f6QIGfrkTFelc4/jzq1UyCsYntqz9Q==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-flip": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz",
+ "integrity": "sha512-4il6Da6G39s9MyWBEee4jztEOUGJ40E6OlPjkMrdpDNvge6hYEAB31BczTYBP/CEY74j4LDSoY5LbcU4kv06yA==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-rotate": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-gaussian": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz",
+ "integrity": "sha512-pVOblmjv7stZjsqloi4YzHVwAPXKGdNaHPhp4KP4vj41qtc6Hxd9z/+VWGYRTunMFac84gUToe0UKIXd6GhoKw==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-invert": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz",
+ "integrity": "sha512-11zuLiXDHr6tFv4U8aieXqNXQEKbDbSBG/h+X62gGTNFpyn8EVPpncHhOqrAFtZUaPibBqMFlNJ15SzwC7ExsQ==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-mask": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz",
+ "integrity": "sha512-hZJ0OiKGJyv7hDSATwJDkunB1Ie80xJnONMgpUuUseteK45YeYNBOiZVUe8vum8QI1UwavgBzcvQ9u4fcgXc9g==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-normalize": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz",
+ "integrity": "sha512-Q4oYhU+sSyTJI7pMZlg9/mYh68ujLfOxXzQGEXuw0sHGoGQs3B0Jw7jmzGa6pIS06Hup5hD2Zuh1ppvMdjJBfQ==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-print": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.8.tgz",
+ "integrity": "sha512-2aokejGn4Drv1FesnZGqh5KEq0FQtR0drlmtyZrBH+r9cx7hh0Qgf4D1BOTDEgXkfSSngjGRjKKRW/fwOrVXYw==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "load-bmfont": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-blit": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-resize": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz",
+ "integrity": "sha512-27nPh8L1YWsxtfmV/+Ub5dOTpXyC0HMF2cu52RQSCYxr+Lm1+23dJF70AF1poUbUe+FWXphwuUxQzjBJza9UoA==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-rotate": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz",
+ "integrity": "sha512-GbjETvL05BDoLdszNUV4Y0yLkHf177MnqGqilA113LIvx9aD0FtUopGXYfRGVvmtTOTouoaGJUc+K6qngvKxww==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-blit": ">=0.3.5",
+ "@jimp/plugin-crop": ">=0.3.5",
+ "@jimp/plugin-resize": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugin-scale": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz",
+ "integrity": "sha512-GzIYWR/oCUK2jAwku23zt19V1ssaEU4pL0x2XsLNKuuJEU6DvEytJyTMXCE7OLG/MpDBQcQclJKHgiyQm5gIOQ==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5",
+ "@jimp/plugin-resize": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/plugins": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.8.tgz",
+ "integrity": "sha512-fMcTI72Vn/Lz6JftezTURmyP5ml/xGMe0Ljx2KRJ85IWyP33vDmGIUuutFiBEbh2+y7lRT+aTSmjs0QGa/xTmQ==",
+ "dependencies": {
+ "@jimp/plugin-blit": "^0.6.8",
+ "@jimp/plugin-blur": "^0.6.8",
+ "@jimp/plugin-color": "^0.6.8",
+ "@jimp/plugin-contain": "^0.6.8",
+ "@jimp/plugin-cover": "^0.6.8",
+ "@jimp/plugin-crop": "^0.6.8",
+ "@jimp/plugin-displace": "^0.6.8",
+ "@jimp/plugin-dither": "^0.6.8",
+ "@jimp/plugin-flip": "^0.6.8",
+ "@jimp/plugin-gaussian": "^0.6.8",
+ "@jimp/plugin-invert": "^0.6.8",
+ "@jimp/plugin-mask": "^0.6.8",
+ "@jimp/plugin-normalize": "^0.6.8",
+ "@jimp/plugin-print": "^0.6.8",
+ "@jimp/plugin-resize": "^0.6.8",
+ "@jimp/plugin-rotate": "^0.6.8",
+ "@jimp/plugin-scale": "^0.6.8",
+ "core-js": "^2.5.7",
+ "timm": "^1.6.1"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/png": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.6.8.tgz",
+ "integrity": "sha512-JHHg/BZ7KDtHQrcG+a7fztw45rdf7okL/YwkN4qU5FH7Fcrp41nX5QnRviDtD9hN+GaNC7kvjvcqRAxW25qjew==",
+ "dependencies": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "pngjs": "^3.3.3"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/tiff": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.8.tgz",
+ "integrity": "sha512-iWHbxd+0IKWdJyJ0HhoJCGYmtjPBOusz1z1HT/DnpePs/Lo3TO4d9ALXqYfUkyG74ZK5jULZ69KLtwuhuJz1bg==",
+ "dependencies": {
+ "core-js": "^2.5.7",
+ "utif": "^2.0.1"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/types": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.6.8.tgz",
+ "integrity": "sha512-vCZ/Cp2osy69VP21XOBACfHI5HeR60Rfd4Jidj4W73UL+HrFWOtyQiJ7hlToyu1vI5mR/NsUQpzyQvz56ADm5A==",
+ "dependencies": {
+ "@jimp/bmp": "^0.6.8",
+ "@jimp/gif": "^0.6.8",
+ "@jimp/jpeg": "^0.6.8",
+ "@jimp/png": "^0.6.8",
+ "@jimp/tiff": "^0.6.8",
+ "core-js": "^2.5.7",
+ "timm": "^1.6.1"
+ },
+ "peerDependencies": {
+ "@jimp/custom": ">=0.3.5"
+ }
+ },
+ "node_modules/@jimp/utils": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.8.tgz",
+ "integrity": "sha512-7RDfxQ2C/rarNG9iso5vmnKQbcvlQjBIlF/p7/uYj72WeZgVCB+5t1fFBKJSU4WhniHX4jUMijK+wYGE3Y3bGw==",
+ "dependencies": {
+ "core-js": "^2.5.7"
+ }
+ },
+ "node_modules/@types/events": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
+ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
+ },
+ "node_modules/@types/formidable": {
+ "version": "1.0.31",
+ "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
+ "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==",
+ "dependencies": {
+ "@types/events": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "13.9.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.5.tgz",
+ "integrity": "sha512-hkzMMD3xu6BrJpGVLeQ3htQQNAcOrJjX7WFmtK8zWQpz2UJf13LCFF2ALA7c9OVdvc2vQJeDdjfR35M0sBCxvw=="
+ },
+ "node_modules/accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "dependencies": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/any-base": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
+ "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+ },
+ "node_modules/axios": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
+ "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
+ "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
+ "dependencies": {
+ "follow-redirects": "1.5.10",
+ "is-buffer": "^2.0.2"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/basic-auth/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/bmp-js": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+ "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM="
+ },
+ "node_modules/buffer": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz",
+ "integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "node_modules/buffer-equal": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+ "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cache-content-type": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz",
+ "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==",
+ "dependencies": {
+ "mime-types": "^2.1.18",
+ "ylru": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "engines": {
+ "iojs": ">= 1.0.0",
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/co-body": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/co-body/-/co-body-5.2.0.tgz",
+ "integrity": "sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ==",
+ "dependencies": {
+ "inflation": "^2.0.0",
+ "qs": "^6.4.0",
+ "raw-body": "^2.2.0",
+ "type-is": "^1.6.14"
+ }
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-disposition/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookies": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
+ "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "keygrip": "~1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cookies/node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
+ "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+ "hasInstallScript": true
+ },
+ "node_modules/cron": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz",
+ "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==",
+ "dependencies": {
+ "moment-timezone": "^0.5.x"
+ }
+ },
+ "node_modules/debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+ },
+ "node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "node_modules/dom-walk": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
+ "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
+ },
+ "node_modules/dotenv": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz",
+ "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/error-inject": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz",
+ "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc="
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/exif-parser": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
+ "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI="
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "dependencies": {
+ "debug": "=3.1.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
+ "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==",
+ "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/global": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
+ "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
+ "dependencies": {
+ "min-document": "^2.19.0",
+ "process": "~0.5.1"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
+ },
+ "node_modules/http-assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz",
+ "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==",
+ "dependencies": {
+ "deep-equal": "~1.0.1",
+ "http-errors": "~1.7.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
+ "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+ },
+ "node_modules/inflation": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz",
+ "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/is-function": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz",
+ "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU="
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz",
+ "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jimp": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.6.8.tgz",
+ "integrity": "sha512-F7emeG7Hp61IM8VFbNvWENLTuHe0ghizWPuP4JS9ujx2r5mCVYEd/zdaz6M2M42ZdN41blxPajLWl9FXo7Mr2Q==",
+ "dependencies": {
+ "@jimp/custom": "^0.6.8",
+ "@jimp/plugins": "^0.6.8",
+ "@jimp/types": "^0.6.8",
+ "core-js": "^2.5.7",
+ "regenerator-runtime": "^0.13.3"
+ }
+ },
+ "node_modules/jpeg-js": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz",
+ "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ=="
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/keygrip": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+ "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
+ "dependencies": {
+ "tsscmp": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/koa": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/koa/-/koa-2.11.0.tgz",
+ "integrity": "sha512-EpR9dElBTDlaDgyhDMiLkXrPwp6ZqgAIBvhhmxQ9XN4TFgW+gEz6tkcsNI6BnUbUftrKDjVFj4lW2/J2aNBMMA==",
+ "dependencies": {
+ "accepts": "^1.3.5",
+ "cache-content-type": "^1.0.0",
+ "content-disposition": "~0.5.2",
+ "content-type": "^1.0.4",
+ "cookies": "~0.8.0",
+ "debug": "~3.1.0",
+ "delegates": "^1.0.0",
+ "depd": "^1.1.2",
+ "destroy": "^1.0.4",
+ "encodeurl": "^1.0.2",
+ "error-inject": "^1.0.0",
+ "escape-html": "^1.0.3",
+ "fresh": "~0.5.2",
+ "http-assert": "^1.3.0",
+ "http-errors": "^1.6.3",
+ "is-generator-function": "^1.0.7",
+ "koa-compose": "^4.1.0",
+ "koa-convert": "^1.2.0",
+ "on-finished": "^2.3.0",
+ "only": "~0.0.2",
+ "parseurl": "^1.3.2",
+ "statuses": "^1.5.0",
+ "type-is": "^1.6.16",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"
+ }
+ },
+ "node_modules/koa-basic-auth": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/koa-basic-auth/-/koa-basic-auth-4.0.0.tgz",
+ "integrity": "sha512-eV1sGVAizDuFWNpY43VF3Z1ND4PotQZB/igxHNrcJXzXw+Flmj8Uv+4hP9LyNXyvqLJz/X5bmXeMu84AAGD9Jw==",
+ "dependencies": {
+ "basic-auth": "^2.0.0",
+ "tsscmp": "^1.0.6"
+ }
+ },
+ "node_modules/koa-body": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/koa-body/-/koa-body-4.1.1.tgz",
+ "integrity": "sha512-rLb/KVD8qplEcK8Qsu6F4Xw+uHkmx3MWogDVmMX07DpjXizhw3pOEp1ja1MqqAcl0ei75AsrbGVDlySmsUrreA==",
+ "dependencies": {
+ "@types/formidable": "^1.0.31",
+ "co-body": "^5.1.1",
+ "formidable": "^1.1.1"
+ }
+ },
+ "node_modules/koa-compose": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
+ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw=="
+ },
+ "node_modules/koa-compress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/koa-compress/-/koa-compress-3.0.0.tgz",
+ "integrity": "sha512-xol+LkNB1mozKJkB5Kj6nYXbJXhkLkZlXl9BsGBPjujVfZ8MsIXwU4GHRTT7TlSfUcl2DU3JtC+j6wOWcovfuQ==",
+ "dependencies": {
+ "bytes": "^3.0.0",
+ "compressible": "^2.0.0",
+ "koa-is-json": "^1.0.0",
+ "statuses": "^1.0.0"
+ }
+ },
+ "node_modules/koa-convert": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz",
+ "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=",
+ "dependencies": {
+ "co": "^4.6.0",
+ "koa-compose": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/koa-convert/node_modules/koa-compose": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz",
+ "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=",
+ "dependencies": {
+ "any-promise": "^1.1.0"
+ }
+ },
+ "node_modules/koa-is-json": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz",
+ "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ="
+ },
+ "node_modules/koa-mount": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.0.0.tgz",
+ "integrity": "sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==",
+ "dependencies": {
+ "debug": "^4.0.1",
+ "koa-compose": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 7.6.0"
+ }
+ },
+ "node_modules/koa-mount/node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/koa-mount/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/koa-router": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz",
+ "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==",
+ "deprecated": "**IMPORTANT 10x+ PERFORMANCE UPGRADE**: Please upgrade to v12.0.1+ as we have fixed an issue with debuglog causing 10x slower router benchmark performance, see https://github.com/koajs/router/pull/173",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "http-errors": "^1.3.1",
+ "koa-compose": "^3.0.0",
+ "methods": "^1.0.1",
+ "path-to-regexp": "^1.1.1",
+ "urijs": "^1.19.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/koa-router/node_modules/koa-compose": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz",
+ "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=",
+ "dependencies": {
+ "any-promise": "^1.1.0"
+ }
+ },
+ "node_modules/koa-send": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz",
+ "integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "http-errors": "^1.6.3",
+ "mz": "^2.7.0",
+ "resolve-path": "^1.4.0"
+ },
+ "engines": {
+ "node": ">= 7.6.0"
+ }
+ },
+ "node_modules/koa-sendfile": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/koa-sendfile/-/koa-sendfile-3.0.0.tgz",
+ "integrity": "sha512-wvvvDQIh7eNFRm/0AZNYtL+gl7uSG7qC4r0b7yJAQUgOh40yRW6hLjUWJPD09aa8t1c077X5G+pzOK9+WuF4Eg==",
+ "dependencies": {
+ "debug": "^4.2.0",
+ "etag": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/koa-sendfile/node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/koa-sendfile/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/koa-static": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
+ "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "koa-send": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 7.6.0"
+ }
+ },
+ "node_modules/load-bmfont": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz",
+ "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==",
+ "dependencies": {
+ "buffer-equal": "0.0.1",
+ "mime": "^1.3.4",
+ "parse-bmfont-ascii": "^1.0.3",
+ "parse-bmfont-binary": "^1.0.5",
+ "parse-bmfont-xml": "^1.1.4",
+ "phin": "^2.9.1",
+ "xhr": "^2.0.1",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.26",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "dependencies": {
+ "mime-db": "1.43.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/min-document": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+ "dependencies": {
+ "dom-walk": "^0.1.0"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
+ "dependencies": {
+ "minimist": "0.0.8"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/moment-timezone": {
+ "version": "0.5.28",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz",
+ "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
+ "dependencies": {
+ "moment": ">= 2.9.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/omggif": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
+ "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="
+ },
+ "node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/only": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
+ "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q="
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "node_modules/parse-bmfont-ascii": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
+ "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU="
+ },
+ "node_modules/parse-bmfont-binary": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
+ "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY="
+ },
+ "node_modules/parse-bmfont-xml": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz",
+ "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==",
+ "dependencies": {
+ "xml-parse-from-string": "^1.0.0",
+ "xml2js": "^0.4.5"
+ }
+ },
+ "node_modules/parse-headers": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz",
+ "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA=="
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "dependencies": {
+ "isarray": "0.0.1"
+ }
+ },
+ "node_modules/path-to-regexp/node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "node_modules/phin": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
+ "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
+ },
+ "node_modules/pixelmatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
+ "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=",
+ "dependencies": {
+ "pngjs": "^3.0.0"
+ },
+ "bin": {
+ "pixelmatch": "bin/pixelmatch"
+ }
+ },
+ "node_modules/pngjs": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
+ "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
+ "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
+ "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==",
+ "dependencies": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.3",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
+ },
+ "node_modules/resolve-path": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
+ "integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=",
+ "dependencies": {
+ "http-errors": "~1.6.2",
+ "path-is-absolute": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/resolve-path/node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/resolve-path/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "node_modules/resolve-path/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
+ "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/timm": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz",
+ "integrity": "sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw=="
+ },
+ "node_modules/tinycolor2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
+ "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+ "engines": {
+ "node": ">=0.6.x"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/urijs": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz",
+ "integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w=="
+ },
+ "node_modules/utif": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
+ "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
+ "dependencies": {
+ "pako": "^1.0.5"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/xhr": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz",
+ "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==",
+ "dependencies": {
+ "global": "~4.3.0",
+ "is-function": "^1.0.1",
+ "parse-headers": "^2.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/xml-parse-from-string": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
+ "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig="
+ },
+ "node_modules/xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/ylru": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz",
+ "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ }
+ },
+ "dependencies": {
+ "@jimp/bmp": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz",
+ "integrity": "sha512-uxVgSkI62uAzk5ZazYHEHBehow590WAkLKmDXLzkr/XP/Hv2Fx1T4DKwJ/15IY5ktq5VAhAUWGXTyd8KWFsx7w==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "bmp-js": "^0.1.0",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/core": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.6.8.tgz",
+ "integrity": "sha512-JOFqBBcSNiDiMZJFr6OJqC6viXj5NVBQISua0eacoYvo4YJtTajOIxC4MqWyUmGrDpRMZBR8QhSsIOwsFrdROA==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "any-base": "^1.1.0",
+ "buffer": "^5.2.0",
+ "core-js": "^2.5.7",
+ "exif-parser": "^0.1.12",
+ "file-type": "^9.0.0",
+ "load-bmfont": "^1.3.1",
+ "mkdirp": "0.5.1",
+ "phin": "^2.9.1",
+ "pixelmatch": "^4.0.2",
+ "tinycolor2": "^1.4.1"
+ },
+ "dependencies": {
+ "file-type": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz",
+ "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw=="
+ }
+ }
+ },
+ "@jimp/custom": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.8.tgz",
+ "integrity": "sha512-FrYlzZRVXP2vuVwd7Nc2dlK+iZk4g6IaT1Ib8Z6vU5Kkwlt83FJIPJ2UUFABf3bF5big0wkk8ZUihWxE4Nzdng==",
+ "requires": {
+ "@jimp/core": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/gif": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.8.tgz",
+ "integrity": "sha512-yyOlujjQcgz9zkjM5ihZDEppn9d1brJ7jQHP5rAKmqep0G7FU1D0AKcV+Ql18RhuI/CgWs10wAVcrQpmLnu4Yw==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "omggif": "^1.0.9"
+ }
+ },
+ "@jimp/jpeg": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.8.tgz",
+ "integrity": "sha512-rGtXbYpFXAn471qLpTGvhbBMNHJo5KiufN+vC5AWyufntmkt5f0Ox2Cx4ijuBMDtirZchxbMLtrfGjznS4L/ew==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "jpeg-js": "^0.3.4"
+ }
+ },
+ "@jimp/plugin-blit": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz",
+ "integrity": "sha512-7Tl6YpKTSpvwQbnGNhsfX2zyl3jRVVopd276Y2hF2zpDz9Bycow7NdfNU/4Nx1jaf96X6uWOtSVINcQ7rGd47w==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-blur": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz",
+ "integrity": "sha512-NpZCMKxXHLDQsX9zPlWtpMA660DQStY6/z8ZetyxCDbqrLe9YCXpeR4MNhdJdABIiwTm1W5FyFF4kp81PHJx3Q==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-color": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.8.tgz",
+ "integrity": "sha512-jjFyU0zNmGOH2rjzHuOMU4kaia0oo82s/7UYfn5h7OUkmUZTd6Do3ZSK1PiXA7KR+s4B76/Omm6Doh/0SGb7BQ==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "tinycolor2": "^1.4.1"
+ }
+ },
+ "@jimp/plugin-contain": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz",
+ "integrity": "sha512-p/P2wCXhAzbmEgXvGsvmxLmbz45feF6VpR4m9suPSOr8PC/i/XvTklTqYEUidYYAft4vHgsYJdS74HKSMnH8lw==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-cover": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz",
+ "integrity": "sha512-2PvWgk+PJfRsfWDI1G8Fpjrsu0ZlpNyZxO2+fqWlVo6y/y2gP4v08FqvbkcqSjNlOu2IDWIFXpgyU0sTINWZLg==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-crop": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz",
+ "integrity": "sha512-CbrcpWE2xxPK1n/JoTXzhRUhP4mO07mTWaSavenCg664oQl/9XCtL+A0FekuNHzIvn4myEqvkiTwN7FsbunS/Q==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-displace": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz",
+ "integrity": "sha512-RmV2bPxoPE6mrPxtYSPtHxm2cGwBQr5a2p+9gH6SPy+eUMrbGjbvjwKNfXWUYD0leML+Pt5XOmAS9pIROmuruQ==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-dither": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz",
+ "integrity": "sha512-x6V/qjxe+xypjpQm7GbiMNqci1EW5UizrcebOhHr8AHijOEqHd2hjXh5f6QIGfrkTFelc4/jzq1UyCsYntqz9Q==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-flip": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz",
+ "integrity": "sha512-4il6Da6G39s9MyWBEee4jztEOUGJ40E6OlPjkMrdpDNvge6hYEAB31BczTYBP/CEY74j4LDSoY5LbcU4kv06yA==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-gaussian": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz",
+ "integrity": "sha512-pVOblmjv7stZjsqloi4YzHVwAPXKGdNaHPhp4KP4vj41qtc6Hxd9z/+VWGYRTunMFac84gUToe0UKIXd6GhoKw==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-invert": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz",
+ "integrity": "sha512-11zuLiXDHr6tFv4U8aieXqNXQEKbDbSBG/h+X62gGTNFpyn8EVPpncHhOqrAFtZUaPibBqMFlNJ15SzwC7ExsQ==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-mask": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz",
+ "integrity": "sha512-hZJ0OiKGJyv7hDSATwJDkunB1Ie80xJnONMgpUuUseteK45YeYNBOiZVUe8vum8QI1UwavgBzcvQ9u4fcgXc9g==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-normalize": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz",
+ "integrity": "sha512-Q4oYhU+sSyTJI7pMZlg9/mYh68ujLfOxXzQGEXuw0sHGoGQs3B0Jw7jmzGa6pIS06Hup5hD2Zuh1ppvMdjJBfQ==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-print": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.8.tgz",
+ "integrity": "sha512-2aokejGn4Drv1FesnZGqh5KEq0FQtR0drlmtyZrBH+r9cx7hh0Qgf4D1BOTDEgXkfSSngjGRjKKRW/fwOrVXYw==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "load-bmfont": "^1.4.0"
+ }
+ },
+ "@jimp/plugin-resize": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz",
+ "integrity": "sha512-27nPh8L1YWsxtfmV/+Ub5dOTpXyC0HMF2cu52RQSCYxr+Lm1+23dJF70AF1poUbUe+FWXphwuUxQzjBJza9UoA==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-rotate": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz",
+ "integrity": "sha512-GbjETvL05BDoLdszNUV4Y0yLkHf177MnqGqilA113LIvx9aD0FtUopGXYfRGVvmtTOTouoaGJUc+K6qngvKxww==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugin-scale": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz",
+ "integrity": "sha512-GzIYWR/oCUK2jAwku23zt19V1ssaEU4pL0x2XsLNKuuJEU6DvEytJyTMXCE7OLG/MpDBQcQclJKHgiyQm5gIOQ==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7"
+ }
+ },
+ "@jimp/plugins": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.8.tgz",
+ "integrity": "sha512-fMcTI72Vn/Lz6JftezTURmyP5ml/xGMe0Ljx2KRJ85IWyP33vDmGIUuutFiBEbh2+y7lRT+aTSmjs0QGa/xTmQ==",
+ "requires": {
+ "@jimp/plugin-blit": "^0.6.8",
+ "@jimp/plugin-blur": "^0.6.8",
+ "@jimp/plugin-color": "^0.6.8",
+ "@jimp/plugin-contain": "^0.6.8",
+ "@jimp/plugin-cover": "^0.6.8",
+ "@jimp/plugin-crop": "^0.6.8",
+ "@jimp/plugin-displace": "^0.6.8",
+ "@jimp/plugin-dither": "^0.6.8",
+ "@jimp/plugin-flip": "^0.6.8",
+ "@jimp/plugin-gaussian": "^0.6.8",
+ "@jimp/plugin-invert": "^0.6.8",
+ "@jimp/plugin-mask": "^0.6.8",
+ "@jimp/plugin-normalize": "^0.6.8",
+ "@jimp/plugin-print": "^0.6.8",
+ "@jimp/plugin-resize": "^0.6.8",
+ "@jimp/plugin-rotate": "^0.6.8",
+ "@jimp/plugin-scale": "^0.6.8",
+ "core-js": "^2.5.7",
+ "timm": "^1.6.1"
+ }
+ },
+ "@jimp/png": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.6.8.tgz",
+ "integrity": "sha512-JHHg/BZ7KDtHQrcG+a7fztw45rdf7okL/YwkN4qU5FH7Fcrp41nX5QnRviDtD9hN+GaNC7kvjvcqRAxW25qjew==",
+ "requires": {
+ "@jimp/utils": "^0.6.8",
+ "core-js": "^2.5.7",
+ "pngjs": "^3.3.3"
+ }
+ },
+ "@jimp/tiff": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.8.tgz",
+ "integrity": "sha512-iWHbxd+0IKWdJyJ0HhoJCGYmtjPBOusz1z1HT/DnpePs/Lo3TO4d9ALXqYfUkyG74ZK5jULZ69KLtwuhuJz1bg==",
+ "requires": {
+ "core-js": "^2.5.7",
+ "utif": "^2.0.1"
+ }
+ },
+ "@jimp/types": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.6.8.tgz",
+ "integrity": "sha512-vCZ/Cp2osy69VP21XOBACfHI5HeR60Rfd4Jidj4W73UL+HrFWOtyQiJ7hlToyu1vI5mR/NsUQpzyQvz56ADm5A==",
+ "requires": {
+ "@jimp/bmp": "^0.6.8",
+ "@jimp/gif": "^0.6.8",
+ "@jimp/jpeg": "^0.6.8",
+ "@jimp/png": "^0.6.8",
+ "@jimp/tiff": "^0.6.8",
+ "core-js": "^2.5.7",
+ "timm": "^1.6.1"
+ }
+ },
+ "@jimp/utils": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.8.tgz",
+ "integrity": "sha512-7RDfxQ2C/rarNG9iso5vmnKQbcvlQjBIlF/p7/uYj72WeZgVCB+5t1fFBKJSU4WhniHX4jUMijK+wYGE3Y3bGw==",
+ "requires": {
+ "core-js": "^2.5.7"
+ }
+ },
+ "@types/events": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
+ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
+ },
+ "@types/formidable": {
+ "version": "1.0.31",
+ "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
+ "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==",
+ "requires": {
+ "@types/events": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/node": {
+ "version": "13.9.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.5.tgz",
+ "integrity": "sha512-hkzMMD3xu6BrJpGVLeQ3htQQNAcOrJjX7WFmtK8zWQpz2UJf13LCFF2ALA7c9OVdvc2vQJeDdjfR35M0sBCxvw=="
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "any-base": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
+ "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg=="
+ },
+ "any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+ },
+ "axios": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz",
+ "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==",
+ "requires": {
+ "follow-redirects": "1.5.10",
+ "is-buffer": "^2.0.2"
+ }
+ },
+ "base64-js": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+ },
+ "basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ }
+ }
+ },
+ "bmp-js": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+ "integrity": "sha1-4Fpj95amwf8l9Hcex62twUjAcjM="
+ },
+ "buffer": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.5.0.tgz",
+ "integrity": "sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==",
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4"
+ }
+ },
+ "buffer-equal": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
+ "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+ },
+ "cache-content-type": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz",
+ "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==",
+ "requires": {
+ "mime-types": "^2.1.18",
+ "ylru": "^1.2.0"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ },
+ "co-body": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/co-body/-/co-body-5.2.0.tgz",
+ "integrity": "sha512-sX/LQ7LqUhgyaxzbe7IqwPeTr2yfpfUIQ/dgpKo6ZI4y4lpQA0YxAomWIY+7I7rHWcG02PG+OuPREzMW/5tszQ==",
+ "requires": {
+ "inflation": "^2.0.0",
+ "qs": "^6.4.0",
+ "raw-body": "^2.2.0",
+ "type-is": "^1.6.14"
+ }
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ }
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookies": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
+ "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
+ "requires": {
+ "depd": "~2.0.0",
+ "keygrip": "~1.1.0"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+ }
+ }
+ },
+ "core-js": {
+ "version": "2.6.11",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
+ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+ },
+ "cron": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz",
+ "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==",
+ "requires": {
+ "moment-timezone": "^0.5.x"
+ }
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "dom-walk": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
+ "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
+ },
+ "dotenv": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz",
+ "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g=="
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "error-inject": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz",
+ "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+ },
+ "exif-parser": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
+ "integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI="
+ },
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "=3.1.0"
+ }
+ },
+ "formidable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
+ "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "global": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
+ "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
+ "requires": {
+ "min-document": "^2.19.0",
+ "process": "~0.5.1"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
+ },
+ "http-assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz",
+ "integrity": "sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw==",
+ "requires": {
+ "deep-equal": "~1.0.1",
+ "http-errors": "~1.7.2"
+ }
+ },
+ "http-errors": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
+ "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+ },
+ "inflation": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz",
+ "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8="
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
+ },
+ "is-function": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz",
+ "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU="
+ },
+ "is-generator-function": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz",
+ "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw=="
+ },
+ "jimp": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.6.8.tgz",
+ "integrity": "sha512-F7emeG7Hp61IM8VFbNvWENLTuHe0ghizWPuP4JS9ujx2r5mCVYEd/zdaz6M2M42ZdN41blxPajLWl9FXo7Mr2Q==",
+ "requires": {
+ "@jimp/custom": "^0.6.8",
+ "@jimp/plugins": "^0.6.8",
+ "@jimp/types": "^0.6.8",
+ "core-js": "^2.5.7",
+ "regenerator-runtime": "^0.13.3"
+ }
+ },
+ "jpeg-js": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz",
+ "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ=="
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "keygrip": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+ "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
+ "requires": {
+ "tsscmp": "1.0.6"
+ }
+ },
+ "koa": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/koa/-/koa-2.11.0.tgz",
+ "integrity": "sha512-EpR9dElBTDlaDgyhDMiLkXrPwp6ZqgAIBvhhmxQ9XN4TFgW+gEz6tkcsNI6BnUbUftrKDjVFj4lW2/J2aNBMMA==",
+ "requires": {
+ "accepts": "^1.3.5",
+ "cache-content-type": "^1.0.0",
+ "content-disposition": "~0.5.2",
+ "content-type": "^1.0.4",
+ "cookies": "~0.8.0",
+ "debug": "~3.1.0",
+ "delegates": "^1.0.0",
+ "depd": "^1.1.2",
+ "destroy": "^1.0.4",
+ "encodeurl": "^1.0.2",
+ "error-inject": "^1.0.0",
+ "escape-html": "^1.0.3",
+ "fresh": "~0.5.2",
+ "http-assert": "^1.3.0",
+ "http-errors": "^1.6.3",
+ "is-generator-function": "^1.0.7",
+ "koa-compose": "^4.1.0",
+ "koa-convert": "^1.2.0",
+ "on-finished": "^2.3.0",
+ "only": "~0.0.2",
+ "parseurl": "^1.3.2",
+ "statuses": "^1.5.0",
+ "type-is": "^1.6.16",
+ "vary": "^1.1.2"
+ }
+ },
+ "koa-basic-auth": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/koa-basic-auth/-/koa-basic-auth-4.0.0.tgz",
+ "integrity": "sha512-eV1sGVAizDuFWNpY43VF3Z1ND4PotQZB/igxHNrcJXzXw+Flmj8Uv+4hP9LyNXyvqLJz/X5bmXeMu84AAGD9Jw==",
+ "requires": {
+ "basic-auth": "^2.0.0",
+ "tsscmp": "^1.0.6"
+ }
+ },
+ "koa-body": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/koa-body/-/koa-body-4.1.1.tgz",
+ "integrity": "sha512-rLb/KVD8qplEcK8Qsu6F4Xw+uHkmx3MWogDVmMX07DpjXizhw3pOEp1ja1MqqAcl0ei75AsrbGVDlySmsUrreA==",
+ "requires": {
+ "@types/formidable": "^1.0.31",
+ "co-body": "^5.1.1",
+ "formidable": "^1.1.1"
+ }
+ },
+ "koa-compose": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
+ "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw=="
+ },
+ "koa-compress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/koa-compress/-/koa-compress-3.0.0.tgz",
+ "integrity": "sha512-xol+LkNB1mozKJkB5Kj6nYXbJXhkLkZlXl9BsGBPjujVfZ8MsIXwU4GHRTT7TlSfUcl2DU3JtC+j6wOWcovfuQ==",
+ "requires": {
+ "bytes": "^3.0.0",
+ "compressible": "^2.0.0",
+ "koa-is-json": "^1.0.0",
+ "statuses": "^1.0.0"
+ }
+ },
+ "koa-convert": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz",
+ "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=",
+ "requires": {
+ "co": "^4.6.0",
+ "koa-compose": "^3.0.0"
+ },
+ "dependencies": {
+ "koa-compose": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz",
+ "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=",
+ "requires": {
+ "any-promise": "^1.1.0"
+ }
+ }
+ }
+ },
+ "koa-is-json": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz",
+ "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ="
+ },
+ "koa-mount": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.0.0.tgz",
+ "integrity": "sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==",
+ "requires": {
+ "debug": "^4.0.1",
+ "koa-compose": "^4.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
+ "koa-router": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz",
+ "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==",
+ "requires": {
+ "debug": "^3.1.0",
+ "http-errors": "^1.3.1",
+ "koa-compose": "^3.0.0",
+ "methods": "^1.0.1",
+ "path-to-regexp": "^1.1.1",
+ "urijs": "^1.19.0"
+ },
+ "dependencies": {
+ "koa-compose": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz",
+ "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=",
+ "requires": {
+ "any-promise": "^1.1.0"
+ }
+ }
+ }
+ },
+ "koa-send": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/koa-send/-/koa-send-5.0.0.tgz",
+ "integrity": "sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ==",
+ "requires": {
+ "debug": "^3.1.0",
+ "http-errors": "^1.6.3",
+ "mz": "^2.7.0",
+ "resolve-path": "^1.4.0"
+ }
+ },
+ "koa-sendfile": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/koa-sendfile/-/koa-sendfile-3.0.0.tgz",
+ "integrity": "sha512-wvvvDQIh7eNFRm/0AZNYtL+gl7uSG7qC4r0b7yJAQUgOh40yRW6hLjUWJPD09aa8t1c077X5G+pzOK9+WuF4Eg==",
+ "requires": {
+ "debug": "^4.2.0",
+ "etag": "^1.8.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ }
+ }
+ },
+ "koa-static": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/koa-static/-/koa-static-5.0.0.tgz",
+ "integrity": "sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==",
+ "requires": {
+ "debug": "^3.1.0",
+ "koa-send": "^5.0.0"
+ }
+ },
+ "load-bmfont": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.0.tgz",
+ "integrity": "sha512-kT63aTAlNhZARowaNYcY29Fn/QYkc52M3l6V1ifRcPewg2lvUZDAj7R6dXjOL9D0sict76op3T5+odumDSF81g==",
+ "requires": {
+ "buffer-equal": "0.0.1",
+ "mime": "^1.3.4",
+ "parse-bmfont-ascii": "^1.0.3",
+ "parse-bmfont-binary": "^1.0.5",
+ "parse-bmfont-xml": "^1.1.4",
+ "phin": "^2.9.1",
+ "xhr": "^2.0.1",
+ "xtend": "^4.0.0"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
+ },
+ "mime-db": {
+ "version": "1.43.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
+ "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
+ },
+ "mime-types": {
+ "version": "2.1.26",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
+ "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "requires": {
+ "mime-db": "1.43.0"
+ }
+ },
+ "min-document": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
+ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
+ "requires": {
+ "dom-walk": "^0.1.0"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ },
+ "moment-timezone": {
+ "version": "0.5.28",
+ "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz",
+ "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==",
+ "requires": {
+ "moment": ">= 2.9.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "requires": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "omggif": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
+ "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "only": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
+ "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q="
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "parse-bmfont-ascii": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
+ "integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU="
+ },
+ "parse-bmfont-binary": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
+ "integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY="
+ },
+ "parse-bmfont-xml": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz",
+ "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==",
+ "requires": {
+ "xml-parse-from-string": "^1.0.0",
+ "xml2js": "^0.4.5"
+ }
+ },
+ "parse-headers": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz",
+ "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA=="
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "requires": {
+ "isarray": "0.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ }
+ }
+ },
+ "phin": {
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz",
+ "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA=="
+ },
+ "pixelmatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
+ "integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=",
+ "requires": {
+ "pngjs": "^3.0.0"
+ }
+ },
+ "pngjs": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
+ "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
+ },
+ "process": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
+ "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+ },
+ "raw-body": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
+ "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==",
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.3",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.5",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+ "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
+ },
+ "resolve-path": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/resolve-path/-/resolve-path-1.4.0.tgz",
+ "integrity": "sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc=",
+ "requires": {
+ "http-errors": "~1.6.2",
+ "path-is-absolute": "1.0.1"
+ },
+ "dependencies": {
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ }
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "thenify": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
+ "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
+ "requires": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
+ "requires": {
+ "thenify": ">= 3.1.0 < 4"
+ }
+ },
+ "timm": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/timm/-/timm-1.6.2.tgz",
+ "integrity": "sha512-IH3DYDL1wMUwmIlVmMrmesw5lZD6N+ZOAFWEyLrtpoL9Bcrs9u7M/vyOnHzDD2SMs4irLkVjqxZbHrXStS/Nmw=="
+ },
+ "tinycolor2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
+ "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "tsscmp": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+ "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA=="
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "urijs": {
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz",
+ "integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w=="
+ },
+ "utif": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz",
+ "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==",
+ "requires": {
+ "pako": "^1.0.5"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "xhr": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz",
+ "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==",
+ "requires": {
+ "global": "~4.3.0",
+ "is-function": "^1.0.1",
+ "parse-headers": "^2.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "xml-parse-from-string": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
+ "integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig="
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+ },
+ "ylru": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz",
+ "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ=="
+ }
+ }
+}
diff --git a/mediaserver/package.json b/mediaserver/package.json
new file mode 100644
index 00000000..2d084a0f
--- /dev/null
+++ b/mediaserver/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "media-server",
+ "version": "1.0.0",
+ "description": "Media server for workup",
+ "main": "index.js",
+ "scripts": {
+ "start": "node index.js",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ahmed54123/workup.git"
+ },
+ "author": "aboueleyes",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^0.18.1",
+ "cron": "^1.7.0",
+ "dotenv": "^7.0.0",
+ "fs-extra": "^7.0.1",
+ "jimp": "^0.6.4",
+ "koa": "^2.7.0",
+ "koa-basic-auth": "^4.0.0",
+ "koa-body": "^4.1.1",
+ "koa-compress": "^3.0.0",
+ "koa-mount": "^4.0.0",
+ "koa-router": "^7.4.0",
+ "koa-sendfile": "^3.0.0",
+ "koa-static": "^5.0.0"
+ }
+}
\ No newline at end of file
diff --git a/mediaserver/src/app.js b/mediaserver/src/app.js
new file mode 100644
index 00000000..3a9b308e
--- /dev/null
+++ b/mediaserver/src/app.js
@@ -0,0 +1,82 @@
+const Koa = require("koa");
+const mount = require("koa-mount");
+const compress = require("koa-compress");
+const body = require("koa-body");
+const { SUPPORTED_MIME_TYPES } = require("./enums");
+
+/**
+ * Constructs an array of all supported MIME types.
+ */
+const mimeTypes = []
+ .concat(SUPPORTED_MIME_TYPES.DOC)
+ .concat(SUPPORTED_MIME_TYPES.VIDEO)
+ .concat(SUPPORTED_MIME_TYPES.IMAGE);
+
+/**
+ * Instantiate a new KOA app.
+ */
+const app = new Koa();
+
+/**
+ * Import all the routes.
+ */
+const static = require("./routes/static");
+const describe = require("./routes/describe");
+const upload = require("./routes/upload");
+
+/**
+ * For each async piece of middleware, try it, and report on any errors.
+ *
+ * Koa middleware cascades, so putting this at the top of the file ensures all errors are captured.
+ */
+app.use(async (ctx, next) => {
+ try {
+ await next();
+ } catch (err) {
+ ctx.status = err.status || 500;
+ ctx.body = {
+ success: false,
+ message: err.message
+ };
+ ctx.app.emit("error", err, ctx);
+ }
+});
+
+/**
+ * Compress all compressible responses over 2KB.
+ * This is includes compression of all mime types listed in the config file.
+ */
+app.use(
+ compress({
+ filter: type => mimeTypes.find(mimeType => mimeType === type),
+ threshold: 2048,
+ flush: require("zlib").Z_SYNC_FLUSH
+ })
+);
+
+/**
+ * Allow KOA to parse the body of a request, including support for multipart forms.
+ * Limit the maximum file size to 300MB.
+ */
+app.use(
+ body({
+ multipart: true,
+ formidable: {
+ maxFileSize: 300 * 1024 * 1024
+ }
+ })
+);
+
+/**
+ * Define routes
+ * - Music uses a cache-able router.
+ * - Static is, well, static.
+ * - Describe tells us about the available media.
+ * - Upload is a secure media upload endpoint.
+ */
+app.use(mount("/static", static.router.routes()));
+app.use(mount("/describe", describe.router.routes()));
+app.use(mount("/upload", upload.router.routes()));
+
+
+module.exports = app;
diff --git a/mediaserver/src/config.js b/mediaserver/src/config.js
new file mode 100644
index 00000000..02fffb58
--- /dev/null
+++ b/mediaserver/src/config.js
@@ -0,0 +1,84 @@
+/**
+ * config.js - Defines the configuration of the media server through a series of constant definitions.
+ */
+
+/**
+ * Define what environment variables must be set.
+ */
+const expected_env_vars = ["DISCOGS_API_KEY", "UPLOAD_USER", "UPLOAD_PASSWORD"];
+
+/**
+ * For each expected environment variable, check if it is set.
+ * If unset, throw an error reporting which environment variable must be set.
+ */
+expected_env_vars.forEach(env_var => {
+ if (!(env_var in process.env)) {
+ throw new Error("Environment variable " + env_var + " must be set.");
+ }
+});
+
+module.exports = {
+ /**
+ * Define some global constants which define required values for the server's function.
+ * This includes API keys and resource upload user credentials.
+ */
+ DISCOGS_API_KEY: process.env.DISCOGS_API_KEY,
+ PORT: process.env.PORT || 3000,
+ UPLOAD: {
+ USER: process.env.UPLOAD_USER,
+ PASSWORD: process.env.UPLOAD_PASSWORD
+ },
+ /**
+ * Define the parameters of all static resource groups. To define a new static group, simply
+ * include a new resource group object in this array. The resource group will be instantiated when the
+ * server is restarted.
+ *
+ * All resource group objects must follow a defined shape:
+ *
+ *```
+ *{
+ * NAME: string,
+ * MIME_TYPE: string,
+ * FILE_EXTENSION: string,
+ * DEFAULT_RESOURCE: string,
+ * QUALITY: float,
+ * PIXEL_DIMENSIONS: {
+ * WIDTH: integer,
+ * HEIGHT: integer
+ * }
+ * }
+ *```
+
+ * `NAME` can be any string.
+ * `MIME_TYPE` can be any valid IANA MIME type. Ensure the MIME type is listed in the supported MIME types at "./enums.js".
+ * `FILE_EXTENSION` can be any valid file extension which matches the MIME type.
+ * `DEFAULT_RESOURCE` can be any valid filename and extension of any default resource listed in the './media/defaults' folder.
+ * `QUALITY` can be any valid float between 1 and 100.
+ * `PIXEL_DIMENSIONS` can be an object with keys WIDTH and HEIGHT with any integer values.
+ *
+ * Some common sense must be used to match the MIME type, file extension, and default resource filename extension.
+ */
+ STATIC: [
+ {
+ NAME: "icons",
+ MIME_TYPE: "image/png",
+ FILE_EXTENSION: "png",
+ DEFAULT_RESOURCE: "raw-icon.png",
+ QUALITY: 100.0,
+ PIXEL_DIMENSIONS: {
+ WIDTH: 16,
+ HEIGHT: 16
+ }
+ },
+ {
+ NAME: "resume",
+ MIME_TYPE: "application",
+ FILE_EXTENSION: "pdf",
+ },
+ {
+ NAME: "attachments",
+ MIME_TYPE: "application",
+ FILE_EXTENSION: "pdf",
+ }
+ ]
+};
diff --git a/mediaserver/src/enums.js b/mediaserver/src/enums.js
new file mode 100644
index 00000000..1649af18
--- /dev/null
+++ b/mediaserver/src/enums.js
@@ -0,0 +1,11 @@
+module.exports = {
+ /**
+ * Defines which media types are supported by this server.
+ * Supported MIME types can be any valid IANA MIME type.
+ */
+ SUPPORTED_MIME_TYPES: {
+ IMAGE: ["image/jpeg", "image/png", "image/gif", "image/bmp", "image/tiff"],
+ VIDEO: ["video/mp4", "video/avi", "video/x-msvideo", "video/quicktime"],
+ DOC: ["application/pdf"]
+ }
+};
diff --git a/mediaserver/src/routes/describe.js b/mediaserver/src/routes/describe.js
new file mode 100644
index 00000000..b68740bf
--- /dev/null
+++ b/mediaserver/src/routes/describe.js
@@ -0,0 +1,58 @@
+/**
+ * describe.js - Provides routes which describe the resource endpoints.
+ */
+
+const koaRouter = require("koa-router");
+const fs = require("fs-extra");
+const { STATIC } = require("../config");
+
+const router = new koaRouter();
+
+const groups = STATIC.map(group => group.NAME);
+
+/**
+ * Return all the static resource groups defined by the config file.
+ *
+ * Describes which groups are present, and at what path.
+ */
+router.get("/", async ctx => {
+ ctx.body = {
+ success: true,
+ path: "/static/",
+ groups: groups
+ };
+});
+
+/**
+ * For a specific group, return the files within it.
+ */
+router.get("/:group", async ctx => {
+ /**
+ * Get and escape the group from the request.
+ */
+ const group = ctx.params.group.toLowerCase();
+
+ const index = groups.indexOf(group);
+
+ if (index !== -1) {
+ const filenames = await fs.readdir("./media/static/" + group);
+
+ ctx.body = {
+ success: true,
+ path: "/static/" + group + "/",
+ mimeType: STATIC[index].MIME_TYPE,
+ files: filenames
+ };
+ } else {
+ ctx.body = {
+ success: false,
+ path: null,
+ fileExtension: null,
+ files: null
+ };
+ }
+});
+
+module.exports = {
+ router: router
+};
diff --git a/mediaserver/src/routes/static.js b/mediaserver/src/routes/static.js
new file mode 100644
index 00000000..adf8fbf7
--- /dev/null
+++ b/mediaserver/src/routes/static.js
@@ -0,0 +1,56 @@
+const koaRouter = require("koa-router");
+const fs = require("fs-extra");
+const { STATIC } = require("../config");
+
+const router = new koaRouter();
+
+/**
+ * Ensure that the static media directory exists locally on the server.
+ */
+fs.ensureDir("./media/static");
+
+/**
+ * For each static resource group, define a route which serves that resource group. Each route is constructed
+ * by reading the configuration for each static resource group from the config file. The route will return
+ * either a meaningful resource if it is found on disk, or else a default 'placeholder' image.
+ */
+STATIC.forEach(async group => {
+ const route = "/" + group.NAME + "/:filename." + group.FILE_EXTENSION;
+
+ /**
+ * Ensure that the directory for this group exists locally on the server.
+ */
+ await fs.ensureDir("./media/static/" + group.NAME);
+
+ router.get(route, async ctx => {
+ ctx.set("Content-Type", group.MIME_TYPE);
+
+ filename = ctx.params.filename.toLowerCase();
+
+ const path =
+ "./media/static/" +
+ group.NAME +
+ "/" +
+ filename +
+ "." +
+ group.FILE_EXTENSION;
+
+ const exists = await fs.pathExists(path);
+
+ /**
+ * If exists, return the file.
+ * Else, return default of a given file type.
+ */
+ if (exists) {
+ ctx.body = await fs.readFile(path);
+ } else {
+ ctx.body = await fs.readFile(
+ "./media/defaults/" + group.DEFAULT_RESOURCE
+ );
+ }
+ });
+});
+
+module.exports = {
+ router: router
+};
diff --git a/mediaserver/src/routes/upload.js b/mediaserver/src/routes/upload.js
new file mode 100644
index 00000000..8e2df14b
--- /dev/null
+++ b/mediaserver/src/routes/upload.js
@@ -0,0 +1,149 @@
+const koaRouter = require("koa-router");
+const basicAuth = require("koa-basic-auth");
+const jimp = require("jimp");
+const { STATIC, UPLOAD } = require("../config");
+const { SUPPORTED_MIME_TYPES } = require("../enums");
+const fs = require("fs");
+
+
+const router = new koaRouter();
+
+/**
+ * Authorise this route using basic HTTP authorisation.
+ * Credentials are defined in the config file.
+ */
+router.use(basicAuth({ name: UPLOAD.USER, pass: UPLOAD.PASSWORD }));
+
+/**
+ * Handles the POSTing of a new resource to the media server.
+ */
+router.post("/:group", async ctx => {
+ const group = ctx.params.group.toLowerCase();
+
+ /**
+ * Return the target group definition if the group exists within the configuration file's static definition.
+ */
+ const targetGroup = STATIC.find(targetGroup => targetGroup.NAME === group);
+
+ /**
+ * Assert that the requested target upload resource group is a valid resource group.
+ */
+ ctx.assert(targetGroup, 400, "invalid resource group: " + group);
+
+ const {resource} = ctx.request.files;
+
+ ctx.assert(resource, 400, "must include 'resource' in POST body");
+
+ const {filename} = ctx.request.body;
+
+ ctx.assert(filename, 400, "must include 'filename' in POST body");
+
+ /**
+ * Deconstruct targetGroup object into individual keys for improved readability.
+ */
+ const { NAME, FILE_EXTENSION, MIME_TYPE } = targetGroup;
+
+ const targetPath =
+ "/static/" + NAME + "/" + escapeFilename(filename) + "." + FILE_EXTENSION;
+
+ ctx.assert(
+ extractMIMEClass(resource.type) === extractMIMEClass(MIME_TYPE),
+ 400,
+ "cannot upload MIME class: " +
+ extractMIMEClass(resource.type) +
+ ", to resource group of class: " +
+ extractMIMEClass(MIME_TYPE)
+ );
+
+ await save(resource, targetPath, targetGroup);
+
+ ctx.body = { success: true, path: targetPath };
+});
+
+/**
+ * Escapes a filename string:
+ * - removes leading and trailing whitespace.
+ * - All chars converted to lower case.
+ * - Replace all spaces with '_'.
+ *
+ * @param {string} filename - The string filename to be escaped.
+ */
+const escapeFilename = filename =>
+ filename
+ .trim()
+ .toLowerCase()
+ .replace(/ /g, "_");
+
+/**
+ * Returns the high level 'class' of the MIME type from a MIME type string.
+ *
+ * @param {string} mimeType - The MIME type from which to extract the MIME class.
+ */
+const extractMIMEClass = mimeType => mimeType.split("/")[0];
+
+/**
+ * An async function which tries to save a given resource at a given path based on given parameters.
+ *
+ * Will handle conversion and compression of all resources.
+ *
+ * @param {*} resource - the resource object of a new resource to be saved.
+ * @param {*} targetPath - the target local path at which the resource should be saved.
+ * @param {*} targetGroup - the parameters of the target group.
+ */
+const save = async (resource, targetPath, { QUALITY, PIXEL_DIMENSIONS }) => {
+ return new Promise(async (resolve, reject) => {
+ /**
+ * If the resource type is an image, apply image processing with jimp.
+ * Else, if the resource is a video, reject the promise.
+ * Else, if the resource is a document, pipe the file to the target path.
+ * Else, the resource has an unsupported MIME type.
+ */
+ if (SUPPORTED_MIME_TYPES.IMAGE.find(type => type === resource.type)) {
+ /**
+ * Read the resource into the jimp buffer.
+ */
+ const image = await jimp.read(resource.path);
+
+ /**
+ * Executing methods on the returned jimp object carries out image manipulation.
+ * Here we:
+ * - Resize the image to the dimensions defined by the target group parameters.
+ * - Compress the image based on the target group parameters.
+ * - Write the file to the target path.
+ */
+ image
+ .cover(PIXEL_DIMENSIONS.WIDTH, PIXEL_DIMENSIONS.HEIGHT)
+ .quality(QUALITY)
+ .write("./media" + targetPath);
+
+ /**
+ * Resolve the promise.
+ */
+ resolve();
+ } else if (
+ SUPPORTED_MIME_TYPES.VIDEO.find(type => type === resource.type)
+ ) {
+ reject(new Error("video processing not supported"));
+ } else if (SUPPORTED_MIME_TYPES.DOC.find(type => type === resource.type)) {
+
+ const src = fs.createReadStream(resource.path)
+ const dest = fs.createWriteStream("./media" + targetPath)
+
+ src.pipe(dest)
+ src.on("close", () => {
+ resolve()
+ })
+ src.on("error", (err) => {
+ reject(new Error(err.message))
+ })
+ }
+ else {
+ reject(new Error("unsupported MIME type: " + resource.type));
+ }
+ }
+ )
+}
+
+module.exports = {
+ router: router
+};
diff --git a/pom.xml b/pom.xml
index 750662ae..c85af379 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,6 @@
shared
- controllerservices/jobsservices/paymentsservices/users
@@ -61,18 +60,18 @@
git-build-hook-maven-plugin3.5.0
-
- true
-
+
+ true
+
-
-
- configure
-
-
+
+
+ configure
+
+
-
+
diff --git a/services/jobs/cassandra-config/cassandra.yaml b/services/jobs/cassandra-config/cassandra.yaml
deleted file mode 100644
index 4a5239b4..00000000
--- a/services/jobs/cassandra-config/cassandra.yaml
+++ /dev/null
@@ -1,1418 +0,0 @@
-# Cassandra storage config YAML
-
-# NOTE:
-# See https://cassandra.apache.org/doc/latest/configuration/ for
-# full explanations of configuration directives
-# /NOTE
-
-# The name of the cluster. This is mainly used to prevent machines in
-# one logical cluster from joining another.
-cluster_name: 'Test Cluster'
-
-# This defines the number of tokens randomly assigned to this node on the ring
-# The more tokens, relative to other nodes, the larger the proportion of data
-# that this node will store. You probably want all nodes to have the same number
-# of tokens assuming they have equal hardware capability.
-#
-# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
-# and will use the initial_token as described below.
-#
-# Specifying initial_token will override this setting on the node's initial start,
-# on subsequent starts, this setting will apply even if initial token is set.
-#
-# See https://cassandra.apache.org/doc/latest/getting_started/production.html#tokens for
-# best practice information about num_tokens.
-#
-num_tokens: 16
-
-# Triggers automatic allocation of num_tokens tokens for this node. The allocation
-# algorithm attempts to choose tokens in a way that optimizes replicated load over
-# the nodes in the datacenter for the replica factor.
-#
-# The load assigned to each node will be close to proportional to its number of
-# vnodes.
-#
-# Only supported with the Murmur3Partitioner.
-
-# Replica factor is determined via the replication strategy used by the specified
-# keyspace.
-# allocate_tokens_for_keyspace: KEYSPACE
-
-# Replica factor is explicitly set, regardless of keyspace or datacenter.
-# This is the replica factor within the datacenter, like NTS.
-allocate_tokens_for_local_replication_factor: 3
-
-# initial_token allows you to specify tokens manually. While you can use it with
-# vnodes (num_tokens > 1, above) -- in which case you should provide a
-# comma-separated list -- it's primarily used when adding nodes to legacy clusters
-# that do not have vnodes enabled.
-# initial_token:
-
-# May either be "true" or "false" to enable globally
-hinted_handoff_enabled: true
-
-# When hinted_handoff_enabled is true, a black list of data centers that will not
-# perform hinted handoff
-# hinted_handoff_disabled_datacenters:
-# - DC1
-# - DC2
-
-# this defines the maximum amount of time a dead host will have hints
-# generated. After it has been dead this long, new hints for it will not be
-# created until it has been seen alive and gone down again.
-max_hint_window_in_ms: 10800000 # 3 hours
-
-# Maximum throttle in KBs per second, per delivery thread. This will be
-# reduced proportionally to the number of nodes in the cluster. (If there
-# are two nodes in the cluster, each delivery thread will use the maximum
-# rate; if there are three, each will throttle to half of the maximum,
-# since we expect two nodes to be delivering hints simultaneously.)
-hinted_handoff_throttle_in_kb: 1024
-
-# Number of threads with which to deliver hints;
-# Consider increasing this number when you have multi-dc deployments, since
-# cross-dc handoff tends to be slower
-max_hints_delivery_threads: 2
-
-# Directory where Cassandra should store hints.
-# If not set, the default directory is $CASSANDRA_HOME/data/hints.
-# hints_directory: /var/lib/cassandra/hints
-
-# How often hints should be flushed from the internal buffers to disk.
-# Will *not* trigger fsync.
-hints_flush_period_in_ms: 10000
-
-# Maximum size for a single hints file, in megabytes.
-max_hints_file_size_in_mb: 128
-
-# Compression to apply to the hint files. If omitted, hints files
-# will be written uncompressed. LZ4, Snappy, and Deflate compressors
-# are supported.
-#hints_compression:
-# - class_name: LZ4Compressor
-# parameters:
-# -
-
-# Maximum throttle in KBs per second, total. This will be
-# reduced proportionally to the number of nodes in the cluster.
-batchlog_replay_throttle_in_kb: 1024
-
-# Authentication backend, implementing IAuthenticator; used to identify users
-# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
-# PasswordAuthenticator}.
-#
-# - AllowAllAuthenticator performs no checks - set it to disable authentication.
-# - PasswordAuthenticator relies on username/password pairs to authenticate
-# users. It keeps usernames and hashed passwords in system_auth.roles table.
-# Please increase system_auth keyspace replication factor if you use this authenticator.
-# If using PasswordAuthenticator, CassandraRoleManager must also be used (see below)
-authenticator: AllowAllAuthenticator
-
-# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
-# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
-# CassandraAuthorizer}.
-#
-# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
-# - CassandraAuthorizer stores permissions in system_auth.role_permissions table. Please
-# increase system_auth keyspace replication factor if you use this authorizer.
-authorizer: AllowAllAuthorizer
-
-# Part of the Authentication & Authorization backend, implementing IRoleManager; used
-# to maintain grants and memberships between roles.
-# Out of the box, Cassandra provides org.apache.cassandra.auth.CassandraRoleManager,
-# which stores role information in the system_auth keyspace. Most functions of the
-# IRoleManager require an authenticated login, so unless the configured IAuthenticator
-# actually implements authentication, most of this functionality will be unavailable.
-#
-# - CassandraRoleManager stores role data in the system_auth keyspace. Please
-# increase system_auth keyspace replication factor if you use this role manager.
-role_manager: CassandraRoleManager
-
-# Network authorization backend, implementing INetworkAuthorizer; used to restrict user
-# access to certain DCs
-# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllNetworkAuthorizer,
-# CassandraNetworkAuthorizer}.
-#
-# - AllowAllNetworkAuthorizer allows access to any DC to any user - set it to disable authorization.
-# - CassandraNetworkAuthorizer stores permissions in system_auth.network_permissions table. Please
-# increase system_auth keyspace replication factor if you use this authorizer.
-network_authorizer: AllowAllNetworkAuthorizer
-
-# Validity period for roles cache (fetching granted roles can be an expensive
-# operation depending on the role manager, CassandraRoleManager is one example)
-# Granted roles are cached for authenticated sessions in AuthenticatedUser and
-# after the period specified here, become eligible for (async) reload.
-# Defaults to 2000, set to 0 to disable caching entirely.
-# Will be disabled automatically for AllowAllAuthenticator.
-roles_validity_in_ms: 2000
-
-# Refresh interval for roles cache (if enabled).
-# After this interval, cache entries become eligible for refresh. Upon next
-# access, an async reload is scheduled and the old value returned until it
-# completes. If roles_validity_in_ms is non-zero, then this must be
-# also.
-# Defaults to the same value as roles_validity_in_ms.
-# roles_update_interval_in_ms: 2000
-
-# Validity period for permissions cache (fetching permissions can be an
-# expensive operation depending on the authorizer, CassandraAuthorizer is
-# one example). Defaults to 2000, set to 0 to disable.
-# Will be disabled automatically for AllowAllAuthorizer.
-permissions_validity_in_ms: 2000
-
-# Refresh interval for permissions cache (if enabled).
-# After this interval, cache entries become eligible for refresh. Upon next
-# access, an async reload is scheduled and the old value returned until it
-# completes. If permissions_validity_in_ms is non-zero, then this must be
-# also.
-# Defaults to the same value as permissions_validity_in_ms.
-# permissions_update_interval_in_ms: 2000
-
-# Validity period for credentials cache. This cache is tightly coupled to
-# the provided PasswordAuthenticator implementation of IAuthenticator. If
-# another IAuthenticator implementation is configured, this cache will not
-# be automatically used and so the following settings will have no effect.
-# Please note, credentials are cached in their encrypted form, so while
-# activating this cache may reduce the number of queries made to the
-# underlying table, it may not bring a significant reduction in the
-# latency of individual authentication attempts.
-# Defaults to 2000, set to 0 to disable credentials caching.
-credentials_validity_in_ms: 2000
-
-# Refresh interval for credentials cache (if enabled).
-# After this interval, cache entries become eligible for refresh. Upon next
-# access, an async reload is scheduled and the old value returned until it
-# completes. If credentials_validity_in_ms is non-zero, then this must be
-# also.
-# Defaults to the same value as credentials_validity_in_ms.
-# credentials_update_interval_in_ms: 2000
-
-# The partitioner is responsible for distributing groups of rows (by
-# partition key) across nodes in the cluster. The partitioner can NOT be
-# changed without reloading all data. If you are adding nodes or upgrading,
-# you should set this to the same partitioner that you are currently using.
-#
-# The default partitioner is the Murmur3Partitioner. Older partitioners
-# such as the RandomPartitioner, ByteOrderedPartitioner, and
-# OrderPreservingPartitioner have been included for backward compatibility only.
-# For new clusters, you should NOT change this value.
-#
-partitioner: org.apache.cassandra.dht.Murmur3Partitioner
-
-# Directories where Cassandra should store data on disk. If multiple
-# directories are specified, Cassandra will spread data evenly across
-# them by partitioning the token ranges.
-# If not set, the default directory is $CASSANDRA_HOME/data/data.
-# data_file_directories:
-# - /var/lib/cassandra/data
-
-# Directory were Cassandra should store the data of the local system keyspaces.
-# By default Cassandra will store the data of the local system keyspaces in the first of the data directories specified
-# by data_file_directories.
-# This approach ensures that if one of the other disks is lost Cassandra can continue to operate. For extra security
-# this setting allows to store those data on a different directory that provides redundancy.
-# local_system_data_file_directory:
-
-# commit log. when running on magnetic HDD, this should be a
-# separate spindle than the data directories.
-# If not set, the default directory is $CASSANDRA_HOME/data/commitlog.
-# commitlog_directory: /var/lib/cassandra/commitlog
-
-# Enable / disable CDC functionality on a per-node basis. This modifies the logic used
-# for write path allocation rejection (standard: never reject. cdc: reject Mutation
-# containing a CDC-enabled table if at space limit in cdc_raw_directory).
-cdc_enabled: false
-
-# CommitLogSegments are moved to this directory on flush if cdc_enabled: true and the
-# segment contains mutations for a CDC-enabled table. This should be placed on a
-# separate spindle than the data directories. If not set, the default directory is
-# $CASSANDRA_HOME/data/cdc_raw.
-# cdc_raw_directory: /var/lib/cassandra/cdc_raw
-
-# Policy for data disk failures:
-#
-# die
-# shut down gossip and client transports and kill the JVM for any fs errors or
-# single-sstable errors, so the node can be replaced.
-#
-# stop_paranoid
-# shut down gossip and client transports even for single-sstable errors,
-# kill the JVM for errors during startup.
-#
-# stop
-# shut down gossip and client transports, leaving the node effectively dead, but
-# can still be inspected via JMX, kill the JVM for errors during startup.
-#
-# best_effort
-# stop using the failed disk and respond to requests based on
-# remaining available sstables. This means you WILL see obsolete
-# data at CL.ONE!
-#
-# ignore
-# ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
-disk_failure_policy: stop
-
-# Policy for commit disk failures:
-#
-# die
-# shut down the node and kill the JVM, so the node can be replaced.
-#
-# stop
-# shut down the node, leaving the node effectively dead, but
-# can still be inspected via JMX.
-#
-# stop_commit
-# shutdown the commit log, letting writes collect but
-# continuing to service reads, as in pre-2.0.5 Cassandra
-#
-# ignore
-# ignore fatal errors and let the batches fail
-commit_failure_policy: stop
-
-# Maximum size of the native protocol prepared statement cache
-#
-# Valid values are either "auto" (omitting the value) or a value greater 0.
-#
-# Note that specifying a too large value will result in long running GCs and possbily
-# out-of-memory errors. Keep the value at a small fraction of the heap.
-#
-# If you constantly see "prepared statements discarded in the last minute because
-# cache limit reached" messages, the first step is to investigate the root cause
-# of these messages and check whether prepared statements are used correctly -
-# i.e. use bind markers for variable parts.
-#
-# Do only change the default value, if you really have more prepared statements than
-# fit in the cache. In most cases it is not neccessary to change this value.
-# Constantly re-preparing statements is a performance penalty.
-#
-# Default value ("auto") is 1/256th of the heap or 10MB, whichever is greater
-prepared_statements_cache_size_mb:
-
-# Maximum size of the key cache in memory.
-#
-# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
-# minimum, sometimes more. The key cache is fairly tiny for the amount of
-# time it saves, so it's worthwhile to use it at large numbers.
-# The row cache saves even more time, but must contain the entire row,
-# so it is extremely space-intensive. It's best to only use the
-# row cache if you have hot rows or static rows.
-#
-# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
-#
-# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
-key_cache_size_in_mb:
-
-# Duration in seconds after which Cassandra should
-# save the key cache. Caches are saved to saved_caches_directory as
-# specified in this configuration file.
-#
-# Saved caches greatly improve cold-start speeds, and is relatively cheap in
-# terms of I/O for the key cache. Row cache saving is much more expensive and
-# has limited use.
-#
-# Default is 14400 or 4 hours.
-key_cache_save_period: 14400
-
-# Number of keys from the key cache to save
-# Disabled by default, meaning all keys are going to be saved
-# key_cache_keys_to_save: 100
-
-# Row cache implementation class name. Available implementations:
-#
-# org.apache.cassandra.cache.OHCProvider
-# Fully off-heap row cache implementation (default).
-#
-# org.apache.cassandra.cache.SerializingCacheProvider
-# This is the row cache implementation availabile
-# in previous releases of Cassandra.
-# row_cache_class_name: org.apache.cassandra.cache.OHCProvider
-
-# Maximum size of the row cache in memory.
-# Please note that OHC cache implementation requires some additional off-heap memory to manage
-# the map structures and some in-flight memory during operations before/after cache entries can be
-# accounted against the cache capacity. This overhead is usually small compared to the whole capacity.
-# Do not specify more memory that the system can afford in the worst usual situation and leave some
-# headroom for OS block level cache. Do never allow your system to swap.
-#
-# Default value is 0, to disable row caching.
-row_cache_size_in_mb: 0
-
-# Duration in seconds after which Cassandra should save the row cache.
-# Caches are saved to saved_caches_directory as specified in this configuration file.
-#
-# Saved caches greatly improve cold-start speeds, and is relatively cheap in
-# terms of I/O for the key cache. Row cache saving is much more expensive and
-# has limited use.
-#
-# Default is 0 to disable saving the row cache.
-row_cache_save_period: 0
-
-# Number of keys from the row cache to save.
-# Specify 0 (which is the default), meaning all keys are going to be saved
-# row_cache_keys_to_save: 100
-
-# Maximum size of the counter cache in memory.
-#
-# Counter cache helps to reduce counter locks' contention for hot counter cells.
-# In case of RF = 1 a counter cache hit will cause Cassandra to skip the read before
-# write entirely. With RF > 1 a counter cache hit will still help to reduce the duration
-# of the lock hold, helping with hot counter cell updates, but will not allow skipping
-# the read entirely. Only the local (clock, count) tuple of a counter cell is kept
-# in memory, not the whole counter, so it's relatively cheap.
-#
-# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
-#
-# Default value is empty to make it "auto" (min(2.5% of Heap (in MB), 50MB)). Set to 0 to disable counter cache.
-# NOTE: if you perform counter deletes and rely on low gcgs, you should disable the counter cache.
-counter_cache_size_in_mb:
-
-# Duration in seconds after which Cassandra should
-# save the counter cache (keys only). Caches are saved to saved_caches_directory as
-# specified in this configuration file.
-#
-# Default is 7200 or 2 hours.
-counter_cache_save_period: 7200
-
-# Number of keys from the counter cache to save
-# Disabled by default, meaning all keys are going to be saved
-# counter_cache_keys_to_save: 100
-
-# saved caches
-# If not set, the default directory is $CASSANDRA_HOME/data/saved_caches.
-# saved_caches_directory: /var/lib/cassandra/saved_caches
-
-# Number of seconds the server will wait for each cache (row, key, etc ...) to load while starting
-# the Cassandra process. Setting this to a negative value is equivalent to disabling all cache loading on startup
-# while still having the cache during runtime.
-# cache_load_timeout_seconds: 30
-
-# commitlog_sync may be either "periodic", "group", or "batch."
-#
-# When in batch mode, Cassandra won't ack writes until the commit log
-# has been flushed to disk. Each incoming write will trigger the flush task.
-# commitlog_sync_batch_window_in_ms is a deprecated value. Previously it had
-# almost no value, and is being removed.
-#
-# commitlog_sync_batch_window_in_ms: 2
-#
-# group mode is similar to batch mode, where Cassandra will not ack writes
-# until the commit log has been flushed to disk. The difference is group
-# mode will wait up to commitlog_sync_group_window_in_ms between flushes.
-#
-# commitlog_sync_group_window_in_ms: 1000
-#
-# the default option is "periodic" where writes may be acked immediately
-# and the CommitLog is simply synced every commitlog_sync_period_in_ms
-# milliseconds.
-commitlog_sync: periodic
-commitlog_sync_period_in_ms: 10000
-
-# When in periodic commitlog mode, the number of milliseconds to block writes
-# while waiting for a slow disk flush to complete.
-# periodic_commitlog_sync_lag_block_in_ms:
-
-# The size of the individual commitlog file segments. A commitlog
-# segment may be archived, deleted, or recycled once all the data
-# in it (potentially from each columnfamily in the system) has been
-# flushed to sstables.
-#
-# The default size is 32, which is almost always fine, but if you are
-# archiving commitlog segments (see commitlog_archiving.properties),
-# then you probably want a finer granularity of archiving; 8 or 16 MB
-# is reasonable.
-# Max mutation size is also configurable via max_mutation_size_in_kb setting in
-# cassandra.yaml. The default is half the size commitlog_segment_size_in_mb * 1024.
-# This should be positive and less than 2048.
-#
-# NOTE: If max_mutation_size_in_kb is set explicitly then commitlog_segment_size_in_mb must
-# be set to at least twice the size of max_mutation_size_in_kb / 1024
-#
-commitlog_segment_size_in_mb: 32
-
-# Compression to apply to the commit log. If omitted, the commit log
-# will be written uncompressed. LZ4, Snappy, and Deflate compressors
-# are supported.
-# commitlog_compression:
-# - class_name: LZ4Compressor
-# parameters:
-# -
-
-# Compression to apply to SSTables as they flush for compressed tables.
-# Note that tables without compression enabled do not respect this flag.
-#
-# As high ratio compressors like LZ4HC, Zstd, and Deflate can potentially
-# block flushes for too long, the default is to flush with a known fast
-# compressor in those cases. Options are:
-#
-# none : Flush without compressing blocks but while still doing checksums.
-# fast : Flush with a fast compressor. If the table is already using a
-# fast compressor that compressor is used.
-# table: Always flush with the same compressor that the table uses. This
-# was the pre 4.0 behavior.
-#
-# flush_compression: fast
-
-# any class that implements the SeedProvider interface and has a
-# constructor that takes a Map of parameters will do.
-seed_provider:
- # Addresses of hosts that are deemed contact points.
- # Cassandra nodes use this list of hosts to find each other and learn
- # the topology of the ring. You must change this if you are running
- # multiple nodes!
- - class_name: org.apache.cassandra.locator.SimpleSeedProvider
- parameters:
- # seeds is actually a comma-delimited list of addresses.
- # Ex: ",,"
- - seeds: "10.0.24.3"
-
-# For workloads with more data than can fit in memory, Cassandra's
-# bottleneck will be reads that need to fetch data from
-# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
-# order to allow the operations to enqueue low enough in the stack
-# that the OS and drives can reorder them. Same applies to
-# "concurrent_counter_writes", since counter writes read the current
-# values before incrementing and writing them back.
-#
-# On the other hand, since writes are almost never IO bound, the ideal
-# number of "concurrent_writes" is dependent on the number of cores in
-# your system; (8 * number_of_cores) is a good rule of thumb.
-concurrent_reads: 32
-concurrent_writes: 32
-concurrent_counter_writes: 32
-
-# For materialized view writes, as there is a read involved, so this should
-# be limited by the less of concurrent reads or concurrent writes.
-concurrent_materialized_view_writes: 32
-
-# Maximum memory to use for inter-node and client-server networking buffers.
-#
-# Defaults to the smaller of 1/16 of heap or 128MB. This pool is allocated off-heap,
-# so is in addition to the memory allocated for heap. The cache also has on-heap
-# overhead which is roughly 128 bytes per chunk (i.e. 0.2% of the reserved size
-# if the default 64k chunk size is used).
-# Memory is only allocated when needed.
-# networking_cache_size_in_mb: 128
-
-# Enable the sstable chunk cache. The chunk cache will store recently accessed
-# sections of the sstable in-memory as uncompressed buffers.
-# file_cache_enabled: false
-
-# Maximum memory to use for sstable chunk cache and buffer pooling.
-# 32MB of this are reserved for pooling buffers, the rest is used for chunk cache
-# that holds uncompressed sstable chunks.
-# Defaults to the smaller of 1/4 of heap or 512MB. This pool is allocated off-heap,
-# so is in addition to the memory allocated for heap. The cache also has on-heap
-# overhead which is roughly 128 bytes per chunk (i.e. 0.2% of the reserved size
-# if the default 64k chunk size is used).
-# Memory is only allocated when needed.
-# file_cache_size_in_mb: 512
-
-# Flag indicating whether to allocate on or off heap when the sstable buffer
-# pool is exhausted, that is when it has exceeded the maximum memory
-# file_cache_size_in_mb, beyond which it will not cache buffers but allocate on request.
-
-# buffer_pool_use_heap_if_exhausted: true
-
-# The strategy for optimizing disk read
-# Possible values are:
-# ssd (for solid state disks, the default)
-# spinning (for spinning disks)
-# disk_optimization_strategy: ssd
-
-# Total permitted memory to use for memtables. Cassandra will stop
-# accepting writes when the limit is exceeded until a flush completes,
-# and will trigger a flush based on memtable_cleanup_threshold
-# If omitted, Cassandra will set both to 1/4 the size of the heap.
-# memtable_heap_space_in_mb: 2048
-# memtable_offheap_space_in_mb: 2048
-
-# memtable_cleanup_threshold is deprecated. The default calculation
-# is the only reasonable choice. See the comments on memtable_flush_writers
-# for more information.
-#
-# Ratio of occupied non-flushing memtable size to total permitted size
-# that will trigger a flush of the largest memtable. Larger mct will
-# mean larger flushes and hence less compaction, but also less concurrent
-# flush activity which can make it difficult to keep your disks fed
-# under heavy write load.
-#
-# memtable_cleanup_threshold defaults to 1 / (memtable_flush_writers + 1)
-# memtable_cleanup_threshold: 0.11
-
-# Specify the way Cassandra allocates and manages memtable memory.
-# Options are:
-#
-# heap_buffers
-# on heap nio buffers
-#
-# offheap_buffers
-# off heap (direct) nio buffers
-#
-# offheap_objects
-# off heap objects
-memtable_allocation_type: heap_buffers
-
-# Limit memory usage for Merkle tree calculations during repairs. The default
-# is 1/16th of the available heap. The main tradeoff is that smaller trees
-# have less resolution, which can lead to over-streaming data. If you see heap
-# pressure during repairs, consider lowering this, but you cannot go below
-# one megabyte. If you see lots of over-streaming, consider raising
-# this or using subrange repair.
-#
-# For more details see https://issues.apache.org/jira/browse/CASSANDRA-14096.
-#
-# repair_session_space_in_mb:
-
-# Total space to use for commit logs on disk.
-#
-# If space gets above this value, Cassandra will flush every dirty CF
-# in the oldest segment and remove it. So a small total commitlog space
-# will tend to cause more flush activity on less-active columnfamilies.
-#
-# The default value is the smaller of 8192, and 1/4 of the total space
-# of the commitlog volume.
-#
-# commitlog_total_space_in_mb: 8192
-
-# This sets the number of memtable flush writer threads per disk
-# as well as the total number of memtables that can be flushed concurrently.
-# These are generally a combination of compute and IO bound.
-#
-# Memtable flushing is more CPU efficient than memtable ingest and a single thread
-# can keep up with the ingest rate of a whole server on a single fast disk
-# until it temporarily becomes IO bound under contention typically with compaction.
-# At that point you need multiple flush threads. At some point in the future
-# it may become CPU bound all the time.
-#
-# You can tell if flushing is falling behind using the MemtablePool.BlockedOnAllocation
-# metric which should be 0, but will be non-zero if threads are blocked waiting on flushing
-# to free memory.
-#
-# memtable_flush_writers defaults to two for a single data directory.
-# This means that two memtables can be flushed concurrently to the single data directory.
-# If you have multiple data directories the default is one memtable flushing at a time
-# but the flush will use a thread per data directory so you will get two or more writers.
-#
-# Two is generally enough to flush on a fast disk [array] mounted as a single data directory.
-# Adding more flush writers will result in smaller more frequent flushes that introduce more
-# compaction overhead.
-#
-# There is a direct tradeoff between number of memtables that can be flushed concurrently
-# and flush size and frequency. More is not better you just need enough flush writers
-# to never stall waiting for flushing to free memory.
-#
-#memtable_flush_writers: 2
-
-# Total space to use for change-data-capture logs on disk.
-#
-# If space gets above this value, Cassandra will throw WriteTimeoutException
-# on Mutations including tables with CDC enabled. A CDCCompactor is responsible
-# for parsing the raw CDC logs and deleting them when parsing is completed.
-#
-# The default value is the min of 4096 mb and 1/8th of the total space
-# of the drive where cdc_raw_directory resides.
-# cdc_total_space_in_mb: 4096
-
-# When we hit our cdc_raw limit and the CDCCompactor is either running behind
-# or experiencing backpressure, we check at the following interval to see if any
-# new space for cdc-tracked tables has been made available. Default to 250ms
-# cdc_free_space_check_interval_ms: 250
-
-# A fixed memory pool size in MB for for SSTable index summaries. If left
-# empty, this will default to 5% of the heap size. If the memory usage of
-# all index summaries exceeds this limit, SSTables with low read rates will
-# shrink their index summaries in order to meet this limit. However, this
-# is a best-effort process. In extreme conditions Cassandra may need to use
-# more than this amount of memory.
-index_summary_capacity_in_mb:
-
-# How frequently index summaries should be resampled. This is done
-# periodically to redistribute memory from the fixed-size pool to sstables
-# proportional their recent read rates. Setting to -1 will disable this
-# process, leaving existing index summaries at their current sampling level.
-index_summary_resize_interval_in_minutes: 60
-
-# Whether to, when doing sequential writing, fsync() at intervals in
-# order to force the operating system to flush the dirty
-# buffers. Enable this to avoid sudden dirty buffer flushing from
-# impacting read latencies. Almost always a good idea on SSDs; not
-# necessarily on platters.
-trickle_fsync: false
-trickle_fsync_interval_in_kb: 10240
-
-# TCP port, for commands and data
-# For security reasons, you should not expose this port to the internet. Firewall it if needed.
-storage_port: 7000
-
-# SSL port, for legacy encrypted communication. This property is unused unless enabled in
-# server_encryption_options (see below). As of cassandra 4.0, this property is deprecated
-# as a single port can be used for either/both secure and insecure connections.
-# For security reasons, you should not expose this port to the internet. Firewall it if needed.
-ssl_storage_port: 7001
-
-# Address or interface to bind to and tell other Cassandra nodes to connect to.
-# You _must_ change this if you want multiple nodes to be able to communicate!
-#
-# Set listen_address OR listen_interface, not both.
-#
-# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
-# will always do the Right Thing _if_ the node is properly configured
-# (hostname, name resolution, etc), and the Right Thing is to use the
-# address associated with the hostname (it might not be). If unresolvable
-# it will fall back to InetAddress.getLoopbackAddress(), which is wrong for production systems.
-#
-# Setting listen_address to 0.0.0.0 is always wrong.
-#
-listen_address: 10.0.24.3
-
-# Set listen_address OR listen_interface, not both. Interfaces must correspond
-# to a single address, IP aliasing is not supported.
-# listen_interface: eth0
-
-# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
-# you can specify which should be chosen using listen_interface_prefer_ipv6. If false the first ipv4
-# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
-# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
-# listen_interface_prefer_ipv6: false
-
-# Address to broadcast to other Cassandra nodes
-# Leaving this blank will set it to the same value as listen_address
-broadcast_address: 10.0.24.3
-
-# When using multiple physical network interfaces, set this
-# to true to listen on broadcast_address in addition to
-# the listen_address, allowing nodes to communicate in both
-# interfaces.
-# Ignore this property if the network configuration automatically
-# routes between the public and private networks such as EC2.
-# listen_on_broadcast_address: false
-
-# Internode authentication backend, implementing IInternodeAuthenticator;
-# used to allow/disallow connections from peer nodes.
-# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
-
-# Whether to start the native transport server.
-# The address on which the native transport is bound is defined by rpc_address.
-start_native_transport: true
-# port for the CQL native transport to listen for clients on
-# For security reasons, you should not expose this port to the internet. Firewall it if needed.
-native_transport_port: 9042
-# Enabling native transport encryption in client_encryption_options allows you to either use
-# encryption for the standard port or to use a dedicated, additional port along with the unencrypted
-# standard native_transport_port.
-# Enabling client encryption and keeping native_transport_port_ssl disabled will use encryption
-# for native_transport_port. Setting native_transport_port_ssl to a different value
-# from native_transport_port will use encryption for native_transport_port_ssl while
-# keeping native_transport_port unencrypted.
-# native_transport_port_ssl: 9142
-# The maximum threads for handling requests (note that idle threads are stopped
-# after 30 seconds so there is not corresponding minimum setting).
-# native_transport_max_threads: 128
-#
-# The maximum size of allowed frame. Frame (requests) larger than this will
-# be rejected as invalid. The default is 256MB. If you're changing this parameter,
-# you may want to adjust max_value_size_in_mb accordingly. This should be positive and less than 2048.
-# native_transport_max_frame_size_in_mb: 256
-
-# The maximum number of concurrent client connections.
-# The default is -1, which means unlimited.
-# native_transport_max_concurrent_connections: -1
-
-# The maximum number of concurrent client connections per source ip.
-# The default is -1, which means unlimited.
-# native_transport_max_concurrent_connections_per_ip: -1
-
-# Controls whether Cassandra honors older, yet currently supported, protocol versions.
-# The default is true, which means all supported protocols will be honored.
-native_transport_allow_older_protocols: true
-
-# Controls when idle client connections are closed. Idle connections are ones that had neither reads
-# nor writes for a time period.
-#
-# Clients may implement heartbeats by sending OPTIONS native protocol message after a timeout, which
-# will reset idle timeout timer on the server side. To close idle client connections, corresponding
-# values for heartbeat intervals have to be set on the client side.
-#
-# Idle connection timeouts are disabled by default.
-# native_transport_idle_timeout_in_ms: 60000
-
-# The address or interface to bind the native transport server to.
-#
-# Set rpc_address OR rpc_interface, not both.
-#
-# Leaving rpc_address blank has the same effect as on listen_address
-# (i.e. it will be based on the configured hostname of the node).
-#
-# Note that unlike listen_address, you can specify 0.0.0.0, but you must also
-# set broadcast_rpc_address to a value other than 0.0.0.0.
-#
-# For security reasons, you should not expose this port to the internet. Firewall it if needed.
-rpc_address: 0.0.0.0
-
-# Set rpc_address OR rpc_interface, not both. Interfaces must correspond
-# to a single address, IP aliasing is not supported.
-# rpc_interface: eth1
-
-# If you choose to specify the interface by name and the interface has an ipv4 and an ipv6 address
-# you can specify which should be chosen using rpc_interface_prefer_ipv6. If false the first ipv4
-# address will be used. If true the first ipv6 address will be used. Defaults to false preferring
-# ipv4. If there is only one address it will be selected regardless of ipv4/ipv6.
-# rpc_interface_prefer_ipv6: false
-
-# RPC address to broadcast to drivers and other Cassandra nodes. This cannot
-# be set to 0.0.0.0. If left blank, this will be set to the value of
-# rpc_address. If rpc_address is set to 0.0.0.0, broadcast_rpc_address must
-# be set.
-broadcast_rpc_address: 10.0.24.3
-
-# enable or disable keepalive on rpc/native connections
-rpc_keepalive: true
-
-# Uncomment to set socket buffer size for internode communication
-# Note that when setting this, the buffer size is limited by net.core.wmem_max
-# and when not setting it it is defined by net.ipv4.tcp_wmem
-# See also:
-# /proc/sys/net/core/wmem_max
-# /proc/sys/net/core/rmem_max
-# /proc/sys/net/ipv4/tcp_wmem
-# /proc/sys/net/ipv4/tcp_wmem
-# and 'man tcp'
-# internode_socket_send_buffer_size_in_bytes:
-
-# Uncomment to set socket buffer size for internode communication
-# Note that when setting this, the buffer size is limited by net.core.wmem_max
-# and when not setting it it is defined by net.ipv4.tcp_wmem
-# internode_socket_receive_buffer_size_in_bytes:
-
-# Set to true to have Cassandra create a hard link to each sstable
-# flushed or streamed locally in a backups/ subdirectory of the
-# keyspace data. Removing these links is the operator's
-# responsibility.
-incremental_backups: false
-
-# Whether or not to take a snapshot before each compaction. Be
-# careful using this option, since Cassandra won't clean up the
-# snapshots for you. Mostly useful if you're paranoid when there
-# is a data format change.
-snapshot_before_compaction: false
-
-# Whether or not a snapshot is taken of the data before keyspace truncation
-# or dropping of column families. The STRONGLY advised default of true
-# should be used to provide data safety. If you set this flag to false, you will
-# lose data on truncation or drop.
-auto_snapshot: true
-
-# The act of creating or clearing a snapshot involves creating or removing
-# potentially tens of thousands of links, which can cause significant performance
-# impact, especially on consumer grade SSDs. A non-zero value here can
-# be used to throttle these links to avoid negative performance impact of
-# taking and clearing snapshots
-snapshot_links_per_second: 0
-
-# Granularity of the collation index of rows within a partition.
-# Increase if your rows are large, or if you have a very large
-# number of rows per partition. The competing goals are these:
-#
-# - a smaller granularity means more index entries are generated
-# and looking up rows withing the partition by collation column
-# is faster
-# - but, Cassandra will keep the collation index in memory for hot
-# rows (as part of the key cache), so a larger granularity means
-# you can cache more hot rows
-column_index_size_in_kb: 64
-
-# Per sstable indexed key cache entries (the collation index in memory
-# mentioned above) exceeding this size will not be held on heap.
-# This means that only partition information is held on heap and the
-# index entries are read from disk.
-#
-# Note that this size refers to the size of the
-# serialized index information and not the size of the partition.
-column_index_cache_size_in_kb: 2
-
-# Number of simultaneous compactions to allow, NOT including
-# validation "compactions" for anti-entropy repair. Simultaneous
-# compactions can help preserve read performance in a mixed read/write
-# workload, by mitigating the tendency of small sstables to accumulate
-# during a single long running compactions. The default is usually
-# fine and if you experience problems with compaction running too
-# slowly or too fast, you should look at
-# compaction_throughput_mb_per_sec first.
-#
-# concurrent_compactors defaults to the smaller of (number of disks,
-# number of cores), with a minimum of 2 and a maximum of 8.
-#
-# If your data directories are backed by SSD, you should increase this
-# to the number of cores.
-#concurrent_compactors: 1
-
-# Number of simultaneous repair validations to allow. If not set or set to
-# a value less than 1, it defaults to the value of concurrent_compactors.
-# To set a value greeater than concurrent_compactors at startup, the system
-# property cassandra.allow_unlimited_concurrent_validations must be set to
-# true. To dynamically resize to a value > concurrent_compactors on a running
-# node, first call the bypassConcurrentValidatorsLimit method on the
-# org.apache.cassandra.db:type=StorageService mbean
-# concurrent_validations: 0
-
-# Number of simultaneous materialized view builder tasks to allow.
-concurrent_materialized_view_builders: 1
-
-# Throttles compaction to the given total throughput across the entire
-# system. The faster you insert data, the faster you need to compact in
-# order to keep the sstable count down, but in general, setting this to
-# 16 to 32 times the rate you are inserting data is more than sufficient.
-# Setting this to 0 disables throttling. Note that this accounts for all types
-# of compaction, including validation compaction (building Merkle trees
-# for repairs).
-compaction_throughput_mb_per_sec: 64
-
-# When compacting, the replacement sstable(s) can be opened before they
-# are completely written, and used in place of the prior sstables for
-# any range that has been written. This helps to smoothly transfer reads
-# between the sstables, reducing page cache churn and keeping hot rows hot
-sstable_preemptive_open_interval_in_mb: 50
-
-# When enabled, permits Cassandra to zero-copy stream entire eligible
-# SSTables between nodes, including every component.
-# This speeds up the network transfer significantly subject to
-# throttling specified by stream_throughput_outbound_megabits_per_sec.
-# Enabling this will reduce the GC pressure on sending and receiving node.
-# When unset, the default is enabled. While this feature tries to keep the
-# disks balanced, it cannot guarantee it. This feature will be automatically
-# disabled if internode encryption is enabled.
-# stream_entire_sstables: true
-
-# Throttles all outbound streaming file transfers on this node to the
-# given total throughput in Mbps. This is necessary because Cassandra does
-# mostly sequential IO when streaming data during bootstrap or repair, which
-# can lead to saturating the network connection and degrading rpc performance.
-# When unset, the default is 200 Mbps or 25 MB/s.
-# stream_throughput_outbound_megabits_per_sec: 200
-
-# Throttles all streaming file transfer between the datacenters,
-# this setting allows users to throttle inter dc stream throughput in addition
-# to throttling all network stream traffic as configured with
-# stream_throughput_outbound_megabits_per_sec
-# When unset, the default is 200 Mbps or 25 MB/s
-# inter_dc_stream_throughput_outbound_megabits_per_sec: 200
-
-# Server side timeouts for requests. The server will return a timeout exception
-# to the client if it can't complete an operation within the corresponding
-# timeout. Those settings are a protection against:
-# 1) having client wait on an operation that might never terminate due to some
-# failures.
-# 2) operations that use too much CPU/read too much data (leading to memory build
-# up) by putting a limit to how long an operation will execute.
-# For this reason, you should avoid putting these settings too high. In other words,
-# if you are timing out requests because of underlying resource constraints then
-# increasing the timeout will just cause more problems. Of course putting them too
-# low is equally ill-advised since clients could get timeouts even for successful
-# operations just because the timeout setting is too tight.
-
-# How long the coordinator should wait for read operations to complete.
-# Lowest acceptable value is 10 ms.
-read_request_timeout_in_ms: 5000
-# How long the coordinator should wait for seq or index scans to complete.
-# Lowest acceptable value is 10 ms.
-range_request_timeout_in_ms: 10000
-# How long the coordinator should wait for writes to complete.
-# Lowest acceptable value is 10 ms.
-write_request_timeout_in_ms: 2000
-# How long the coordinator should wait for counter writes to complete.
-# Lowest acceptable value is 10 ms.
-counter_write_request_timeout_in_ms: 5000
-# How long a coordinator should continue to retry a CAS operation
-# that contends with other proposals for the same row.
-# Lowest acceptable value is 10 ms.
-cas_contention_timeout_in_ms: 1000
-# How long the coordinator should wait for truncates to complete
-# (This can be much longer, because unless auto_snapshot is disabled
-# we need to flush first so we can snapshot before removing the data.)
-# Lowest acceptable value is 10 ms.
-truncate_request_timeout_in_ms: 60000
-# The default timeout for other, miscellaneous operations.
-# Lowest acceptable value is 10 ms.
-request_timeout_in_ms: 10000
-
-# Defensive settings for protecting Cassandra from true network partitions.
-# See (CASSANDRA-14358) for details.
-#
-# The amount of time to wait for internode tcp connections to establish.
-# internode_tcp_connect_timeout_in_ms: 2000
-#
-# The amount of time unacknowledged data is allowed on a connection before we throw out the connection
-# Note this is only supported on Linux + epoll, and it appears to behave oddly above a setting of 30000
-# (it takes much longer than 30s) as of Linux 4.12. If you want something that high set this to 0
-# which picks up the OS default and configure the net.ipv4.tcp_retries2 sysctl to be ~8.
-# internode_tcp_user_timeout_in_ms: 30000
-
-# The amount of time unacknowledged data is allowed on a streaming connection.
-# The default is 5 minutes. Increase it or set it to 0 in order to increase the timeout.
-# internode_streaming_tcp_user_timeout_in_ms: 300000
-
-# Global, per-endpoint and per-connection limits imposed on messages queued for delivery to other nodes
-# and waiting to be processed on arrival from other nodes in the cluster. These limits are applied to the on-wire
-# size of the message being sent or received.
-#
-# The basic per-link limit is consumed in isolation before any endpoint or global limit is imposed.
-# Each node-pair has three links: urgent, small and large. So any given node may have a maximum of
-# N*3*(internode_application_send_queue_capacity_in_bytes+internode_application_receive_queue_capacity_in_bytes)
-# messages queued without any coordination between them although in practice, with token-aware routing, only RF*tokens
-# nodes should need to communicate with significant bandwidth.
-#
-# The per-endpoint limit is imposed on all messages exceeding the per-link limit, simultaneously with the global limit,
-# on all links to or from a single node in the cluster.
-# The global limit is imposed on all messages exceeding the per-link limit, simultaneously with the per-endpoint limit,
-# on all links to or from any node in the cluster.
-#
-# internode_application_send_queue_capacity_in_bytes: 4194304 #4MiB
-# internode_application_send_queue_reserve_endpoint_capacity_in_bytes: 134217728 #128MiB
-# internode_application_send_queue_reserve_global_capacity_in_bytes: 536870912 #512MiB
-# internode_application_receive_queue_capacity_in_bytes: 4194304 #4MiB
-# internode_application_receive_queue_reserve_endpoint_capacity_in_bytes: 134217728 #128MiB
-# internode_application_receive_queue_reserve_global_capacity_in_bytes: 536870912 #512MiB
-
-
-# How long before a node logs slow queries. Select queries that take longer than
-# this timeout to execute, will generate an aggregated log message, so that slow queries
-# can be identified. Set this value to zero to disable slow query logging.
-slow_query_log_timeout_in_ms: 500
-
-# Enable operation timeout information exchange between nodes to accurately
-# measure request timeouts. If disabled, replicas will assume that requests
-# were forwarded to them instantly by the coordinator, which means that
-# under overload conditions we will waste that much extra time processing
-# already-timed-out requests.
-#
-# Warning: It is generally assumed that users have setup NTP on their clusters, and that clocks are modestly in sync,
-# since this is a requirement for general correctness of last write wins.
-#cross_node_timeout: true
-
-# Set keep-alive period for streaming
-# This node will send a keep-alive message periodically with this period.
-# If the node does not receive a keep-alive message from the peer for
-# 2 keep-alive cycles the stream session times out and fail
-# Default value is 300s (5 minutes), which means stalled stream
-# times out in 10 minutes by default
-# streaming_keep_alive_period_in_secs: 300
-
-# Limit number of connections per host for streaming
-# Increase this when you notice that joins are CPU-bound rather that network
-# bound (for example a few nodes with big files).
-# streaming_connections_per_host: 1
-
-
-# phi value that must be reached for a host to be marked down.
-# most users should never need to adjust this.
-# phi_convict_threshold: 8
-
-# endpoint_snitch -- Set this to a class that implements
-# IEndpointSnitch. The snitch has two functions:
-#
-# - it teaches Cassandra enough about your network topology to route
-# requests efficiently
-# - it allows Cassandra to spread replicas around your cluster to avoid
-# correlated failures. It does this by grouping machines into
-# "datacenters" and "racks." Cassandra will do its best not to have
-# more than one replica on the same "rack" (which may not actually
-# be a physical location)
-#
-# CASSANDRA WILL NOT ALLOW YOU TO SWITCH TO AN INCOMPATIBLE SNITCH
-# ONCE DATA IS INSERTED INTO THE CLUSTER. This would cause data loss.
-# This means that if you start with the default SimpleSnitch, which
-# locates every node on "rack1" in "datacenter1", your only options
-# if you need to add another datacenter are GossipingPropertyFileSnitch
-# (and the older PFS). From there, if you want to migrate to an
-# incompatible snitch like Ec2Snitch you can do it by adding new nodes
-# under Ec2Snitch (which will locate them in a new "datacenter") and
-# decommissioning the old ones.
-#
-# Out of the box, Cassandra provides:
-#
-# SimpleSnitch:
-# Treats Strategy order as proximity. This can improve cache
-# locality when disabling read repair. Only appropriate for
-# single-datacenter deployments.
-#
-# GossipingPropertyFileSnitch
-# This should be your go-to snitch for production use. The rack
-# and datacenter for the local node are defined in
-# cassandra-rackdc.properties and propagated to other nodes via
-# gossip. If cassandra-topology.properties exists, it is used as a
-# fallback, allowing migration from the PropertyFileSnitch.
-#
-# PropertyFileSnitch:
-# Proximity is determined by rack and data center, which are
-# explicitly configured in cassandra-topology.properties.
-#
-# Ec2Snitch:
-# Appropriate for EC2 deployments in a single Region. Loads Region
-# and Availability Zone information from the EC2 API. The Region is
-# treated as the datacenter, and the Availability Zone as the rack.
-# Only private IPs are used, so this will not work across multiple
-# Regions.
-#
-# Ec2MultiRegionSnitch:
-# Uses public IPs as broadcast_address to allow cross-region
-# connectivity. (Thus, you should set seed addresses to the public
-# IP as well.) You will need to open the storage_port or
-# ssl_storage_port on the public IP firewall. (For intra-Region
-# traffic, Cassandra will switch to the private IP after
-# establishing a connection.)
-#
-# RackInferringSnitch:
-# Proximity is determined by rack and data center, which are
-# assumed to correspond to the 3rd and 2nd octet of each node's IP
-# address, respectively. Unless this happens to match your
-# deployment conventions, this is best used as an example of
-# writing a custom Snitch class and is provided in that spirit.
-#
-# You can use a custom Snitch by setting this to the full class name
-# of the snitch, which will be assumed to be on your classpath.
-endpoint_snitch: SimpleSnitch
-
-# controls how often to perform the more expensive part of host score
-# calculation
-dynamic_snitch_update_interval_in_ms: 100
-# controls how often to reset all host scores, allowing a bad host to
-# possibly recover
-dynamic_snitch_reset_interval_in_ms: 600000
-# if set greater than zero, this will allow
-# 'pinning' of replicas to hosts in order to increase cache capacity.
-# The badness threshold will control how much worse the pinned host has to be
-# before the dynamic snitch will prefer other replicas over it. This is
-# expressed as a double which represents a percentage. Thus, a value of
-# 0.2 means Cassandra would continue to prefer the static snitch values
-# until the pinned host was 20% worse than the fastest.
-dynamic_snitch_badness_threshold: 1.0
-
-# Configure server-to-server internode encryption
-#
-# JVM and netty defaults for supported SSL socket protocols and cipher suites can
-# be replaced using custom encryption options. This is not recommended
-# unless you have policies in place that dictate certain settings, or
-# need to disable vulnerable ciphers or protocols in case the JVM cannot
-# be updated.
-#
-# FIPS compliant settings can be configured at JVM level and should not
-# involve changing encryption settings here:
-# https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/FIPS.html
-#
-# **NOTE** this default configuration is an insecure configuration. If you need to
-# enable server-to-server encryption generate server keystores (and truststores for mutual
-# authentication) per:
-# http://download.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
-# Then perform the following configuration changes:
-#
-# Step 1: Set internode_encryption= and explicitly set optional=true. Restart all nodes
-#
-# Step 2: Set optional=false (or remove it) and if you generated truststores and want to use mutual
-# auth set require_client_auth=true. Restart all nodes
-server_encryption_options:
- # On outbound connections, determine which type of peers to securely connect to.
- # The available options are :
- # none : Do not encrypt outgoing connections
- # dc : Encrypt connections to peers in other datacenters but not within datacenters
- # rack : Encrypt connections to peers in other racks but not within racks
- # all : Always use encrypted connections
- internode_encryption: none
- # When set to true, encrypted and unencrypted connections are allowed on the storage_port
- # This should _only be true_ while in unencrypted or transitional operation
- # optional defaults to true if internode_encryption is none
- # optional: true
- # If enabled, will open up an encrypted listening socket on ssl_storage_port. Should only be used
- # during upgrade to 4.0; otherwise, set to false.
- enable_legacy_ssl_storage_port: false
- # Set to a valid keystore if internode_encryption is dc, rack or all
- keystore: conf/.keystore
- keystore_password: cassandra
- # Verify peer server certificates
- require_client_auth: false
- # Set to a valid trustore if require_client_auth is true
- truststore: conf/.truststore
- truststore_password: cassandra
- # Verify that the host name in the certificate matches the connected host
- require_endpoint_verification: false
- # More advanced defaults:
- # protocol: TLS
- # store_type: JKS
- # cipher_suites: [
- # TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- # TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA,
- # TLS_RSA_WITH_AES_256_CBC_SHA
- # ]
-
-# Configure client-to-server encryption.
-#
-# **NOTE** this default configuration is an insecure configuration. If you need to
-# enable client-to-server encryption generate server keystores (and truststores for mutual
-# authentication) per:
-# http://download.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
-# Then perform the following configuration changes:
-#
-# Step 1: Set enabled=true and explicitly set optional=true. Restart all nodes
-#
-# Step 2: Set optional=false (or remove it) and if you generated truststores and want to use mutual
-# auth set require_client_auth=true. Restart all nodes
-client_encryption_options:
- # Enable client-to-server encryption
- enabled: false
- # When set to true, encrypted and unencrypted connections are allowed on the native_transport_port
- # This should _only be true_ while in unencrypted or transitional operation
- # optional defaults to true when enabled is false, and false when enabled is true.
- # optional: true
- # Set keystore and keystore_password to valid keystores if enabled is true
- keystore: conf/.keystore
- keystore_password: cassandra
- # Verify client certificates
- require_client_auth: false
- # Set trustore and truststore_password if require_client_auth is true
- # truststore: conf/.truststore
- # truststore_password: cassandra
- # More advanced defaults:
- # protocol: TLS
- # store_type: JKS
- # cipher_suites: [
- # TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
- # TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- # TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA,
- # TLS_RSA_WITH_AES_256_CBC_SHA
- # ]
-
-# internode_compression controls whether traffic between nodes is
-# compressed.
-# Can be:
-#
-# all
-# all traffic is compressed
-#
-# dc
-# traffic between different datacenters is compressed
-#
-# none
-# nothing is compressed.
-internode_compression: dc
-
-# Enable or disable tcp_nodelay for inter-dc communication.
-# Disabling it will result in larger (but fewer) network packets being sent,
-# reducing overhead from the TCP protocol itself, at the cost of increasing
-# latency if you block for cross-datacenter responses.
-inter_dc_tcp_nodelay: false
-
-# TTL for different trace types used during logging of the repair process.
-tracetype_query_ttl: 86400
-tracetype_repair_ttl: 604800
-
-# If unset, all GC Pauses greater than gc_log_threshold_in_ms will log at
-# INFO level
-# UDFs (user defined functions) are disabled by default.
-# As of Cassandra 3.0 there is a sandbox in place that should prevent execution of evil code.
-enable_user_defined_functions: false
-
-# Enables scripted UDFs (JavaScript UDFs).
-# Java UDFs are always enabled, if enable_user_defined_functions is true.
-# Enable this option to be able to use UDFs with "language javascript" or any custom JSR-223 provider.
-# This option has no effect, if enable_user_defined_functions is false.
-enable_scripted_user_defined_functions: false
-
-# The default Windows kernel timer and scheduling resolution is 15.6ms for power conservation.
-# Lowering this value on Windows can provide much tighter latency and better throughput, however
-# some virtualized environments may see a negative performance impact from changing this setting
-# below their system default. The sysinternals 'clockres' tool can confirm your system's default
-# setting.
-windows_timer_interval: 1
-
-
-# Enables encrypting data at-rest (on disk). Different key providers can be plugged in, but the default reads from
-# a JCE-style keystore. A single keystore can hold multiple keys, but the one referenced by
-# the "key_alias" is the only key that will be used for encrypt opertaions; previously used keys
-# can still (and should!) be in the keystore and will be used on decrypt operations
-# (to handle the case of key rotation).
-#
-# It is strongly recommended to download and install Java Cryptography Extension (JCE)
-# Unlimited Strength Jurisdiction Policy Files for your version of the JDK.
-# (current link: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html)
-#
-# Currently, only the following file types are supported for transparent data encryption, although
-# more are coming in future cassandra releases: commitlog, hints
-transparent_data_encryption_options:
- enabled: false
- chunk_length_kb: 64
- cipher: AES/CBC/PKCS5Padding
- key_alias: testing:1
- # CBC IV length for AES needs to be 16 bytes (which is also the default size)
- # iv_length: 16
- key_provider:
- - class_name: org.apache.cassandra.security.JKSKeyProvider
- parameters:
- - keystore: conf/.keystore
- keystore_password: cassandra
- store_type: JCEKS
- key_password: cassandra
-
-
-#####################
-# SAFETY THRESHOLDS #
-#####################
-
-# When executing a scan, within or across a partition, we need to keep the
-# tombstones seen in memory so we can return them to the coordinator, which
-# will use them to make sure other replicas also know about the deleted rows.
-# With workloads that generate a lot of tombstones, this can cause performance
-# problems and even exaust the server heap.
-# (http://www.datastax.com/dev/blog/cassandra-anti-patterns-queues-and-queue-like-datasets)
-# Adjust the thresholds here if you understand the dangers and want to
-# scan more tombstones anyway. These thresholds may also be adjusted at runtime
-# using the StorageService mbean.
-tombstone_warn_threshold: 1000
-tombstone_failure_threshold: 100000
-
-# Filtering and secondary index queries at read consistency levels above ONE/LOCAL_ONE use a
-# mechanism called replica filtering protection to ensure that results from stale replicas do
-# not violate consistency. (See CASSANDRA-8272 and CASSANDRA-15907 for more details.) This
-# mechanism materializes replica results by partition on-heap at the coordinator. The more possibly
-# stale results returned by the replicas, the more rows materialized during the query.
-replica_filtering_protection:
- # These thresholds exist to limit the damage severely out-of-date replicas can cause during these
- # queries. They limit the number of rows from all replicas individual index and filtering queries
- # can materialize on-heap to return correct results at the desired read consistency level.
- #
- # "cached_replica_rows_warn_threshold" is the per-query threshold at which a warning will be logged.
- # "cached_replica_rows_fail_threshold" is the per-query threshold at which the query will fail.
- #
- # These thresholds may also be adjusted at runtime using the StorageService mbean.
- #
- # If the failure threshold is breached, it is likely that either the current page/fetch size
- # is too large or one or more replicas is severely out-of-sync and in need of repair.
- cached_rows_warn_threshold: 2000
- cached_rows_fail_threshold: 32000
-
-# Log WARN on any multiple-partition batch size exceeding this value. 5kb per batch by default.
-# Caution should be taken on increasing the size of this threshold as it can lead to node instability.
-batch_size_warn_threshold_in_kb: 5
-
-# Fail any multiple-partition batch exceeding this value. 50kb (10x warn threshold) by default.
-batch_size_fail_threshold_in_kb: 50
-
-# Log WARN on any batches not of type LOGGED than span across more partitions than this limit
-unlogged_batch_across_partitions_warn_threshold: 10
-
-# Log a warning when compacting partitions larger than this value
-compaction_large_partition_warning_threshold_mb: 100
-
-# GC Pauses greater than 200 ms will be logged at INFO level
-# This threshold can be adjusted to minimize logging if necessary
-# gc_log_threshold_in_ms: 200
-
-# GC Pauses greater than gc_warn_threshold_in_ms will be logged at WARN level
-# Adjust the threshold based on your application throughput requirement. Setting to 0
-# will deactivate the feature.
-# gc_warn_threshold_in_ms: 1000
-
-# Maximum size of any value in SSTables. Safety measure to detect SSTable corruption
-# early. Any value size larger than this threshold will result into marking an SSTable
-# as corrupted. This should be positive and less than 2048.
-# max_value_size_in_mb: 256
-
-# Track a metric per keyspace indicating whether replication achieved the ideal consistency
-# level for writes without timing out. This is different from the consistency level requested by
-# each write which may be lower in order to facilitate availability.
-# ideal_consistency_level: EACH_QUORUM
-
-# Automatically upgrade sstables after upgrade - if there is no ordinary compaction to do, the
-# oldest non-upgraded sstable will get upgraded to the latest version
-# automatic_sstable_upgrade: false
-# Limit the number of concurrent sstable upgrades
-# max_concurrent_automatic_sstable_upgrades: 1
-
-# Audit logging - Logs every incoming CQL command request, authentication to a node. See the docs
-# on audit_logging for full details about the various configuration options.
-audit_logging_options:
- enabled: false
- logger:
- - class_name: BinAuditLogger
- # audit_logs_dir:
- # included_keyspaces:
- # excluded_keyspaces: system, system_schema, system_virtual_schema
- # included_categories:
- # excluded_categories:
- # included_users:
- # excluded_users:
- # roll_cycle: HOURLY
- # block: true
- # max_queue_weight: 268435456 # 256 MiB
- # max_log_size: 17179869184 # 16 GiB
- ## archive command is "/path/to/script.sh %path" where %path is replaced with the file being rolled:
- # archive_command:
- # max_archive_retries: 10
-
-
- # default options for full query logging - these can be overridden from command line when executing
- # nodetool enablefullquerylog
- #full_query_logging_options:
- # log_dir:
- # roll_cycle: HOURLY
- # block: true
- # max_queue_weight: 268435456 # 256 MiB
- # max_log_size: 17179869184 # 16 GiB
- ## archive command is "/path/to/script.sh %path" where %path is replaced with the file being rolled:
- # archive_command:
- # max_archive_retries: 10
-
-# validate tombstones on reads and compaction
-# can be either "disabled", "warn" or "exception"
-# corrupted_tombstone_strategy: disabled
-
-# Diagnostic Events #
-# If enabled, diagnostic events can be helpful for troubleshooting operational issues. Emitted events contain details
-# on internal state and temporal relationships across events, accessible by clients via JMX.
-diagnostic_events_enabled: false
-
-# Use native transport TCP message coalescing. If on upgrade to 4.0 you found your throughput decreasing, and in
-# particular you run an old kernel or have very fewer client connections, this option might be worth evaluating.
-#native_transport_flush_in_batches_legacy: false
-
-# Enable tracking of repaired state of data during reads and comparison between replicas
-# Mismatches between the repaired sets of replicas can be characterized as either confirmed
-# or unconfirmed. In this context, unconfirmed indicates that the presence of pending repair
-# sessions, unrepaired partition tombstones, or some other condition means that the disparity
-# cannot be considered conclusive. Confirmed mismatches should be a trigger for investigation
-# as they may be indicative of corruption or data loss.
-# There are separate flags for range vs partition reads as single partition reads are only tracked
-# when CL > 1 and a digest mismatch occurs. Currently, range queries don't use digests so if
-# enabled for range reads, all range reads will include repaired data tracking. As this adds
-# some overhead, operators may wish to disable it whilst still enabling it for partition reads
-repaired_data_tracking_for_range_reads_enabled: false
-repaired_data_tracking_for_partition_reads_enabled: false
-# If false, only confirmed mismatches will be reported. If true, a separate metric for unconfirmed
-# mismatches will also be recorded. This is to avoid potential signal:noise issues are unconfirmed
-# mismatches are less actionable than confirmed ones.
-report_unconfirmed_repaired_data_mismatches: false
-
-# Having many tables and/or keyspaces negatively affects performance of many operations in the
-# cluster. When the number of tables/keyspaces in the cluster exceeds the following thresholds
-# a client warning will be sent back to the user when creating a table or keyspace.
-# table_count_warn_threshold: 150
-# keyspace_count_warn_threshold: 40
-
-#########################
-# EXPERIMENTAL FEATURES #
-#########################
-
-# Enables materialized view creation on this node.
-# Materialized views are considered experimental and are not recommended for production use.
-enable_materialized_views: false
-
-# Enables SASI index creation on this node.
-# SASI indexes are considered experimental and are not recommended for production use.
-enable_sasi_indexes: true
-
-# Enables creation of transiently replicated keyspaces on this node.
-# Transient replication is experimental and is not recommended for production use.
-enable_transient_replication: false
-
-# Enables the used of 'ALTER ... DROP COMPACT STORAGE' statements on this node.
-# 'ALTER ... DROP COMPACT STORAGE' is considered experimental and is not recommended for production use.
-enable_drop_compact_storage: false
\ No newline at end of file
diff --git a/services/jobs/pom.xml b/services/jobs/pom.xml
index 53281e1e..d6d51d0f 100644
--- a/services/jobs/pom.xml
+++ b/services/jobs/pom.xml
@@ -48,19 +48,26 @@
shared${project.version}
-
- javassist
- javassist
- 3.12.1.GA
- org.springframework.bootspring-boot-starter-test
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+ testorg.springframework.bootspring-boot-testcontainers
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+ test
@@ -94,6 +101,12 @@
org.springframework.bootspring-boot-starter-data-cassandra
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+ org.projectlombok
diff --git a/services/jobs/src/main/java/com/workup/jobs/ControllerMQListener.java b/services/jobs/src/main/java/com/workup/jobs/ControllerMQListener.java
index 1a63839a..8ef28ae6 100644
--- a/services/jobs/src/main/java/com/workup/jobs/ControllerMQListener.java
+++ b/services/jobs/src/main/java/com/workup/jobs/ControllerMQListener.java
@@ -1,38 +1,123 @@
package com.workup.jobs;
+import com.workup.jobs.commands.JobCommand;
import com.workup.jobs.commands.JobCommandMap;
-import com.workup.shared.commands.controller.SetMaxThreadsRequest;
+import com.workup.shared.commands.Command;
+import com.workup.shared.commands.CommandRequest;
+import com.workup.shared.commands.CommandResponse;
+import com.workup.shared.commands.controller.*;
+import com.workup.shared.enums.ServiceQueueNames;
+import com.workup.shared.enums.ThreadPoolSize;
import java.lang.reflect.Field;
-import javassist.*;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configurator;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
@Service
-@RabbitListener(queues = "#{controllerQueue.name}")
+@RabbitListener(queues = "#{controllerQueue.name}", id = "#{controllerQueue.name}")
public class ControllerMQListener {
-
@Autowired public JobCommandMap commandMap;
@Autowired public ThreadPoolTaskExecutor taskExecutor;
-
@Autowired private ApplicationContext context;
+ @Autowired private RabbitListenerEndpointRegistry registry;
@RabbitHandler
public void receive(SetMaxThreadsRequest in) throws Exception {
try {
- ThreadPoolTaskExecutor myBean = context.getBean(ThreadPoolTaskExecutor.class);
- Field maxPoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("maxPoolSize");
- maxPoolSize.setAccessible(true);
- maxPoolSize.set(myBean, in.getMaxThreads());
- Field corePoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("corePoolSize");
- corePoolSize.setAccessible(true);
- corePoolSize.set(myBean, in.getMaxThreads());
+ System.out.println("Max threads is: " + taskExecutor.getMaxPoolSize());
+ setThreads(in.getMaxThreads());
+ ThreadPoolSize.POOL_SIZE = taskExecutor.getMaxPoolSize();
+ System.out.println("Max threads set to: " + taskExecutor.getMaxPoolSize());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(SetLoggingLevelRequest in) throws Exception {
+ try {
+ Logger logger = LogManager.getRootLogger();
+ Configurator.setAllLevels(logger.getName(), Level.valueOf(in.getLevel()));
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(FreezeRequest in) throws Exception {
+ try {
+ registry.getListenerContainer(ServiceQueueNames.JOBS).stop();
+ taskExecutor.shutdown();
+ setThreads(0);
+ System.out.println("Stopped all threads.");
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
+
+ @RabbitHandler
+ public void receive(ContinueRequest in) throws Exception {
+ try {
+ taskExecutor.start();
+ setThreads(ThreadPoolSize.POOL_SIZE);
+ registry.getListenerContainer(ServiceQueueNames.JOBS).start();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @RabbitHandler
+ public void receive(UpdateCommandRequest in) throws Exception {
+ try {
+ String className = commandMap.getCommand(in.getCommandName()).getClass().getName();
+ byte[] byteArray = in.getByteCode();
+ Class> clazz =
+ (Class>)
+ (new MyClassLoader(this.getClass().getClassLoader()).loadClass(byteArray, className));
+
+ commandMap.replaceCommand(
+ in.getCommandName(),
+ (Class extends JobCommand extends CommandRequest, ? extends CommandResponse>>)
+ ((Command, ?>) clazz.newInstance()).getClass());
+
+ System.out.println("Updated command: " + in.getCommandName());
+ // clazz.newInstance().Run(null);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ static class MyClassLoader extends ClassLoader {
+ public MyClassLoader(ClassLoader classLoader) {
+ super(classLoader);
+ }
+
+ public Class> loadClass(byte[] byteCode, String className) {
+ return defineClass(className, byteCode, 0, byteCode.length);
+ }
+ }
+
+ private void setThreads(int threads) throws NoSuchFieldException, IllegalAccessException {
+ ThreadPoolTaskExecutor myBean = context.getBean(ThreadPoolTaskExecutor.class);
+ Field maxPoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("maxPoolSize");
+ maxPoolSize.setAccessible(true);
+ maxPoolSize.set(myBean, threads);
+ Field corePoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("corePoolSize");
+ corePoolSize.setAccessible(true);
+ corePoolSize.set(myBean, threads);
+ }
}
diff --git a/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java b/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java
index 01eb15b9..4ae506ef 100644
--- a/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java
+++ b/services/jobs/src/main/java/com/workup/jobs/JobsApplication.java
@@ -2,6 +2,7 @@
import com.workup.shared.enums.ControllerQueueNames;
import com.workup.shared.enums.ServiceQueueNames;
+import com.workup.shared.enums.ThreadPoolSize;
import org.springframework.amqp.core.*;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
@@ -56,8 +57,9 @@ public MessageConverter messageConverter() {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(50);
- executor.setMaxPoolSize(50);
+ executor.setCorePoolSize(ThreadPoolSize.POOL_SIZE);
+ executor.setMaxPoolSize(ThreadPoolSize.POOL_SIZE);
+ executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("jobs-");
executor.initialize();
diff --git a/services/jobs/src/main/java/com/workup/jobs/RabbitMQListener.java b/services/jobs/src/main/java/com/workup/jobs/RabbitMQListener.java
index 847f20c3..76e5a1b7 100644
--- a/services/jobs/src/main/java/com/workup/jobs/RabbitMQListener.java
+++ b/services/jobs/src/main/java/com/workup/jobs/RabbitMQListener.java
@@ -1,7 +1,6 @@
package com.workup.jobs;
import com.workup.jobs.commands.AcceptProposalCommand;
-import com.workup.jobs.commands.CreateJobCommand;
import com.workup.jobs.commands.CreateProposalCommand;
import com.workup.jobs.commands.GetJobByIdCommand;
import com.workup.jobs.commands.GetMyJobsCommand;
@@ -9,6 +8,8 @@
import com.workup.jobs.commands.GetProposalsByJobIdCommand;
import com.workup.jobs.commands.JobCommandMap;
import com.workup.jobs.commands.SearchJobsCommand;
+import com.workup.shared.commands.Command;
+import com.workup.shared.commands.CommandRequest;
import com.workup.shared.commands.jobs.proposals.requests.AcceptProposalRequest;
import com.workup.shared.commands.jobs.proposals.requests.CreateProposalRequest;
import com.workup.shared.commands.jobs.proposals.requests.GetMyProposalsRequest;
@@ -34,7 +35,7 @@
import org.springframework.stereotype.Service;
@Service
-@RabbitListener(queues = ServiceQueueNames.JOBS)
+@RabbitListener(queues = ServiceQueueNames.JOBS, id = ServiceQueueNames.JOBS)
public class RabbitMQListener {
@Autowired public JobCommandMap commandMap;
@@ -42,8 +43,10 @@ public class RabbitMQListener {
@RabbitHandler
@Async
public CompletableFuture receive(CreateJobRequest in) throws Exception {
- CreateJobResponse response = ((CreateJobCommand) commandMap.getCommand("CreateJob")).Run(in);
- return CompletableFuture.completedFuture(response);
+ CreateJobResponse resp =
+ (CreateJobResponse)
+ ((Command) commandMap.getCommand("CreateJob")).Run(in);
+ return CompletableFuture.completedFuture(resp);
}
@RabbitHandler
diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/JobCommand.java b/services/jobs/src/main/java/com/workup/jobs/commands/JobCommand.java
index de96129e..1b46a9f7 100644
--- a/services/jobs/src/main/java/com/workup/jobs/commands/JobCommand.java
+++ b/services/jobs/src/main/java/com/workup/jobs/commands/JobCommand.java
@@ -11,9 +11,9 @@
public abstract class JobCommand
implements Command {
- @Setter JobRepository jobRepository;
+ @Setter public JobRepository jobRepository;
- @Setter ProposalRepository proposalRepository;
+ @Setter public ProposalRepository proposalRepository;
- @Setter AmqpTemplate rabbitTemplate;
+ @Setter public AmqpTemplate rabbitTemplate;
}
diff --git a/services/jobs/src/main/java/com/workup/jobs/commands/JobCommandMap.java b/services/jobs/src/main/java/com/workup/jobs/commands/JobCommandMap.java
index 8bc9456a..3de55692 100644
--- a/services/jobs/src/main/java/com/workup/jobs/commands/JobCommandMap.java
+++ b/services/jobs/src/main/java/com/workup/jobs/commands/JobCommandMap.java
@@ -13,11 +13,11 @@
public class JobCommandMap
extends CommandMap> {
- @Autowired JobRepository jobRepository;
+ @Autowired public JobRepository jobRepository;
- @Autowired ProposalRepository proposalRepository;
+ @Autowired public ProposalRepository proposalRepository;
- @Autowired AmqpTemplate rabbitTemplate;
+ @Autowired public AmqpTemplate rabbitTemplate;
public void registerCommands() {
commands.put("CreateJob", CreateJobCommand.class);
diff --git a/services/jobs/src/main/resources/application.properties b/services/jobs/src/main/resources/application.properties
index 61221848..ce5d1578 100644
--- a/services/jobs/src/main/resources/application.properties
+++ b/services/jobs/src/main/resources/application.properties
@@ -5,4 +5,4 @@ spring.rabbitmq.password=guest
spring.cassandra.local-datacenter=datacenter1
spring.cassandra.keyspace-name=jobs_data
spring.cassandra.contact-points=${JOBS_DB_URL}
-spring.cassandra.schema-action=CREATE_IF_NOT_EXISTS
\ No newline at end of file
+spring.cassandra.schema-action=CREATE_IF_NOT_EXISTS
diff --git a/services/payments/src/main/java/com/workup/payments/ControllerMQListener.java b/services/payments/src/main/java/com/workup/payments/ControllerMQListener.java
index 706bf74d..17526e5f 100644
--- a/services/payments/src/main/java/com/workup/payments/ControllerMQListener.java
+++ b/services/payments/src/main/java/com/workup/payments/ControllerMQListener.java
@@ -1,38 +1,125 @@
-package com.workup.payments;
-
-import com.workup.shared.commands.controller.SetMaxThreadsRequest;
-import java.lang.reflect.Field;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.springframework.amqp.rabbit.annotation.RabbitHandler;
-import org.springframework.amqp.rabbit.annotation.RabbitListener;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
-import org.springframework.stereotype.Service;
-
-@Service
-@RabbitListener(queues = "#{controllerQueue.name}")
-public class ControllerMQListener {
-
- private static final Logger logger = LogManager.getLogger(ControllerMQListener.class);
-
- @Autowired public ThreadPoolTaskExecutor taskExecutor;
-
- @Autowired private ApplicationContext context;
-
- @RabbitHandler
- public void receive(SetMaxThreadsRequest in) throws Exception {
- try {
- ThreadPoolTaskExecutor myBean = context.getBean(ThreadPoolTaskExecutor.class);
- Field maxPoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("maxPoolSize");
- maxPoolSize.setAccessible(true);
- maxPoolSize.set(myBean, in.getMaxThreads());
- Field corePoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("corePoolSize");
- corePoolSize.setAccessible(true);
- corePoolSize.set(myBean, in.getMaxThreads());
- } catch (Exception e) {
- logger.error("Error setting max threads", e.getMessage());
- }
- }
-}
+package com.workup.payments;
+
+import com.workup.payments.commands.PaymentCommandMap;
+import com.workup.shared.commands.controller.*;
+import com.workup.shared.enums.ServiceQueueNames;
+import com.workup.shared.enums.ThreadPoolSize;
+import com.zaxxer.hikari.HikariDataSource;
+import java.lang.reflect.Field;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+
+@Service
+@RabbitListener(queues = "#{controllerQueue.name}", id = "#{controllerQueue.name}")
+public class ControllerMQListener {
+ @Autowired public PaymentCommandMap commandMap;
+ @Autowired public ThreadPoolTaskExecutor taskExecutor;
+ @Autowired private ApplicationContext context;
+ @Autowired private RabbitListenerEndpointRegistry registry;
+ @Autowired private HikariDataSource hikariDataSource;
+
+ @RabbitHandler
+ public void receive(SetMaxThreadsRequest in) throws Exception {
+ try {
+ System.out.println("Max threads is: " + taskExecutor.getMaxPoolSize());
+ setThreads(in.getMaxThreads());
+ ThreadPoolSize.POOL_SIZE = taskExecutor.getMaxPoolSize();
+ System.out.println("Max threads set to: " + taskExecutor.getMaxPoolSize());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(SetLoggingLevelRequest in) throws Exception {
+ try {
+ Logger logger = LogManager.getRootLogger();
+ Configurator.setAllLevels(logger.getName(), Level.valueOf(in.getLevel()));
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(FreezeRequest in) throws Exception {
+ try {
+ registry.getListenerContainer(ServiceQueueNames.JOBS).stop();
+ taskExecutor.shutdown();
+ setThreads(0);
+ System.out.println("Stopped all threads.");
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(ContinueRequest in) throws Exception {
+ try {
+ taskExecutor.start();
+ setThreads(ThreadPoolSize.POOL_SIZE);
+ registry.getListenerContainer(ServiceQueueNames.JOBS).start();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ @RabbitHandler
+ public void receive(UpdateCommandRequest in) throws Exception {
+ // try {
+ // String className = commandMap.getCommand(in.getName()).getClass().getName();
+ // System.out.println("Updating command: " + in.getName());
+ // System.out.println("Class: " + className);
+ // Class newClass = new MyClassLoader().loadClass(in.getByteCode(), className);
+ // commandMap.replaceCommand(in.getName(), newClass);
+ // } catch (Exception e) {
+ // System.out.println(e.getMessage());
+ // e.printStackTrace();
+ // }
+ }
+
+ @RabbitHandler
+ private void SetMaxDBConnections(SetMaxDBConnectionsRequest in) {
+ try {
+ if (hikariDataSource == null) {
+ System.out.println("HikariDataSource is null");
+ return;
+ }
+ System.out.println("Max DB connections is: " + hikariDataSource.getMaximumPoolSize());
+ hikariDataSource.setMaximumPoolSize(in.getMaxDBConnections());
+ System.out.println("Max DB connections set to: " + hikariDataSource.getMaximumPoolSize());
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private void setThreads(int threads) throws NoSuchFieldException, IllegalAccessException {
+ ThreadPoolTaskExecutor myBean = context.getBean(ThreadPoolTaskExecutor.class);
+ Field maxPoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("maxPoolSize");
+ maxPoolSize.setAccessible(true);
+ maxPoolSize.set(myBean, threads);
+ Field corePoolSize = ThreadPoolTaskExecutor.class.getDeclaredField("corePoolSize");
+ corePoolSize.setAccessible(true);
+ corePoolSize.set(myBean, threads);
+ }
+}
+
+class MyClassLoader extends ClassLoader {
+ public Class> loadClass(byte[] byteCode, String className) {
+ System.out.println("Loading class: " + className);
+ return defineClass(className, byteCode, 0, byteCode.length);
+ }
+}
diff --git a/services/payments/src/main/java/com/workup/payments/PaymentsApplication.java b/services/payments/src/main/java/com/workup/payments/PaymentsApplication.java
index fee10a68..dfd16f9b 100644
--- a/services/payments/src/main/java/com/workup/payments/PaymentsApplication.java
+++ b/services/payments/src/main/java/com/workup/payments/PaymentsApplication.java
@@ -2,6 +2,7 @@
import com.workup.shared.enums.ControllerQueueNames;
import com.workup.shared.enums.ServiceQueueNames;
+import com.workup.shared.enums.ThreadPoolSize;
import org.springframework.amqp.core.AnonymousQueue;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
@@ -32,11 +33,6 @@ public Queue myQueue() {
return new Queue(ServiceQueueNames.PAYMENTS);
}
- @Bean
- public MessageConverter messageConverter() {
- return new Jackson2JsonMessageConverter();
- }
-
@Bean
public Queue controllerQueue() {
return new AnonymousQueue();
@@ -52,11 +48,17 @@ public Binding fanoutBinding(FanoutExchange fanout, Queue controllerQueue) {
return BindingBuilder.bind(controllerQueue).to(fanout);
}
+ @Bean
+ public MessageConverter messageConverter() {
+ return new Jackson2JsonMessageConverter();
+ }
+
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(50);
- executor.setMaxPoolSize(50);
+ executor.setCorePoolSize(ThreadPoolSize.POOL_SIZE);
+ executor.setMaxPoolSize(ThreadPoolSize.POOL_SIZE);
+ executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("payments-");
executor.initialize();
diff --git a/services/users/.env b/services/users/.env
index 64db5d17..9b4de138 100644
--- a/services/users/.env
+++ b/services/users/.env
@@ -5,4 +5,6 @@ USERS_SECRET_KEY=j4#BbFGfoc^2k*Bz
ADMIN_EMAIL=admin@workup.com
ADMIN_PASSWORD=admin
-ADMIN_ID=admin
\ No newline at end of file
+ADMIN_ID=admin
+
+REDIS_URL=service_redis
\ No newline at end of file
diff --git a/services/users/pom.xml b/services/users/pom.xml
index 281a218b..fade9f78 100644
--- a/services/users/pom.xml
+++ b/services/users/pom.xml
@@ -18,9 +18,21 @@
21
-
+ org.springframework.boot
- spring-boot-starter
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+
+ com.lmax
+ disruptor
+ 3.4.2
@@ -55,6 +67,17 @@
3.1.2
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
org.junit.jupiterjunit-jupiter-params
@@ -124,6 +147,11 @@
0.11.2runtime
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
diff --git a/services/users/src/main/java/com/workup/users/RedisConfig.java b/services/users/src/main/java/com/workup/users/RedisConfig.java
new file mode 100644
index 00000000..2d1939f7
--- /dev/null
+++ b/services/users/src/main/java/com/workup/users/RedisConfig.java
@@ -0,0 +1,8 @@
+package com.workup.users;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import(com.workup.shared.redis.RedisConfig.class)
+public class RedisConfig {}
diff --git a/services/users/src/main/java/com/workup/users/UsersApplication.java b/services/users/src/main/java/com/workup/users/UsersApplication.java
index c9cfee2e..b88fac3a 100644
--- a/services/users/src/main/java/com/workup/users/UsersApplication.java
+++ b/services/users/src/main/java/com/workup/users/UsersApplication.java
@@ -11,12 +11,14 @@
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@SpringBootApplication
@EnableAsync
+@EnableCaching
// @EnableMongoRepositories(basePackageClasses = ClientRepository.class)
public class UsersApplication {
diff --git a/services/users/src/main/java/com/workup/users/commands/AddFreelancerAchievementCommand.java b/services/users/src/main/java/com/workup/users/commands/AddFreelancerAchievementCommand.java
index 5f8bfd16..7166e427 100644
--- a/services/users/src/main/java/com/workup/users/commands/AddFreelancerAchievementCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/AddFreelancerAchievementCommand.java
@@ -6,17 +6,24 @@
import com.workup.users.db.Achievement;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class AddFreelancerAchievementCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(AddFreelancerAchievementCommand.class);
+
@Override
public AddFreelancerAchievementResponse Run(AddFreelancerAchievementRequest request) {
+ logger.info("Add Freelancer Achievement - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return AddFreelancerAchievementResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Achievement newAchievement =
Achievement.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/AddFreelancerEducationCommand.java b/services/users/src/main/java/com/workup/users/commands/AddFreelancerEducationCommand.java
index 93263bfa..b0f34aec 100644
--- a/services/users/src/main/java/com/workup/users/commands/AddFreelancerEducationCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/AddFreelancerEducationCommand.java
@@ -6,18 +6,24 @@
import com.workup.users.db.Education;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class AddFreelancerEducationCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(AddFreelancerEducationCommand.class);
@Override
public AddFreelancerEducationResponse Run(AddFreelancerEducationRequest request) {
+ logger.info("Add Freelancer Education - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return AddFreelancerEducationResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Education newEducation =
Education.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/AddFreelancerExperienceCommand.java b/services/users/src/main/java/com/workup/users/commands/AddFreelancerExperienceCommand.java
index 5e6a81ed..e1c09ecb 100644
--- a/services/users/src/main/java/com/workup/users/commands/AddFreelancerExperienceCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/AddFreelancerExperienceCommand.java
@@ -6,17 +6,24 @@
import com.workup.users.db.Experience;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class AddFreelancerExperienceCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(AddFreelancerExperienceCommand.class);
+
@Override
public AddFreelancerExperienceResponse Run(AddFreelancerExperienceRequest request) {
+ logger.info("Add Freelancer Experience - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return AddFreelancerExperienceResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Experience newExperience =
Experience.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/AddFreelancerLanguageCommand.java b/services/users/src/main/java/com/workup/users/commands/AddFreelancerLanguageCommand.java
index 09322f18..a847be78 100644
--- a/services/users/src/main/java/com/workup/users/commands/AddFreelancerLanguageCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/AddFreelancerLanguageCommand.java
@@ -6,18 +6,24 @@
import com.workup.users.db.Freelancer;
import java.util.ArrayList;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class AddFreelancerLanguageCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(AddFreelancerLanguageCommand.class);
@Override
public AddFreelancerLanguageResponse Run(AddFreelancerLanguageRequest request) {
+ logger.info("Add Freelancer Language - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return AddFreelancerLanguageResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
if (freelancer.getLanguages() == null) freelancer.setLanguages(new ArrayList<>());
freelancer.getLanguages().add(request.getNewLanguage());
diff --git a/services/users/src/main/java/com/workup/users/commands/AddFreelancerSkillCommand.java b/services/users/src/main/java/com/workup/users/commands/AddFreelancerSkillCommand.java
index d30ca841..896bc312 100644
--- a/services/users/src/main/java/com/workup/users/commands/AddFreelancerSkillCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/AddFreelancerSkillCommand.java
@@ -7,18 +7,24 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class AddFreelancerSkillCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(AddFreelancerSkillCommand.class);
@Override
public AddFreelancerSkillResponse Run(AddFreelancerSkillRequest request) {
+ logger.info("Add Freelancer Skill - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return AddFreelancerSkillResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
String newSkill = request.getNewSkill();
Freelancer freelancer = freelancerOptional.get();
if (freelancer.getSkills() == null) freelancer.setSkills(new ArrayList<>());
diff --git a/services/users/src/main/java/com/workup/users/commands/ClientGetPhotoCommand.java b/services/users/src/main/java/com/workup/users/commands/ClientGetPhotoCommand.java
index 43c1fd7b..eb95d91a 100644
--- a/services/users/src/main/java/com/workup/users/commands/ClientGetPhotoCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/ClientGetPhotoCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Client;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class ClientGetPhotoCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(ClientGetPhotoCommand.class);
@Override
public ClientGetPhotoResponse Run(ClientGetPhotoRequest request) {
+ logger.info("[i] Getting Photo for Client with id: " + request.getUserId());
Optional clientOptional = clientRepository.findById(request.getUserId());
if (!clientOptional.isPresent()) {
+ logger.error("[x] Client Doesn't Exist");
return ClientGetPhotoResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/ClientGetProfileCommand.java b/services/users/src/main/java/com/workup/users/commands/ClientGetProfileCommand.java
index 882e2017..c862e5d3 100644
--- a/services/users/src/main/java/com/workup/users/commands/ClientGetProfileCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/ClientGetProfileCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Client;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class ClientGetProfileCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(ClientGetProfileCommand.class);
@Override
public ClientGetProfileResponse Run(ClientGetProfileRequest request) {
+ logger.info("[i] Getting Profile for Client with id: " + request.getUserId());
Optional clientOptional = clientRepository.findById(request.getUserId());
if (!clientOptional.isPresent()) {
+ logger.error("[x] Client Doesn't Exist");
return ClientGetProfileResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/ClientRegisterCommand.java b/services/users/src/main/java/com/workup/users/commands/ClientRegisterCommand.java
index 016a2b1e..053941ad 100644
--- a/services/users/src/main/java/com/workup/users/commands/ClientRegisterCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/ClientRegisterCommand.java
@@ -8,20 +8,35 @@
import com.workup.shared.enums.users.UserType;
import com.workup.users.db.Client;
import java.util.Objects;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class ClientRegisterCommand extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(ClientRegisterCommand.class);
@Override
public SignUpAndInResponse Run(ClientRegisterRequest request) {
+ logger.info("[i] Registering Client with Email: " + request.getEmail());
if (Objects.isNull(request.getEmail())
|| Objects.isNull(request.getPassword())
|| Objects.isNull(request.getClientName())) {
+ logger.error("[x] Missing Required Fields");
return SignUpAndInResponse.builder()
.withStatusCode(HttpStatusCode.BAD_REQUEST)
.withSuccess(false)
.build();
}
try {
+ // check if registered already as freelancer
+ if (freelancerRepository.findByEmail(request.getEmail()).isPresent()) {
+ logger.error(
+ "[x] User with email" + request.getEmail() + " already registered as freelancer");
+ return SignUpAndInResponse.builder()
+ .withStatusCode(HttpStatusCode.BAD_REQUEST)
+ .withSuccess(false)
+ .withErrorMessage("User already registered as freelancer")
+ .build();
+ }
Client client =
Client.builder()
.withEmail(request.getEmail())
@@ -42,6 +57,7 @@ public SignUpAndInResponse Run(ClientRegisterRequest request) {
.withStatusCode(HttpStatusCode.OK)
.build();
} catch (Exception e) {
+ logger.error("[x] Error Registering Client: " + e.getMessage());
return SignUpAndInResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.withSuccess(false)
diff --git a/services/users/src/main/java/com/workup/users/commands/ClientSetPhotoCommand.java b/services/users/src/main/java/com/workup/users/commands/ClientSetPhotoCommand.java
index e1b71cd8..d9bd217b 100644
--- a/services/users/src/main/java/com/workup/users/commands/ClientSetPhotoCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/ClientSetPhotoCommand.java
@@ -5,16 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Client;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class ClientSetPhotoCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(ClientSetPhotoCommand.class);
@Override
public ClientSetPhotoResponse Run(ClientSetPhotoRequest request) {
-
+ logger.info("[i] Setting Photo for Client with id: " + request.getUserId());
Optional clientOption = clientRepository.findById(request.getUserId());
if (!clientOption.isPresent()) {
+ logger.error("[x] Client Doesn't Exist");
throw new RuntimeException("User not found");
}
diff --git a/services/users/src/main/java/com/workup/users/commands/ClientSetProfileCommand.java b/services/users/src/main/java/com/workup/users/commands/ClientSetProfileCommand.java
index 3341bcc1..64b2f5a7 100644
--- a/services/users/src/main/java/com/workup/users/commands/ClientSetProfileCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/ClientSetProfileCommand.java
@@ -5,13 +5,16 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Client;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class ClientSetProfileCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(ClientSetProfileCommand.class);
@Override
public ClientSetProfileResponse Run(ClientSetProfileRequest request) {
-
+ logger.info("[i] Setting Profile for Client with id: " + request.getUserId());
Client client;
if (request.getUserId() == null) {
@@ -19,6 +22,7 @@ public ClientSetProfileResponse Run(ClientSetProfileRequest request) {
} else {
Optional clientOption = clientRepository.findById(request.getUserId());
if (!clientOption.isPresent()) {
+ logger.error("[x] Client Doesn't Exist");
throw new RuntimeException("User not found");
}
client = clientOption.get();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerGetPhotoCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerGetPhotoCommand.java
index 99867f8a..8a6f4dd8 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerGetPhotoCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerGetPhotoCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerGetPhotoCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerGetPhotoCommand.class);
@Override
public FreelancerGetPhotoResponse Run(FreelancerGetPhotoRequest request) {
+ logger.info("[i] Getting Photo for Freelancer with id: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
if (!freelancerOptional.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
return FreelancerGetPhotoResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileBriefCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileBriefCommand.java
index fddec877..75837df7 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileBriefCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileBriefCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerGetProfileBriefCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerGetProfileBriefCommand.class);
@Override
public FreelancerGetProfileBriefResponse Run(FreelancerGetProfileBriefRequest request) {
+ logger.info("[i] Getting Profile Brief for Freelancer with id: " + request.getUserId());
Optional freelancer = freelancerRepository.findById(request.getUserId());
if (!freelancer.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
return FreelancerGetProfileBriefResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileCommand.java
index 384fca09..1391956d 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerGetProfileCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerGetProfileCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerGetProfileCommand.class);
@Override
public FreelancerGetProfileResponse Run(FreelancerGetProfileRequest request) {
+ logger.info("[i] Getting Profile for Freelancer with id: " + request.getUserId());
Optional freelancer = freelancerRepository.findById(request.getUserId());
if (!freelancer.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
return FreelancerGetProfileResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerGetResumeCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerGetResumeCommand.java
index ef11b3ea..f2b286b8 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerGetResumeCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerGetResumeCommand.java
@@ -5,15 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerGetResumeCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerGetResumeCommand.class);
@Override
public FreelancerGetResumeResponse Run(FreelancerGetResumeRequest request) {
+ logger.info("[i] Getting Resume for Freelancer with id: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
if (!freelancerOptional.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
return FreelancerGetResumeResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.build();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerRegisterCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerRegisterCommand.java
index ff0c0a39..2dee423c 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerRegisterCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerRegisterCommand.java
@@ -10,21 +10,35 @@
import com.workup.shared.enums.users.UserType;
import com.workup.users.db.Freelancer;
import java.util.Objects;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerRegisterCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerRegisterCommand.class);
@Override
public SignUpAndInResponse Run(FreelancerRegisterRequest request) {
+ logger.info("[i] Registering Freelancer with Email: " + request.getEmail());
if (Objects.isNull(request.getEmail())
|| Objects.isNull(request.getPassword())
|| Objects.isNull(request.getFullName())) {
+ logger.error("[x] Missing Required Fields");
return SignUpAndInResponse.builder()
.withStatusCode(HttpStatusCode.BAD_REQUEST)
.withSuccess(false)
.build();
}
try {
+ // check if registered already as client
+ if (clientRepository.findByEmail(request.getEmail()).isPresent()) {
+ logger.error("[x] User with email" + request.getEmail() + " already registered as client");
+ return SignUpAndInResponse.builder()
+ .withStatusCode(HttpStatusCode.BAD_REQUEST)
+ .withSuccess(false)
+ .withErrorMessage("User already registered as client")
+ .build();
+ }
Freelancer freelancer =
Freelancer.builder()
.withEmail(request.getEmail())
@@ -50,8 +64,10 @@ public SignUpAndInResponse Run(FreelancerRegisterRequest request) {
.withStatusCode(HttpStatusCode.OK)
.build();
} catch (Exception e) {
+ logger.error("[x] Error Registering Freelancer: " + e.getMessage());
return SignUpAndInResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
+ .withErrorMessage(e.getMessage())
.withSuccess(false)
.build();
}
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerSetPhotoCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerSetPhotoCommand.java
index 13aa1acb..cf7a00cf 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerSetPhotoCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerSetPhotoCommand.java
@@ -5,16 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerSetPhotoCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerSetPhotoCommand.class);
@Override
public FreelancerSetPhotoResponse Run(FreelancerSetPhotoRequest request) {
-
+ logger.info("[i] Setting Photo for Freelancer with id: " + request.getUserId());
Optional freelancerOption = freelancerRepository.findById(request.getUserId());
if (!freelancerOption.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
throw new RuntimeException("User not found");
}
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerSetProfileCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerSetProfileCommand.java
index 6e2622a6..81f1fb91 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerSetProfileCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerSetProfileCommand.java
@@ -5,13 +5,17 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerSetProfileCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerSetProfileCommand.class);
@Override
public FreelancerSetProfileResponse Run(FreelancerSetProfileRequest request) {
+ logger.info("[i] Setting Profile for Freelancer with id: " + request.getUserId());
Freelancer freelancer;
if (request.getUserId() == null) {
freelancer = Freelancer.builder().withId(null).build();
@@ -19,6 +23,7 @@ public FreelancerSetProfileResponse Run(FreelancerSetProfileRequest request) {
Optional freelancerOption = freelancerRepository.findById(request.getUserId());
if (!freelancerOption.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
throw new RuntimeException("User not found");
}
freelancer = freelancerOption.get();
diff --git a/services/users/src/main/java/com/workup/users/commands/FreelancerSetResumeCommand.java b/services/users/src/main/java/com/workup/users/commands/FreelancerSetResumeCommand.java
index d1fc0a9a..c6c51e7e 100644
--- a/services/users/src/main/java/com/workup/users/commands/FreelancerSetResumeCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/FreelancerSetResumeCommand.java
@@ -5,16 +5,20 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class FreelancerSetResumeCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(FreelancerSetResumeCommand.class);
@Override
public FreelancerSetResumeResponse Run(FreelancerSetResumeRequest request) {
-
+ logger.info("[i] Setting Resume for Freelancer with id: " + request.getUserId());
Optional freelancerOption = freelancerRepository.findById(request.getUserId());
if (!freelancerOption.isPresent()) {
+ logger.error("[x] Freelancer Doesn't Exist");
throw new RuntimeException("User not found");
}
diff --git a/services/users/src/main/java/com/workup/users/commands/GetFreelancerAchievementsCommand.java b/services/users/src/main/java/com/workup/users/commands/GetFreelancerAchievementsCommand.java
index 41cb08f8..d1541dff 100644
--- a/services/users/src/main/java/com/workup/users/commands/GetFreelancerAchievementsCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/GetFreelancerAchievementsCommand.java
@@ -9,17 +9,24 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class GetFreelancerAchievementsCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(GetFreelancerAchievementsCommand.class);
+
@Override
public GetFreelancerAchievementsResponse Run(GetFreelancerAchievementsRequest request) {
+ logger.info("Get Freelancer Achievements - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return GetFreelancerAchievementsResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
List achievements = convertToAchievementViewList(freelancer.getAchievements());
return GetFreelancerAchievementsResponse.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/GetFreelancerEducationsCommand.java b/services/users/src/main/java/com/workup/users/commands/GetFreelancerEducationsCommand.java
index 6e27ee60..2be131f9 100644
--- a/services/users/src/main/java/com/workup/users/commands/GetFreelancerEducationsCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/GetFreelancerEducationsCommand.java
@@ -9,17 +9,24 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class GetFreelancerEducationsCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(GetFreelancerEducationsCommand.class);
+
@Override
public GetFreelancerEducationsResponse Run(GetFreelancerEducationsRequest request) {
+ logger.info("Get Freelancer Educations - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return GetFreelancerEducationsResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
List educations = convertToEducationViewList(freelancer.getEducations());
return GetFreelancerEducationsResponse.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/GetFreelancerExperiencesCommand.java b/services/users/src/main/java/com/workup/users/commands/GetFreelancerExperiencesCommand.java
index bccce631..ead3a9b3 100644
--- a/services/users/src/main/java/com/workup/users/commands/GetFreelancerExperiencesCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/GetFreelancerExperiencesCommand.java
@@ -9,18 +9,24 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class GetFreelancerExperiencesCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(GetFreelancerExperiencesCommand.class);
@Override
public GetFreelancerExperiencesResponse Run(GetFreelancerExperiencesRequest request) {
+ logger.info("Get Freelancer Experiences - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return GetFreelancerExperiencesResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
List experiences = convertToExperienceViewList(freelancer.getExperiences());
return GetFreelancerExperiencesResponse.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/GetFreelancerLanguagesCommand.java b/services/users/src/main/java/com/workup/users/commands/GetFreelancerLanguagesCommand.java
index f49a80c6..f6426927 100644
--- a/services/users/src/main/java/com/workup/users/commands/GetFreelancerLanguagesCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/GetFreelancerLanguagesCommand.java
@@ -6,17 +6,24 @@
import com.workup.users.db.Freelancer;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class GetFreelancerLanguagesCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(GetFreelancerLanguagesCommand.class);
+
@Override
public GetFreelancerLanguagesResponse Run(GetFreelancerLanguagesRequest request) {
+ logger.info("Get Freelancer Languages - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return GetFreelancerLanguagesResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
List languages = freelancer.getLanguages();
return GetFreelancerLanguagesResponse.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/GetFreelancerSkillsCommand.java b/services/users/src/main/java/com/workup/users/commands/GetFreelancerSkillsCommand.java
index 79c4c009..338f513d 100644
--- a/services/users/src/main/java/com/workup/users/commands/GetFreelancerSkillsCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/GetFreelancerSkillsCommand.java
@@ -6,18 +6,24 @@
import com.workup.users.db.Freelancer;
import java.util.List;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class GetFreelancerSkillsCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(GetFreelancerSkillsCommand.class);
@Override
public GetFreelancerSkillsResponse Run(GetFreelancerSkillsRequest request) {
+ logger.info("Get Freelancer Skills - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return GetFreelancerSkillsResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
List skills = freelancer.getSkills();
return GetFreelancerSkillsResponse.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/LoginCommand.java b/services/users/src/main/java/com/workup/users/commands/LoginCommand.java
index c56b5cf7..4dbb39e4 100644
--- a/services/users/src/main/java/com/workup/users/commands/LoginCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/LoginCommand.java
@@ -9,10 +9,15 @@
import com.workup.users.db.Client;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class LoginCommand extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(LoginCommand.class);
+
@Override
public SignUpAndInResponse Run(LoginRequest request) {
+ logger.info("[i] Logging in user with email: " + request.getEmail());
String email = request.getEmail();
String password = request.getPassword();
try {
@@ -52,6 +57,7 @@ public SignUpAndInResponse Run(LoginRequest request) {
}
}
+ logger.error("[x] User not found or password incorrect");
// return unauthorized
return SignUpAndInResponse.builder()
.withSuccess(false)
@@ -59,8 +65,7 @@ public SignUpAndInResponse Run(LoginRequest request) {
.withErrorMessage("Invalid email or password")
.build();
} catch (Exception e) {
- System.out.println(e.getMessage());
- e.printStackTrace();
+ logger.error("[x] Error Logging in user: " + e.getMessage());
return SignUpAndInResponse.builder()
.withStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR)
.withSuccess(false)
diff --git a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerAchievementCommand.java b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerAchievementCommand.java
index 1500f06f..475bf8bf 100644
--- a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerAchievementCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerAchievementCommand.java
@@ -6,17 +6,25 @@
import com.workup.users.db.Achievement;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class RemoveFreelancerAchievementCommand
extends UserCommand {
+ private static final Logger logger =
+ LogManager.getLogger(RemoveFreelancerAchievementCommand.class);
+
@Override
public RemoveFreelancerAchievementResponse Run(RemoveFreelancerAchievementRequest request) {
+ logger.info("Remove Freelancer Achievement - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return RemoveFreelancerAchievementResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
freelancer
.getAchievements()
diff --git a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerEducationCommand.java b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerEducationCommand.java
index b7740bfc..9d0ec663 100644
--- a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerEducationCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerEducationCommand.java
@@ -6,18 +6,24 @@
import com.workup.users.db.Education;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class RemoveFreelancerEducationCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(RemoveFreelancerEducationCommand.class);
@Override
public RemoveFreelancerEducationResponse Run(RemoveFreelancerEducationRequest request) {
+ logger.info("Remove Freelancer Education - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return RemoveFreelancerEducationResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
freelancer
.getEducations()
diff --git a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerExperienceCommand.java b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerExperienceCommand.java
index c34467b0..4b06d935 100644
--- a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerExperienceCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerExperienceCommand.java
@@ -6,17 +6,25 @@
import com.workup.users.db.Experience;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class RemoveFreelancerExperienceCommand
extends UserCommand {
+ private static final Logger logger =
+ LogManager.getLogger(RemoveFreelancerExperienceCommand.class);
+
@Override
public RemoveFreelancerExperienceResponse Run(RemoveFreelancerExperienceRequest request) {
+ logger.info("Remove Freelancer Experience - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return RemoveFreelancerExperienceResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
freelancer
.getExperiences()
diff --git a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerLanguageCommand.java b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerLanguageCommand.java
index 35d7b5ef..a9786037 100644
--- a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerLanguageCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerLanguageCommand.java
@@ -5,17 +5,24 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class RemoveFreelancerLanguageCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(RemoveFreelancerLanguageCommand.class);
+
@Override
public RemoveFreelancerLanguageResponse Run(RemoveFreelancerLanguageRequest request) {
+ logger.info("Remove Freelancer Language - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return RemoveFreelancerLanguageResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
freelancer.getLanguages().remove(request.getLanguageToRemove());
freelancerRepository.save(freelancer);
diff --git a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerSkillCommand.java b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerSkillCommand.java
index 4fee235d..1656945e 100644
--- a/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerSkillCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/RemoveFreelancerSkillCommand.java
@@ -5,17 +5,24 @@
import com.workup.shared.enums.HttpStatusCode;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class RemoveFreelancerSkillCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(RemoveFreelancerSkillCommand.class);
+
@Override
public RemoveFreelancerSkillResponse Run(RemoveFreelancerSkillRequest request) {
+ logger.info("Remove Freelancer Skill - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return RemoveFreelancerSkillResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
freelancer.getSkills().remove(request.getSkillToRemove());
freelancerRepository.save(freelancer);
diff --git a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerAchievementCommand.java b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerAchievementCommand.java
index f94005e0..7644da23 100644
--- a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerAchievementCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerAchievementCommand.java
@@ -6,17 +6,25 @@
import com.workup.users.db.Achievement;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class UpdateFreelancerAchievementCommand
extends UserCommand {
+ private static final Logger logger =
+ LogManager.getLogger(UpdateFreelancerAchievementCommand.class);
+
@Override
public UpdateFreelancerAchievementResponse Run(UpdateFreelancerAchievementRequest request) {
+ logger.info("Update Freelancer Achievement - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return UpdateFreelancerAchievementResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Achievement updatedAchievement =
Achievement.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerEducationCommand.java b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerEducationCommand.java
index 97aaaae1..37c79d4e 100644
--- a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerEducationCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerEducationCommand.java
@@ -6,18 +6,24 @@
import com.workup.users.db.Education;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class UpdateFreelancerEducationCommand
extends UserCommand {
+ private static final Logger logger = LogManager.getLogger(UpdateFreelancerEducationCommand.class);
@Override
public UpdateFreelancerEducationResponse Run(UpdateFreelancerEducationRequest request) {
+ logger.info("Update Freelancer Education - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return UpdateFreelancerEducationResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Education updatedEducation =
Education.builder()
diff --git a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerExperienceCommand.java b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerExperienceCommand.java
index 95a6ac8a..e54c2029 100644
--- a/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerExperienceCommand.java
+++ b/services/users/src/main/java/com/workup/users/commands/UpdateFreelancerExperienceCommand.java
@@ -6,17 +6,25 @@
import com.workup.users.db.Experience;
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
public class UpdateFreelancerExperienceCommand
extends UserCommand {
+ private static final Logger logger =
+ LogManager.getLogger(UpdateFreelancerExperienceCommand.class);
+
@Override
public UpdateFreelancerExperienceResponse Run(UpdateFreelancerExperienceRequest request) {
+ logger.info("Update Freelancer Experience - Freelancer ID: " + request.getUserId());
Optional freelancerOptional = freelancerRepository.findById(request.getUserId());
- if (freelancerOptional.isEmpty())
+ if (freelancerOptional.isEmpty()) {
+ logger.error("Freelancer Not Found - Freelancer ID: " + request.getUserId());
return UpdateFreelancerExperienceResponse.builder()
.withStatusCode(HttpStatusCode.NOT_FOUND)
.withErrorMessage("Freelancer Doesn't Exist")
.build();
+ }
Freelancer freelancer = freelancerOptional.get();
Experience updatedExperience =
Experience.builder()
diff --git a/services/users/src/main/java/com/workup/users/db/Client.java b/services/users/src/main/java/com/workup/users/db/Client.java
index acff0d3c..de3fe980 100644
--- a/services/users/src/main/java/com/workup/users/db/Client.java
+++ b/services/users/src/main/java/com/workup/users/db/Client.java
@@ -1,5 +1,6 @@
package com.workup.users.db;
+import java.io.Serializable;
import java.util.Date;
import lombok.Builder;
import lombok.Getter;
@@ -13,7 +14,7 @@
@Getter
@Setter
@Document
-public class Client {
+public class Client implements Serializable {
@Id private ObjectId id;
@Indexed(unique = true)
diff --git a/services/users/src/main/java/com/workup/users/db/Freelancer.java b/services/users/src/main/java/com/workup/users/db/Freelancer.java
index a51f4a7e..80d4d2c2 100644
--- a/services/users/src/main/java/com/workup/users/db/Freelancer.java
+++ b/services/users/src/main/java/com/workup/users/db/Freelancer.java
@@ -1,5 +1,6 @@
package com.workup.users.db;
+import java.io.Serializable;
import java.util.Date;
import java.util.List;
import lombok.Builder;
@@ -15,7 +16,7 @@
@Getter
@Setter
@Document(collection = "Freelancer")
-public class Freelancer {
+public class Freelancer implements Serializable {
@Id private ObjectId id;
@Indexed(unique = true)
diff --git a/services/users/src/main/java/com/workup/users/repositories/ClientRepository.java b/services/users/src/main/java/com/workup/users/repositories/ClientRepository.java
index b3af5feb..520b73b4 100644
--- a/services/users/src/main/java/com/workup/users/repositories/ClientRepository.java
+++ b/services/users/src/main/java/com/workup/users/repositories/ClientRepository.java
@@ -2,9 +2,15 @@
import com.workup.users.db.Client;
import java.util.Optional;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface ClientRepository extends MongoRepository {
// find by clientEmail
+ @Cacheable(value = "clients", key = "#email")
Optional findByEmail(String email);
+
+ @CacheEvict(value = "clients", key = "#entity.email")
+ S save(S entity);
}
diff --git a/services/users/src/main/java/com/workup/users/repositories/FreelancerRepository.java b/services/users/src/main/java/com/workup/users/repositories/FreelancerRepository.java
index 74b38922..bdce7c86 100644
--- a/services/users/src/main/java/com/workup/users/repositories/FreelancerRepository.java
+++ b/services/users/src/main/java/com/workup/users/repositories/FreelancerRepository.java
@@ -2,9 +2,15 @@
import com.workup.users.db.Freelancer;
import java.util.Optional;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface FreelancerRepository extends MongoRepository {
// find by freelancerEmail
+ @Cacheable(value = "freelancers", key = "#email")
Optional findByEmail(String email);
+
+ @CacheEvict(value = "freelancers", key = "#entity.email")
+ S save(S entity);
}
diff --git a/services/users/src/main/resources/application.properties b/services/users/src/main/resources/application.properties
index 29c286b8..472d896d 100644
--- a/services/users/src/main/resources/application.properties
+++ b/services/users/src/main/resources/application.properties
@@ -1,5 +1,6 @@
spring.data.mongodb.uri=${USERS_DB_URI}
spring.data.mongodb.database=mydatabase
+spring.data.mongodb.auto-index-creation=true
spring.rabbitmq.host=${USERS_MQ_HOST}
spring.rabbitmq.port=5672
@@ -7,3 +8,7 @@ spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
SECRET_KEY=${USERS_SECRET_KEY}
+spring.cache.type=redis
+spring.cache.host=${REDIS_URL}
+spring.cache.port=6379
+spring.cache.redis.time-to-live=60000
diff --git a/services/users/src/main/resources/log4j2.xml b/services/users/src/main/resources/log4j2.xml
new file mode 100644
index 00000000..43243f82
--- /dev/null
+++ b/services/users/src/main/resources/log4j2.xml
@@ -0,0 +1,52 @@
+
+
+ logs/users
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/services/users/src/test/java/com/workup/users/UsersApplicationTests.java b/services/users/src/test/java/com/workup/users/UsersApplicationTests.java
index 7b160a70..a03853d7 100644
--- a/services/users/src/test/java/com/workup/users/UsersApplicationTests.java
+++ b/services/users/src/test/java/com/workup/users/UsersApplicationTests.java
@@ -27,6 +27,7 @@
import org.springframework.context.annotation.Import;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
+import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.containers.RabbitMQContainer;
import org.testcontainers.junit.jupiter.Container;
@@ -45,6 +46,11 @@ class UsersApplicationTests {
static final MongoDBContainer mongoDBContainer =
new MongoDBContainer("mongo:7.0").withExposedPorts(27017);
+ // redis container
+ @Container
+ static final GenericContainer redisContainer =
+ new GenericContainer("redis:7.2.4").withExposedPorts(6379);
+
@Autowired private AmqpTemplate template;
@Autowired private ClientRepository paymentRequestRepository;
@Autowired private ExperienceRepository experienceRepository;
@@ -78,6 +84,9 @@ static void setDatasourceProperties(DynamicPropertyRegistry registry) {
registry.add("spring.rabbitmq.port", rabbitMQContainer::getFirstMappedPort);
registry.add("spring.rabbitmq.username", rabbitMQContainer::getAdminUsername);
registry.add("spring.rabbitmq.password", rabbitMQContainer::getAdminPassword);
+ // add redis properties
+ registry.add("spring.cache.host", redisContainer::getHost);
+ registry.add("spring.cache.port", redisContainer::getFirstMappedPort);
}
@Test
diff --git a/shared/pom.xml b/shared/pom.xml
index 17c02a8c..cec90cd1 100644
--- a/shared/pom.xml
+++ b/shared/pom.xml
@@ -1,42 +1,53 @@
- 4.0.0
- com.workup
- shared
- 0.0.1-SNAPSHOT
- shared
-
- 21
- 21
-
-
- com.workup
- main
- 1.0-SNAPSHOT
-
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-xml
- 2.16.0
-
-
- org.springframework.boot
- spring-boot-starter-data-redis
- 3.1.2
-
-
- redis.clients
- jedis
- 3.7.0
-
-
- org.projectlombok
- lombok
- 1.18.30
- provided
-
-
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ com.workup
+ shared
+ 0.0.1-SNAPSHOT
+ shared
+
+ 21
+ 21
+
+
+ com.workup
+ main
+ 1.0-SNAPSHOT
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ 2.16.0
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+ 3.1.2
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ redis.clients
+ jedis
+ 3.7.0
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+ 3.2.5
+
+
\ No newline at end of file
diff --git a/shared/src/main/java/com/workup/shared/commands/CommandMap.java b/shared/src/main/java/com/workup/shared/commands/CommandMap.java
index 8d34facd..ece18e29 100644
--- a/shared/src/main/java/com/workup/shared/commands/CommandMap.java
+++ b/shared/src/main/java/com/workup/shared/commands/CommandMap.java
@@ -24,4 +24,8 @@ public R getCommand(String command) throws Exception {
setupCommand(commandInstance);
return commandInstance;
}
+
+ public void replaceCommand(String command, Class extends R> newCommand) {
+ commands.put(command, newCommand);
+ }
}
diff --git a/shared/src/main/java/com/workup/shared/commands/controller/ContinueRequest.java b/shared/src/main/java/com/workup/shared/commands/controller/ContinueRequest.java
index 947802a6..99f68a77 100644
--- a/shared/src/main/java/com/workup/shared/commands/controller/ContinueRequest.java
+++ b/shared/src/main/java/com/workup/shared/commands/controller/ContinueRequest.java
@@ -1,6 +1,11 @@
package com.workup.shared.commands.controller;
+import lombok.Builder;
+import lombok.extern.jackson.Jacksonized;
+
/** Makes a service start accepting requests and acquire resources again. */
+@Builder
+@Jacksonized
public class ContinueRequest {
// No fields are required?
}
diff --git a/shared/src/main/java/com/workup/shared/commands/controller/FreezeRequest.java b/shared/src/main/java/com/workup/shared/commands/controller/FreezeRequest.java
index 563c88c2..3f841e89 100644
--- a/shared/src/main/java/com/workup/shared/commands/controller/FreezeRequest.java
+++ b/shared/src/main/java/com/workup/shared/commands/controller/FreezeRequest.java
@@ -1,6 +1,11 @@
package com.workup.shared.commands.controller;
+import lombok.Builder;
+import lombok.extern.jackson.Jacksonized;
+
/** Makes a service stop accepting requests and release resources. */
+@Builder
+@Jacksonized
public class FreezeRequest {
// No fields are required?
}
diff --git a/shared/src/main/java/com/workup/shared/commands/controller/SetErrorReportingLevelRequest.java b/shared/src/main/java/com/workup/shared/commands/controller/SetLoggingLevelRequest.java
similarity index 68%
rename from shared/src/main/java/com/workup/shared/commands/controller/SetErrorReportingLevelRequest.java
rename to shared/src/main/java/com/workup/shared/commands/controller/SetLoggingLevelRequest.java
index f7b15132..e035135a 100644
--- a/shared/src/main/java/com/workup/shared/commands/controller/SetErrorReportingLevelRequest.java
+++ b/shared/src/main/java/com/workup/shared/commands/controller/SetLoggingLevelRequest.java
@@ -1,6 +1,5 @@
package com.workup.shared.commands.controller;
-import com.workup.shared.enums.ErrorLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.jackson.Jacksonized;
@@ -9,6 +8,6 @@
@Getter
@Builder(setterPrefix = "with")
@Jacksonized
-public class SetErrorReportingLevelRequest {
- ErrorLevel errorLevel;
+public class SetLoggingLevelRequest {
+ String level;
}
diff --git a/shared/src/main/java/com/workup/shared/commands/controller/UpdateCommandRequest.java b/shared/src/main/java/com/workup/shared/commands/controller/UpdateCommandRequest.java
index 1d2fbafa..b8c01ed0 100644
--- a/shared/src/main/java/com/workup/shared/commands/controller/UpdateCommandRequest.java
+++ b/shared/src/main/java/com/workup/shared/commands/controller/UpdateCommandRequest.java
@@ -9,5 +9,6 @@
@Builder(setterPrefix = "with")
@Jacksonized
public class UpdateCommandRequest {
- String name;
+ String commandName;
+ byte[] byteCode;
}
diff --git a/shared/src/main/java/com/workup/shared/enums/ErrorLevel.java b/shared/src/main/java/com/workup/shared/enums/ErrorLevel.java
deleted file mode 100644
index 444baebe..00000000
--- a/shared/src/main/java/com/workup/shared/enums/ErrorLevel.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.workup.shared.enums;
-
-public enum ErrorLevel {
- INFO,
- WARNING,
- ERROR,
- FATAL
-}
diff --git a/shared/src/main/java/com/workup/shared/enums/ThreadPoolSize.java b/shared/src/main/java/com/workup/shared/enums/ThreadPoolSize.java
new file mode 100644
index 00000000..4a6df8bc
--- /dev/null
+++ b/shared/src/main/java/com/workup/shared/enums/ThreadPoolSize.java
@@ -0,0 +1,5 @@
+package com.workup.shared.enums;
+
+public class ThreadPoolSize {
+ public static int POOL_SIZE = 50;
+}
diff --git a/webserver/src/main/resources/application.properties b/webserver/src/main/resources/application.properties
index ddd689b1..42437f86 100644
--- a/webserver/src/main/resources/application.properties
+++ b/webserver/src/main/resources/application.properties
@@ -4,5 +4,6 @@ spring.rabbitmq.host=${WEBSERVER_MQ_HOST}
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
+spring.rabbitmq.template.reply-timeout=60000
auth.secret = ${WEBSERVER_SECRET_KEY}