init backend, added admin page in front
Signed-off-by: Nicolas Froger <nicolas@kektus.xyz>
This commit is contained in:
parent
5c6e641fbd
commit
ddc6c64f0f
89 changed files with 5083 additions and 9 deletions
97
summer2024-backend/src/main/docker/Dockerfile.jvm
Normal file
97
summer2024-backend/src/main/docker/Dockerfile.jvm
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./mvnw package
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/summer2024-backend-jvm .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend-jvm
|
||||
#
|
||||
# If you want to include the debug port into your docker image
|
||||
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||
# when running the container
|
||||
#
|
||||
# Then run the container using :
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend-jvm
|
||||
#
|
||||
# This image uses the `run-java.sh` script to run the application.
|
||||
# This scripts computes the command line to execute your Java application, and
|
||||
# includes memory/GC tuning.
|
||||
# You can configure the behavior using the following environment properties:
|
||||
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||
# of the container available memory as set here. The default is `50` which means 50%
|
||||
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||
# a container without any memory constraints for the container then this option has
|
||||
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||
# when things are happening. This option, if set to true, will set
|
||||
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||
# true").
|
||||
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||
# (example: "20")
|
||||
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||
# (example: "40")
|
||||
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||
# (example: "4")
|
||||
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||
# previous GC times. (example: "90")
|
||||
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||
# contain the necessary JRE command-line options to specify the required GC, which
|
||||
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||
COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
|
||||
COPY --chown=185 target/quarkus-app/*.jar /deployments/
|
||||
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
|
||||
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
|
||||
|
||||
EXPOSE 8080
|
||||
USER 185
|
||||
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||
|
||||
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||
|
||||
93
summer2024-backend/src/main/docker/Dockerfile.legacy-jar
Normal file
93
summer2024-backend/src/main/docker/Dockerfile.legacy-jar
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./mvnw package -Dquarkus.package.jar.type=legacy-jar
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/summer2024-backend-legacy-jar .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend-legacy-jar
|
||||
#
|
||||
# If you want to include the debug port into your docker image
|
||||
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||
# when running the container
|
||||
#
|
||||
# Then run the container using :
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend-legacy-jar
|
||||
#
|
||||
# This image uses the `run-java.sh` script to run the application.
|
||||
# This scripts computes the command line to execute your Java application, and
|
||||
# includes memory/GC tuning.
|
||||
# You can configure the behavior using the following environment properties:
|
||||
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
|
||||
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||
# of the container available memory as set here. The default is `50` which means 50%
|
||||
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||
# If used in a container without any memory constraints for the container then this
|
||||
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||
# a container without any memory constraints for the container then this option has
|
||||
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||
# when things are happening. This option, if set to true, will set
|
||||
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||
# true").
|
||||
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||
# (example: "20")
|
||||
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||
# (example: "40")
|
||||
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||
# (example: "4")
|
||||
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||
# previous GC times. (example: "90")
|
||||
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||
# contain the necessary JRE command-line options to specify the required GC, which
|
||||
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
|
||||
COPY target/lib/* /deployments/lib/
|
||||
COPY target/*-runner.jar /deployments/quarkus-run.jar
|
||||
|
||||
EXPOSE 8080
|
||||
USER 185
|
||||
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||
|
||||
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||
27
summer2024-backend/src/main/docker/Dockerfile.native
Normal file
27
summer2024-backend/src/main/docker/Dockerfile.native
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./mvnw package -Dnative
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.native -t quarkus/summer2024-backend .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend
|
||||
#
|
||||
###
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root target/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
30
summer2024-backend/src/main/docker/Dockerfile.native-micro
Normal file
30
summer2024-backend/src/main/docker/Dockerfile.native-micro
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
####
|
||||
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||
# It uses a micro base image, tuned for Quarkus native executables.
|
||||
# It reduces the size of the resulting container image.
|
||||
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
|
||||
#
|
||||
# Before building the container image run:
|
||||
#
|
||||
# ./mvnw package -Dnative
|
||||
#
|
||||
# Then, build the image with:
|
||||
#
|
||||
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/summer2024-backend .
|
||||
#
|
||||
# Then run the container using:
|
||||
#
|
||||
# docker run -i --rm -p 8080:8080 quarkus/summer2024-backend
|
||||
#
|
||||
###
|
||||
FROM quay.io/quarkus/quarkus-micro-image:2.0
|
||||
WORKDIR /work/
|
||||
RUN chown 1001 /work \
|
||||
&& chmod "g+rwX" /work \
|
||||
&& chown 1001:root /work
|
||||
COPY --chown=1001:root target/*-runner /work/application
|
||||
|
||||
EXPOSE 8080
|
||||
USER 1001
|
||||
|
||||
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package fr.kektus.summer2024;
|
||||
|
||||
import io.smallrye.common.constraint.NotNull;
|
||||
import jakarta.ws.rs.InternalServerErrorException;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
|
||||
public enum Validators {
|
||||
;
|
||||
|
||||
public static void assertNotNull(final Exception exception) {
|
||||
if (exception == null) {
|
||||
throw new InternalServerErrorException("Null exception was passed");
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotNull(final Object object, final @NotNull WebApplicationException exception) {
|
||||
assertNotNull(exception);
|
||||
|
||||
if (object == null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.kektus.summer2024.converters;
|
||||
|
||||
public interface Converter<TYPE_LEFT, TYPE_RIGHT> {
|
||||
public TYPE_LEFT toLeft(TYPE_RIGHT right);
|
||||
|
||||
public TYPE_RIGHT toRight(TYPE_LEFT left);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package fr.kektus.summer2024.converters;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Post;
|
||||
import fr.kektus.summer2024.domain.service.AssetService;
|
||||
import fr.kektus.summer2024.presentation.rest.PostApi;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PublicPostConverter {
|
||||
@Inject AssetService assetService;
|
||||
|
||||
public PostApi.PostDto toPostDto(Post post) {
|
||||
return new PostApi.PostDto().withId(post.id)
|
||||
.withDate(post.date)
|
||||
.withDescription(post.description)
|
||||
.withAssets(post.assets.stream()
|
||||
.map(assetService::getPresignedUrlForAsset)
|
||||
.toList())
|
||||
.withLocation(new PostApi.PostDto.Location().withCity(post.city)
|
||||
.withCountry(post.country)
|
||||
.withLat(post.latitude)
|
||||
.withLon(post.longitude));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package fr.kektus.summer2024.data.model;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.With;
|
||||
|
||||
@Entity
|
||||
@AllArgsConstructor @NoArgsConstructor @With
|
||||
@Getter @Setter
|
||||
public class Asset {
|
||||
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long id;
|
||||
public String filename;
|
||||
@ManyToOne @JoinColumn(name = "post_id") public Post post;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package fr.kektus.summer2024.data.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.With;
|
||||
import org.hibernate.Length;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor @AllArgsConstructor
|
||||
@With @Getter @Setter
|
||||
public class Post {
|
||||
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long id;
|
||||
public ZonedDateTime date;
|
||||
@Column(length = Length.LONG) public String description;
|
||||
public Float latitude;
|
||||
public Float longitude;
|
||||
@Column(length = 64) public String city;
|
||||
@Column(length = 64) public String country;
|
||||
@OneToMany(mappedBy = "post") public List<Asset> assets;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package fr.kektus.summer2024.data.repository;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Asset;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class AssetRepository implements PanacheRepository<Asset> {
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package fr.kektus.summer2024.data.repository;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Post;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PostRepository implements PanacheRepository<Post> {
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package fr.kektus.summer2024.domain.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
public class AssetEntity {
|
||||
public Long id;
|
||||
public String presignedUrl;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package fr.kektus.summer2024.domain.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.With;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor
|
||||
@With @Getter @Setter
|
||||
public class PostEntity {
|
||||
public Long id;
|
||||
public ZonedDateTime date;
|
||||
public String description;
|
||||
public Float latitude;
|
||||
public Float longitude;
|
||||
public String city;
|
||||
public String country;
|
||||
public List<Long> assets;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package fr.kektus.summer2024.domain.entity;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Asset;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.With;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor
|
||||
@With @Getter @Setter
|
||||
public class PostNestedEntity {
|
||||
public Long id;
|
||||
public ZonedDateTime date;
|
||||
public String description;
|
||||
public Float latitude;
|
||||
public Float longitude;
|
||||
public String city;
|
||||
public String country;
|
||||
public List<AssetEntity> assets;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package fr.kektus.summer2024.domain.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
public class PresignedAsset {
|
||||
public Long id;
|
||||
public String filename;
|
||||
public Map<String, String> formData;
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package fr.kektus.summer2024.domain.service;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Asset;
|
||||
import fr.kektus.summer2024.data.repository.AssetRepository;
|
||||
import fr.kektus.summer2024.domain.entity.PresignedAsset;
|
||||
import io.minio.GetPresignedObjectUrlArgs;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PostPolicy;
|
||||
import io.minio.errors.MinioException;
|
||||
import io.minio.http.Method;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.InternalServerErrorException;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.wildfly.common.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ApplicationScoped
|
||||
public class AssetService {
|
||||
@Inject AssetRepository assetRepository;
|
||||
@Inject MinioClient minioClient;
|
||||
|
||||
@ConfigProperty(name = "kektus.assets.bucket") String bucketName;
|
||||
|
||||
@Transactional
|
||||
public PresignedAsset createAsset(String filename) {
|
||||
String actualFilename = UUID.randomUUID() + "_" + filename;
|
||||
PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusMinutes(60));
|
||||
policy.addEqualsCondition("key", actualFilename);
|
||||
policy.addContentLengthRangeCondition(64 * 1024, 200 * 1024 * 1024);
|
||||
try {
|
||||
Map<String, String> formData = minioClient.getPresignedPostFormData(policy);
|
||||
Asset asset = new Asset().withFilename(actualFilename);
|
||||
assetRepository.persist(asset);
|
||||
return new PresignedAsset().withId(asset.id).withFilename(actualFilename).withFormData(formData);
|
||||
} catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
|
||||
throw new InternalServerErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPresignedUrlForAsset(Asset asset) {
|
||||
Assert.assertNotNull(asset);
|
||||
|
||||
try {
|
||||
return minioClient.getPresignedObjectUrl(
|
||||
GetPresignedObjectUrlArgs.builder()
|
||||
.method(Method.GET)
|
||||
.bucket(bucketName)
|
||||
.object(asset.filename)
|
||||
.expiry(10, TimeUnit.MINUTES)
|
||||
.build());
|
||||
} catch (MinioException | InvalidKeyException | IOException | NoSuchAlgorithmException e) {
|
||||
throw new InternalServerErrorException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package fr.kektus.summer2024.domain.service;
|
||||
|
||||
import fr.kektus.summer2024.Validators;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.wildfly.common.Assert;
|
||||
|
||||
@ApplicationScoped
|
||||
public class AuthService {
|
||||
@Inject Logger logger;
|
||||
|
||||
@ConfigProperty(name = "kektus.admin.token") String adminToken;
|
||||
|
||||
public boolean isAdminTokenValid(final String adminToken) {
|
||||
logger.info("Checking admin token");
|
||||
|
||||
Validators.assertNotNull(adminToken, new NotAuthorizedException("no admin token provided"));
|
||||
|
||||
return adminToken.equals(this.adminToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package fr.kektus.summer2024.domain.service;
|
||||
|
||||
import fr.kektus.summer2024.data.model.Asset;
|
||||
import fr.kektus.summer2024.data.model.Post;
|
||||
import fr.kektus.summer2024.data.repository.AssetRepository;
|
||||
import fr.kektus.summer2024.data.repository.PostRepository;
|
||||
import fr.kektus.summer2024.domain.entity.AssetEntity;
|
||||
import fr.kektus.summer2024.domain.entity.PostEntity;
|
||||
import fr.kektus.summer2024.domain.entity.PostNestedEntity;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.wildfly.common.Assert;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@ApplicationScoped
|
||||
public class PostService {
|
||||
@Inject PostRepository postRepository;
|
||||
@Inject AssetRepository assetRepository;
|
||||
@Inject AssetService assetService;
|
||||
|
||||
@Transactional
|
||||
public List<PostNestedEntity> getAllPosts() {
|
||||
return postRepository.findAll().stream().map(post -> new PostNestedEntity()
|
||||
.withId(post.id)
|
||||
.withDescription(post.description)
|
||||
.withDate(post.date)
|
||||
.withLatitude(post.latitude)
|
||||
.withLongitude(post.longitude)
|
||||
.withCity(post.city)
|
||||
.withCountry(post.country)
|
||||
.withAssets(post.assets.stream().map(asset -> new AssetEntity()
|
||||
.withId(asset.id)
|
||||
.withPresignedUrl(assetService.getPresignedUrlForAsset(asset))
|
||||
).toList())).toList();
|
||||
}
|
||||
|
||||
public List<Post> getAllPostsOrdered() {
|
||||
return postRepository.findAll(Sort.descending("id")).stream().toList();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void createPost(PostEntity post) {
|
||||
Assert.assertNotNull(post);
|
||||
Assert.assertNotNull(post.description);
|
||||
Assert.assertNotNull(post.latitude);
|
||||
Assert.assertNotNull(post.longitude);
|
||||
Assert.assertNotNull(post.city);
|
||||
Assert.assertNotNull(post.country);
|
||||
Assert.assertNotNull(post.assets);
|
||||
Assert.assertFalse(post.assets.isEmpty());
|
||||
|
||||
List<Asset> postAssets = post.assets.stream().map(assetRepository::findById).toList();
|
||||
Post model = new Post().withDescription(post.description)
|
||||
.withDate(ZonedDateTime.now())
|
||||
.withLatitude(post.latitude)
|
||||
.withLongitude(post.longitude)
|
||||
.withCity(post.city)
|
||||
.withCountry(post.country)
|
||||
.withAssets(postAssets);
|
||||
postRepository.persist(model);
|
||||
postAssets.forEach(asset -> asset.setPost(model));
|
||||
postAssets.forEach(asset -> assetRepository.persist(asset));
|
||||
|
||||
post.id = model.id;
|
||||
post.date = model.date;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package fr.kektus.summer2024.presentation.rest;
|
||||
|
||||
import fr.kektus.summer2024.domain.service.AssetService;
|
||||
import fr.kektus.summer2024.domain.service.AuthService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
import org.jboss.resteasy.reactive.RestHeader;
|
||||
import org.wildfly.common.Assert;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Path("/admin/assets")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class AdminAssetApi {
|
||||
@Inject AssetService assetService;
|
||||
@Inject AuthService authService;
|
||||
|
||||
@POST
|
||||
public CreateAssets.Response createAsset(@RestHeader("X-admin-token") final String adminToken, CreateAssets.Request request) {
|
||||
if (!authService.isAdminTokenValid(adminToken))
|
||||
throw new NotAuthorizedException("provided admin token is invalid");
|
||||
|
||||
Assert.assertNotNull(request);
|
||||
Assert.assertNotNull(request.filename);
|
||||
|
||||
final var asset = assetService.createAsset(request.filename);
|
||||
return new CreateAssets.Response()
|
||||
.withId(asset.id)
|
||||
.withFilename(asset.filename)
|
||||
.withFormData(asset.formData);
|
||||
}
|
||||
|
||||
public static class CreateAssets {
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
public static class Request {
|
||||
public String filename;
|
||||
}
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
public static class Response {
|
||||
public Long id;
|
||||
public String filename;
|
||||
public Map<String, String> formData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package fr.kektus.summer2024.presentation.rest;
|
||||
|
||||
import fr.kektus.summer2024.domain.entity.PostEntity;
|
||||
import fr.kektus.summer2024.domain.service.AuthService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
import org.jboss.resteasy.reactive.RestHeader;
|
||||
|
||||
@Path("/admin/auth")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class AdminAuthApi {
|
||||
@Inject AuthService authService;
|
||||
|
||||
@GET @Path("/check")
|
||||
public CheckAuth.Response checkAuth(@RestHeader("X-admin-token") final String adminToken) {
|
||||
if (!authService.isAdminTokenValid(adminToken))
|
||||
throw new NotAuthorizedException("provided admin token is invalid");
|
||||
|
||||
return new CheckAuth.Response().withStatus("ok");
|
||||
}
|
||||
|
||||
public static class CheckAuth {
|
||||
@AllArgsConstructor @NoArgsConstructor @With
|
||||
public static class Response {
|
||||
public String status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package fr.kektus.summer2024.presentation.rest;
|
||||
|
||||
import fr.kektus.summer2024.domain.entity.PostEntity;
|
||||
import fr.kektus.summer2024.domain.entity.PostNestedEntity;
|
||||
import fr.kektus.summer2024.domain.service.AuthService;
|
||||
import fr.kektus.summer2024.domain.service.PostService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.NotAuthorizedException;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.With;
|
||||
import org.jboss.resteasy.reactive.RestHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Path("/admin/posts")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class AdminPostApi {
|
||||
@Inject PostService postService;
|
||||
@Inject AuthService authService;
|
||||
|
||||
@POST
|
||||
public PostEntity createPost(@RestHeader("X-admin-token") final String adminToken, CreatePost.Request request) {
|
||||
if (!authService.isAdminTokenValid(adminToken))
|
||||
throw new NotAuthorizedException("provided admin token is invalid");
|
||||
|
||||
PostEntity post = new PostEntity().withDescription(request.description)
|
||||
.withLatitude(request.latitude)
|
||||
.withLongitude(request.longitude)
|
||||
.withCity(request.city)
|
||||
.withCountry(request.country)
|
||||
.withAssets(request.assets);
|
||||
|
||||
postService.createPost(post);
|
||||
|
||||
return post;
|
||||
}
|
||||
|
||||
@GET
|
||||
public List<PostNestedEntity> getPosts(@RestHeader("X-admin-token") final String adminToken) {
|
||||
if (!authService.isAdminTokenValid(adminToken))
|
||||
throw new NotAuthorizedException("provided admin token is invalid");
|
||||
|
||||
return postService.getAllPosts();
|
||||
}
|
||||
|
||||
public static class CreatePost {
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
public static class Request {
|
||||
public String description;
|
||||
public Float latitude;
|
||||
public Float longitude;
|
||||
public String city;
|
||||
public String country;
|
||||
public List<Long> assets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package fr.kektus.summer2024.presentation.rest;
|
||||
|
||||
import fr.kektus.summer2024.converters.PublicPostConverter;
|
||||
import fr.kektus.summer2024.domain.service.PostService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.With;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Path("/posts")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class PostApi {
|
||||
@Inject PostService postService;
|
||||
@Inject PublicPostConverter postConverter;
|
||||
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
@Getter @Setter
|
||||
public static class PostDto {
|
||||
@NoArgsConstructor @AllArgsConstructor @With
|
||||
@Getter @Setter
|
||||
public static class Location {
|
||||
public Float lat;
|
||||
public Float lon;
|
||||
public String city;
|
||||
public String country;
|
||||
}
|
||||
|
||||
public Long id;
|
||||
public ZonedDateTime date;
|
||||
public String description;
|
||||
public Location location;
|
||||
public List<String> assets;
|
||||
}
|
||||
|
||||
@GET
|
||||
public List<PostDto> list() {
|
||||
return postService.getAllPostsOrdered().stream().map(postConverter::toPostDto).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
quarkus.analytics.disabled=true
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
%dev.kektus.assets.bucket=assets
|
||||
%dev.kektus.admin.token=hunter2
|
||||
quarkus.http.cors=true
|
||||
%dev.quarkus.http.cors.origins=/.*/
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
create table Post (
|
||||
id bigserial not null primary key,
|
||||
description text,
|
||||
date timestamp(6) with time zone,
|
||||
city varchar(64),
|
||||
country varchar(64),
|
||||
latitude float4,
|
||||
longitude float4
|
||||
);
|
||||
|
||||
create table Asset (
|
||||
id bigserial not null primary key,
|
||||
filename varchar(255) not null,
|
||||
post_id bigint references Post
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue