qsdqsdqsdef
This commit is contained in:
parent
3d78b90ff1
commit
a88e7d2ce5
@ -83,7 +83,7 @@ RUN pnpm static:build
|
|||||||
#FROM bellsoft/liberica-openjdk-alpine:latest
|
#FROM bellsoft/liberica-openjdk-alpine:latest
|
||||||
## add wget to manage the health check...
|
## add wget to manage the health check...
|
||||||
#RUN apk add --no-cache wget
|
#RUN apk add --no-cache wget
|
||||||
FROM common
|
FROM common AS prod
|
||||||
|
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|
||||||
@ -98,4 +98,6 @@ EXPOSE 80
|
|||||||
HEALTHCHECK --start-period=10s --start-interval=2s --interval=30s --timeout=5s --retries=10 \
|
HEALTHCHECK --start-period=10s --start-interval=2s --interval=30s --timeout=5s --retries=10 \
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost:80/api/health_check || exit 1
|
CMD wget --no-verbose --tries=1 --spider http://localhost:80/api/health_check || exit 1
|
||||||
|
|
||||||
CMD ["java", "-Xms64M", "-Xmx1G", "-cp", "/application/application.jar", "org.atriasoft.karideo.WebLauncher"]
|
CMD ["java", "-Xms128M", "-Xmx1G", "-cp", "/application/application.jar", "org.atriasoft.karideo.WebLauncher"]
|
||||||
|
|
||||||
|
#RUN cat /etc/passwd
|
@ -53,6 +53,9 @@ Checkstyle configuration that checks the sun coding conventions.
|
|||||||
<module name="LambdaParameterName"/>
|
<module name="LambdaParameterName"/>
|
||||||
<module name="Regexp"/>
|
<module name="Regexp"/>
|
||||||
<module name="RegexpSinglelineJava"/>
|
<module name="RegexpSinglelineJava"/>
|
||||||
|
<module name="UnusedPrivateField">
|
||||||
|
<property name="ignorePattern" value="LOGGER"/>
|
||||||
|
</module>
|
||||||
</module>
|
</module>
|
||||||
<module name="BeforeExecutionExclusionFileFilter">
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||||
|
245
back/pom.xml.versionsBackup
Normal file
245
back/pom.xml.versionsBackup
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.kar</groupId>
|
||||||
|
<artifactId>karideo</artifactId>
|
||||||
|
<version>0.3.0</version>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.version>3.1</maven.compiler.version>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<maven.dependency.version>3.1.1</maven.dependency.version>
|
||||||
|
</properties>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>gitea</id>
|
||||||
|
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>kangaroo-and-rabbit</groupId>
|
||||||
|
<artifactId>archidata</artifactId>
|
||||||
|
<version>0.11.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>2.0.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.16.1</version>
|
||||||
|
</dependency>
|
||||||
|
<!--
|
||||||
|
************************************************************
|
||||||
|
** TEST dependency **
|
||||||
|
************************************************************
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
|
<version>5.10.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>5.10.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src</sourceDirectory>
|
||||||
|
<testSourceDirectory>test/src</testSourceDirectory>
|
||||||
|
<directory>${project.basedir}/out/maven/</directory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/resources</directory>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<testResources>
|
||||||
|
<testResource>
|
||||||
|
<directory>${basedir}/test/resources</directory>
|
||||||
|
</testResource>
|
||||||
|
</testResources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>${maven.compiler.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<source>${maven.compiler.source}</source>
|
||||||
|
<target>${maven.compiler.target}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.4.0</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.kar.karideo.WebLauncher</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Create the source bundle -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>3.2.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>attach-sources</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!-- junit results -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>3.0.0-M5</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>fully.qualified.MainClass</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Create coverage -->
|
||||||
|
<!--
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>0.8.10</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>prepare-agent</id>
|
||||||
|
<goals>
|
||||||
|
<goal>prepare-agent</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>report</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>jacoco-check</id>
|
||||||
|
<goals>
|
||||||
|
<goal>check</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<rules>
|
||||||
|
<rule>
|
||||||
|
<element>PACKAGE</element>
|
||||||
|
<limits>
|
||||||
|
<limit>
|
||||||
|
<counter>LINE</counter>
|
||||||
|
<value>COVEREDRATIO</value>
|
||||||
|
<minimum>0.50</minimum>
|
||||||
|
</limit>
|
||||||
|
</limits>
|
||||||
|
</rule>
|
||||||
|
</rules>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
-->
|
||||||
|
<!-- Java-doc generation for stand-alone site -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.2.0</version>
|
||||||
|
<configuration>
|
||||||
|
<show>private</show>
|
||||||
|
<nohelp>true</nohelp>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>exec-application</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>java</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>org.kar.karideo.WebLauncher</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- Check the style of the code -->
|
||||||
|
<!--
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<configLocation>CheckStyle.xml</configLocation>
|
||||||
|
<consoleOutput>true</consoleOutput>
|
||||||
|
<failOnViolation>true</failOnViolation>
|
||||||
|
<failsOnError>true</failsOnError>
|
||||||
|
<includeTestSourceDirectory>true</includeTestSourceDirectory>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>net.revelc.code.formatter</groupId>
|
||||||
|
<artifactId>formatter-maven-plugin</artifactId>
|
||||||
|
<version>2.12.2</version>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<lineEnding>LF</lineEnding>
|
||||||
|
<configFile>Formatter.xml</configFile>
|
||||||
|
<directories>
|
||||||
|
<directory>src/</directory>
|
||||||
|
<directory>test/src</directory>
|
||||||
|
</directories>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.java</include>
|
||||||
|
</includes>
|
||||||
|
<excludes>
|
||||||
|
<exclude>module-info.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>validate</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
-->
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<!-- Generate Java-docs As Part Of Project Reports -->
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.2.0</version>
|
||||||
|
<configuration>
|
||||||
|
<show>public</show>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
</project>
|
20
back/src/org/atriasoft/karideo/CacheFilter.java__
Normal file
20
back/src/org/atriasoft/karideo/CacheFilter.java__
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package org.kar.karideo;
|
||||||
|
|
||||||
|
public class CacheFilter {
|
||||||
|
@Override
|
||||||
|
public List<ResourceFilter> create(AbstractMethod am) {
|
||||||
|
if (am.isAnnotationPresent(CacheMaxAge.class)) {
|
||||||
|
CacheMaxAge maxAge = am.getAnnotation(CacheMaxAge.class);
|
||||||
|
return newCacheFilter("max-age: " + maxAge.unit().toSeconds(maxAge.time()));
|
||||||
|
} else if (am.isAnnotationPresent(NoCache.class)) {
|
||||||
|
return newCacheFilter("no-cache");
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ResourceFilter> newCacheFilter(String content) {
|
||||||
|
return Collections
|
||||||
|
.<ResourceFilter> singletonList(new CacheResponseFilter(content));
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package org.atriasoft.karideo;
|
package org.atriasoft.karideo;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.TimeZone;
|
||||||
import java.util.logging.LogManager;
|
import java.util.logging.LogManager;
|
||||||
|
|
||||||
import org.atriasoft.archidata.UpdateJwtPublicKey;
|
import org.atriasoft.archidata.UpdateJwtPublicKey;
|
||||||
import org.atriasoft.archidata.api.DataResource;
|
|
||||||
import org.atriasoft.archidata.catcher.GenericCatcher;
|
import org.atriasoft.archidata.catcher.GenericCatcher;
|
||||||
import org.atriasoft.archidata.db.DbConfig;
|
import org.atriasoft.archidata.db.DbConfig;
|
||||||
import org.atriasoft.archidata.filter.CORSFilter;
|
import org.atriasoft.archidata.filter.CORSFilter;
|
||||||
@ -12,6 +12,7 @@ import org.atriasoft.archidata.filter.OptionFilter;
|
|||||||
import org.atriasoft.archidata.migration.MigrationEngine;
|
import org.atriasoft.archidata.migration.MigrationEngine;
|
||||||
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
||||||
import org.atriasoft.archidata.tools.ContextGenericTools;
|
import org.atriasoft.archidata.tools.ContextGenericTools;
|
||||||
|
import org.atriasoft.karideo.api.DataResource;
|
||||||
import org.atriasoft.karideo.api.Front;
|
import org.atriasoft.karideo.api.Front;
|
||||||
import org.atriasoft.karideo.api.HealthCheck;
|
import org.atriasoft.karideo.api.HealthCheck;
|
||||||
import org.atriasoft.karideo.api.MediaResource;
|
import org.atriasoft.karideo.api.MediaResource;
|
||||||
@ -47,6 +48,7 @@ public class WebLauncher {
|
|||||||
protected HttpServer server = null;
|
protected HttpServer server = null;
|
||||||
|
|
||||||
public WebLauncher() {
|
public WebLauncher() {
|
||||||
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
ConfigBaseVariable.bdDatabase = "karideo";
|
ConfigBaseVariable.bdDatabase = "karideo";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +128,10 @@ public class WebLauncher {
|
|||||||
|
|
||||||
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
|
this.server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc);
|
||||||
final HttpServer serverLink = this.server;
|
final HttpServer serverLink = this.server;
|
||||||
|
// for (final NetworkListener listener : serverLink.getListeners()) {
|
||||||
|
// listener.getKeepAlive().setIdleTimeoutInSeconds(30); // Set idle timeout
|
||||||
|
// listener.getKeepAlive().setMaxRequestsCount(80); // Set request timeout
|
||||||
|
// }
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
531
back/src/org/atriasoft/karideo/api/DataResource.java
Normal file
531
back/src/org/atriasoft/karideo/api/DataResource.java
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
package org.atriasoft.karideo.api;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import org.atriasoft.archidata.annotation.apiGenerator.ApiInputOptional;
|
||||||
|
import org.atriasoft.archidata.annotation.security.PermitTokenInURI;
|
||||||
|
import org.atriasoft.archidata.api.MediaStreamer;
|
||||||
|
import org.atriasoft.archidata.dataAccess.DataAccess;
|
||||||
|
import org.atriasoft.archidata.dataAccess.QueryCondition;
|
||||||
|
import org.atriasoft.archidata.dataAccess.options.Condition;
|
||||||
|
import org.atriasoft.archidata.exception.FailException;
|
||||||
|
import org.atriasoft.archidata.filter.GenericContext;
|
||||||
|
import org.atriasoft.archidata.model.Data;
|
||||||
|
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
|
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
||||||
|
import org.glassfish.jersey.media.multipart.FormDataParam;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
|
import jakarta.ws.rs.Consumes;
|
||||||
|
import jakarta.ws.rs.GET;
|
||||||
|
import jakarta.ws.rs.HeaderParam;
|
||||||
|
import jakarta.ws.rs.InternalServerErrorException;
|
||||||
|
import jakarta.ws.rs.POST;
|
||||||
|
import jakarta.ws.rs.Path;
|
||||||
|
import jakarta.ws.rs.PathParam;
|
||||||
|
import jakarta.ws.rs.Produces;
|
||||||
|
import jakarta.ws.rs.QueryParam;
|
||||||
|
import jakarta.ws.rs.core.CacheControl;
|
||||||
|
import jakarta.ws.rs.core.Context;
|
||||||
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import jakarta.ws.rs.core.SecurityContext;
|
||||||
|
import jakarta.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/35367113/jersey-webservice-scalable-approach-to-download-file-and-reply-to-client
|
||||||
|
// https://gist.github.com/aitoroses/4f7a2b197b732a6a691d
|
||||||
|
|
||||||
|
@Path("/data")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public class DataResource {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class);
|
||||||
|
private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
|
||||||
|
private final static int CHUNK_SIZE_IN = 50 * 1024 * 1024; // 1MB chunks
|
||||||
|
/** Upload some datas */
|
||||||
|
private static long tmpFolderId = 1;
|
||||||
|
|
||||||
|
private static void createFolder(final String path) throws IOException {
|
||||||
|
if (!Files.exists(java.nio.file.Path.of(path))) {
|
||||||
|
// Log.print("Create folder: " + path);
|
||||||
|
Files.createDirectories(java.nio.file.Path.of(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getTmpDataId() {
|
||||||
|
return tmpFolderId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTmpFileInData(final long tmpFolderId) {
|
||||||
|
final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId;
|
||||||
|
try {
|
||||||
|
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileDataOld(final UUID uuid) {
|
||||||
|
final String stringUUID = uuid.toString();
|
||||||
|
final String part1 = stringUUID.substring(0, 2);
|
||||||
|
final String part2 = stringUUID.substring(2, 4);
|
||||||
|
final String part3 = stringUUID.substring(4);
|
||||||
|
final String finalPath = part1 + File.separator + part2;
|
||||||
|
String filePath = ConfigBaseVariable.getMediaDataFolder() + "_uuid" + File.separator + finalPath
|
||||||
|
+ File.separator;
|
||||||
|
try {
|
||||||
|
createFolder(filePath);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
filePath += part3;
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileData(final ObjectId oid) {
|
||||||
|
final String stringOid = oid.toHexString();
|
||||||
|
String dir1 = stringOid.substring(0, 2);
|
||||||
|
String dir2 = stringOid.substring(2, 4);
|
||||||
|
String dir3 = stringOid.substring(4, 6);
|
||||||
|
try {
|
||||||
|
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
final byte[] hashBytes = digest.digest(oid.toByteArray());
|
||||||
|
dir1 = String.format("%02x", hashBytes[0]);
|
||||||
|
dir2 = String.format("%02x", hashBytes[1]);
|
||||||
|
dir3 = String.format("%02x", hashBytes[2]);
|
||||||
|
} catch (final NoSuchAlgorithmException ex) {
|
||||||
|
LOGGER.error("Fail to generate the hash of the objectId ==> ise direct value ... {}", ex.getMessage());
|
||||||
|
}
|
||||||
|
final String finalPath = dir1 + File.separator + dir2 + File.separator + dir3;
|
||||||
|
String filePath = ConfigBaseVariable.getMediaDataFolder() + "_oid" + File.separator + finalPath
|
||||||
|
+ File.separator;
|
||||||
|
try {
|
||||||
|
createFolder(filePath);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
filePath += stringOid;
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileMetaData(final ObjectId oid) {
|
||||||
|
return getFileData(oid) + ".json";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data getWithSha512(final String sha512) {
|
||||||
|
LOGGER.info("find sha512 = {}", sha512);
|
||||||
|
try {
|
||||||
|
return DataAccess.getWhere(Data.class, new Condition(new QueryCondition("sha512", "=", sha512)));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data getWithId(final long id) {
|
||||||
|
LOGGER.info("find id = {}", id);
|
||||||
|
try {
|
||||||
|
return DataAccess.get(Data.class, id);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getMimeType(final String extension) throws IOException {
|
||||||
|
return switch (extension.toLowerCase()) {
|
||||||
|
case "jpg", "jpeg" -> "image/jpeg";
|
||||||
|
case "png" -> "image/png";
|
||||||
|
case "webp" -> "image/webp";
|
||||||
|
case "mka" -> "audio/x-matroska";
|
||||||
|
case "mkv" -> "video/x-matroska";
|
||||||
|
case "webm" -> "video/webm";
|
||||||
|
default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data createNewData(final long tmpUID, final String originalFileName, final String sha512)
|
||||||
|
throws IOException {
|
||||||
|
// determine mime type:
|
||||||
|
Data injectedData = new Data();
|
||||||
|
String mimeType = "";
|
||||||
|
final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
|
||||||
|
mimeType = getMimeType(extension);
|
||||||
|
injectedData.mimeType = mimeType;
|
||||||
|
injectedData.sha512 = sha512;
|
||||||
|
final String tmpPath = getTmpFileInData(tmpUID);
|
||||||
|
injectedData.size = Files.size(Paths.get(tmpPath));
|
||||||
|
|
||||||
|
try {
|
||||||
|
injectedData = DataAccess.insert(injectedData);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final String mediaPath = getFileData(injectedData.oid);
|
||||||
|
LOGGER.info("src = {}", tmpPath);
|
||||||
|
LOGGER.info("dst = {}", mediaPath);
|
||||||
|
Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE);
|
||||||
|
LOGGER.info("Move done");
|
||||||
|
return injectedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void modeFileOldModelToNewModel(final UUID uuid, final ObjectId oid) throws IOException {
|
||||||
|
String mediaCurentPath = getFileDataOld(uuid);
|
||||||
|
String mediaDestPath = getFileData(oid);
|
||||||
|
LOGGER.info("src = {}", mediaCurentPath);
|
||||||
|
LOGGER.info("dst = {}", mediaDestPath);
|
||||||
|
if (Files.exists(Paths.get(mediaCurentPath))) {
|
||||||
|
LOGGER.info("move: {} ==> {}", mediaCurentPath, mediaDestPath);
|
||||||
|
Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE);
|
||||||
|
}
|
||||||
|
// Move old meta-data...
|
||||||
|
mediaCurentPath = mediaCurentPath.substring(mediaCurentPath.length() - 4) + "meta.json";
|
||||||
|
mediaDestPath = mediaCurentPath.substring(mediaDestPath.length() - 4) + "meta.json";
|
||||||
|
if (Files.exists(Paths.get(mediaCurentPath))) {
|
||||||
|
LOGGER.info("moveM: {} ==> {}", mediaCurentPath, mediaDestPath);
|
||||||
|
Files.move(Paths.get(mediaCurentPath), Paths.get(mediaDestPath), StandardCopyOption.ATOMIC_MOVE);
|
||||||
|
}
|
||||||
|
LOGGER.info("Move done");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData)
|
||||||
|
throws FailException {
|
||||||
|
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeTemporaryFile(final long idData) {
|
||||||
|
final String filepath = DataResource.getTmpFileInData(idData);
|
||||||
|
if (Files.exists(Paths.get(filepath))) {
|
||||||
|
try {
|
||||||
|
Files.delete(Paths.get(filepath));
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOGGER.info("can not delete temporary file : {}", Paths.get(filepath));
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save uploaded file to a defined location on the server
|
||||||
|
static String saveFile(final InputStream uploadedInputStream, final String serverLocation) throws FailException {
|
||||||
|
String out = "";
|
||||||
|
MessageDigest md = null;
|
||||||
|
try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
|
||||||
|
md = MessageDigest.getInstance("SHA-512");
|
||||||
|
outpuStream.flush();
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
|
||||||
|
} catch (final NoSuchAlgorithmException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not find sha512 algorithms", ex);
|
||||||
|
}
|
||||||
|
if (md != null) {
|
||||||
|
try (OutputStream outpuStream = new FileOutputStream(new File(serverLocation))) {
|
||||||
|
int read = 0;
|
||||||
|
final byte[] bytes = new byte[CHUNK_SIZE_IN];
|
||||||
|
while ((read = uploadedInputStream.read(bytes)) != -1) {
|
||||||
|
// logger.info("write {}", read);
|
||||||
|
md.update(bytes, 0, read);
|
||||||
|
outpuStream.write(bytes, 0, read);
|
||||||
|
}
|
||||||
|
LOGGER.info("Flush input stream ... {}", serverLocation);
|
||||||
|
outpuStream.flush();
|
||||||
|
// create the end of sha512
|
||||||
|
final byte[] sha512Digest = md.digest();
|
||||||
|
// convert in hexadecimal
|
||||||
|
out = bytesToHex(sha512Digest);
|
||||||
|
uploadedInputStream.close();
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Can not write in temporary file", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String bytesToHex(final byte[] bytes) {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
for (final byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data getSmall(final ObjectId oid) {
|
||||||
|
try {
|
||||||
|
return DataAccess.get(Data.class, oid);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/upload/")
|
||||||
|
@Consumes({ MediaType.MULTIPART_FORM_DATA })
|
||||||
|
@RolesAllowed("ADMIN")
|
||||||
|
@Operation(description = "Insert a new data in the data environment", tags = "SYSTEM")
|
||||||
|
public void uploadFile(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@FormDataParam("file") final InputStream fileInputStream,
|
||||||
|
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws FailException {
|
||||||
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
|
LOGGER.info("===================================================");
|
||||||
|
LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken));
|
||||||
|
LOGGER.info("===================================================");
|
||||||
|
// public NodeSmall uploadFile(final FormDataMultiPart form) {
|
||||||
|
LOGGER.info("Upload file: ");
|
||||||
|
final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
|
||||||
|
try {
|
||||||
|
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
|
||||||
|
"Impossible to create the folder in the server", ex);
|
||||||
|
}
|
||||||
|
saveFile(fileInputStream, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{oid}")
|
||||||
|
@PermitTokenInURI
|
||||||
|
@RolesAllowed("USER")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
@Operation(description = "Get back some data from the data environment", tags = "SYSTEM")
|
||||||
|
public Response retrieveDataId(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
|
||||||
|
@HeaderParam("Range") final String range,
|
||||||
|
@PathParam("oid") final ObjectId oid) throws FailException {
|
||||||
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
|
// logger.info("===================================================");
|
||||||
|
LOGGER.info("== DATA retrieveDataId ? oid={} user={}", oid, (gc == null ? "null" : gc.userByToken));
|
||||||
|
// logger.info("===================================================");
|
||||||
|
final Data value = getSmall(oid);
|
||||||
|
if (value == null) {
|
||||||
|
return Response.status(404).entity("media NOT FOUND: " + oid).type("text/plain").build();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return buildStream(getFileData(oid), range,
|
||||||
|
value.mimeType == null ? "application/octet-stream" : value.mimeType);
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("thumbnail/{oid}")
|
||||||
|
@RolesAllowed("USER")
|
||||||
|
@PermitTokenInURI
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
@Operation(description = "Get a thumbnail of from the data environment (if resize is possible)", tags = "SYSTEM")
|
||||||
|
// @CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
|
||||||
|
public Response retrieveDataThumbnailId(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
|
||||||
|
@HeaderParam("Range") final String range,
|
||||||
|
@PathParam("oid") final ObjectId oid) throws FailException {
|
||||||
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
|
LOGGER.info("===================================================");
|
||||||
|
LOGGER.info("== DATA retrieveDataThumbnailId ? {}", (gc == null ? "null" : gc.userByToken));
|
||||||
|
LOGGER.info("===================================================");
|
||||||
|
final Data value = getSmall(oid);
|
||||||
|
if (value == null) {
|
||||||
|
return Response.status(404).entity("media NOT FOUND: " + oid).type("text/plain").build();
|
||||||
|
}
|
||||||
|
final String filePathName = getFileData(oid);
|
||||||
|
final File inputFile = new File(filePathName);
|
||||||
|
if (!inputFile.exists()) {
|
||||||
|
return Response.status(404).entity("{\"error\":\"media Does not exist: " + oid + "\"}")
|
||||||
|
.type("application/json").build();
|
||||||
|
}
|
||||||
|
if (value.mimeType.contentEquals("image/jpeg") || value.mimeType.contentEquals("image/png")
|
||||||
|
// || value.mimeType.contentEquals("image/webp")
|
||||||
|
) {
|
||||||
|
// reads input image
|
||||||
|
BufferedImage inputImage;
|
||||||
|
try {
|
||||||
|
inputImage = ImageIO.read(inputFile);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to READ the image", ex);
|
||||||
|
}
|
||||||
|
LOGGER.info("input size image: {}x{} type={}", inputImage.getWidth(), inputImage.getHeight(),
|
||||||
|
inputImage.getType());
|
||||||
|
final int scaledWidth = ConfigBaseVariable.getThumbnailWidth();
|
||||||
|
final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth()
|
||||||
|
* scaledWidth);
|
||||||
|
// creates output image
|
||||||
|
final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType());
|
||||||
|
|
||||||
|
// scales the input image to the output image
|
||||||
|
final Graphics2D g2d = outputImage.createGraphics();
|
||||||
|
LOGGER.info("output size image: {}x{}", scaledWidth, scaledHeight);
|
||||||
|
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
|
||||||
|
g2d.dispose();
|
||||||
|
// create the output stream:
|
||||||
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
ImageIO.write(outputImage, ConfigBaseVariable.getThumbnailFormat(), baos);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
final byte[] imageData = baos.toByteArray();
|
||||||
|
LOGGER.info("output length {}", imageData.length);
|
||||||
|
if (imageData.length == 0) {
|
||||||
|
LOGGER.error("Fail to convert image... Availlable format:");
|
||||||
|
for (final String data : ImageIO.getWriterFormatNames()) {
|
||||||
|
LOGGER.error(" - {}", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH,
|
||||||
|
imageData.length);
|
||||||
|
try {
|
||||||
|
out.type(getMimeType(ConfigBaseVariable.getThumbnailFormat()));
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR,
|
||||||
|
"Fail to convert mime type of " + ConfigBaseVariable.getThumbnailFormat(), ex);
|
||||||
|
}
|
||||||
|
// TODO: move this in a decorator !!!
|
||||||
|
final CacheControl cc = new CacheControl();
|
||||||
|
cc.setMaxAge(3600);
|
||||||
|
cc.setNoCache(false);
|
||||||
|
out.cacheControl(cc);
|
||||||
|
return out.build();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return buildStream(filePathName, range, value.mimeType);
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to build output stream", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("{oid}/{name}")
|
||||||
|
@PermitTokenInURI
|
||||||
|
@RolesAllowed("USER")
|
||||||
|
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
@Operation(description = "Get back some data from the data environment (with a beautiful name (permit download with basic name)", tags = "SYSTEM")
|
||||||
|
public Response retrieveDataFull(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@QueryParam(HttpHeaders.AUTHORIZATION) final String token,
|
||||||
|
@ApiInputOptional @HeaderParam("Range") final String range,
|
||||||
|
@PathParam("oid") final ObjectId oid,
|
||||||
|
@PathParam("name") final String name) throws Exception {
|
||||||
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
|
// logger.info("===================================================");
|
||||||
|
LOGGER.info("== DATA retrieveDataFull ? id={} user={}", oid, (gc == null ? "null" : gc.userByToken));
|
||||||
|
// logger.info("===================================================");
|
||||||
|
final Data value = getSmall(oid);
|
||||||
|
if (value == null) {
|
||||||
|
return Response.status(404).entity("media NOT FOUND: " + oid).type("text/plain").build();
|
||||||
|
}
|
||||||
|
return buildStream(getFileData(oid), range,
|
||||||
|
value.mimeType == null ? "application/octet-stream" : value.mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adapted from http://stackoverflow.com/questions/12768812/video-streaming-to-ipad-does-not-work-with-tapestry5/12829541#12829541
|
||||||
|
*
|
||||||
|
* @param range range header
|
||||||
|
* @return Streaming output
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
* @throws Exception IOException if an error occurs in streaming. */
|
||||||
|
private Response buildStream(final String filename, final String range, final String mimeType)
|
||||||
|
throws FailException {
|
||||||
|
final File file = new File(filename);
|
||||||
|
// logger.info("request range : {}", range);
|
||||||
|
// range not requested : Firefox does not send range headers
|
||||||
|
if (range == null) {
|
||||||
|
final StreamingOutput output = new StreamingOutput() {
|
||||||
|
@Override
|
||||||
|
public void write(final OutputStream out) {
|
||||||
|
try (FileInputStream in = new FileInputStream(file)) {
|
||||||
|
final byte[] buf = new byte[1024 * 1024];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) != -1) {
|
||||||
|
try {
|
||||||
|
out.write(buf, 0, len);
|
||||||
|
out.flush();
|
||||||
|
// logger.info("---- wrote {} bytes file ----", len);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
LOGGER.info("remote close connection");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new InternalServerErrorException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final Response.ResponseBuilder out = Response.ok(output).header(HttpHeaders.CONTENT_LENGTH, file.length());
|
||||||
|
if (mimeType != null) {
|
||||||
|
out.type(mimeType);
|
||||||
|
}
|
||||||
|
return out.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] ranges = range.split("=")[1].split("-");
|
||||||
|
final long from = Long.parseLong(ranges[0]);
|
||||||
|
|
||||||
|
// logger.info("request range : {}", ranges.length);
|
||||||
|
// Chunk media if the range upper bound is unspecified. Chrome, Opera sends "bytes=0-"
|
||||||
|
long to = CHUNK_SIZE + from;
|
||||||
|
if (ranges.length == 1) {
|
||||||
|
to = file.length() - 1;
|
||||||
|
} else if (to >= file.length()) {
|
||||||
|
to = file.length() - 1;
|
||||||
|
}
|
||||||
|
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
|
||||||
|
// LOGGER.info("responseRange: {}", responseRange);
|
||||||
|
try {
|
||||||
|
final RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||||
|
raf.seek(from);
|
||||||
|
|
||||||
|
final long len = to - from + 1;
|
||||||
|
final MediaStreamer streamer = new MediaStreamer(len, raf);
|
||||||
|
final Response.ResponseBuilder out = Response.ok(streamer).status(Response.Status.PARTIAL_CONTENT)
|
||||||
|
.header("Accept-Ranges", "bytes").header("Content-Range", responseRange)
|
||||||
|
.header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth())
|
||||||
|
.header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified()));
|
||||||
|
if (mimeType != null) {
|
||||||
|
out.type(mimeType);
|
||||||
|
}
|
||||||
|
return out.build();
|
||||||
|
} catch (final FileNotFoundException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to find the required file.", ex);
|
||||||
|
} catch (final IOException ex) {
|
||||||
|
throw new FailException(Response.Status.INTERNAL_SERVER_ERROR, "Fail to access to the required file.", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void undelete(final Long id) throws Exception {
|
||||||
|
DataAccess.unsetDelete(Data.class, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -29,34 +29,43 @@ import jakarta.ws.rs.core.SecurityContext;
|
|||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public class UserMediaAdvancementResource {
|
public class UserMediaAdvancementResource {
|
||||||
static final Logger LOGGER = LoggerFactory.getLogger(UserMediaAdvancementResource.class);
|
static final Logger LOGGER = LoggerFactory.getLogger(UserMediaAdvancementResource.class);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed("USER")
|
@RolesAllowed("USER")
|
||||||
@Operation(description = "Get a specific user advancement with his ID", tags = "GLOBAL")
|
@Operation(description = "Get a specific user advancement with his ID", tags = "GLOBAL")
|
||||||
public UserMediaAdvancement get(@Context final SecurityContext sc, @PathParam("id") final Long id) throws Exception {
|
public UserMediaAdvancement get(@Context final SecurityContext sc, @PathParam("id") final Long id)
|
||||||
|
throws Exception {
|
||||||
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
return DataAccess.getWhere(UserMediaAdvancement.class, new Condition(new QueryAnd(new QueryCondition("mediaId", "=", id), new QueryCondition("userId", "=", gc.userByToken.id))));
|
return DataAccess.getWhere(UserMediaAdvancement.class,
|
||||||
|
new Condition(new QueryAnd(new QueryCondition("mediaId", "=", id),
|
||||||
|
new QueryCondition("userId", "=", gc.userByToken.id))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@RolesAllowed("USER")
|
@RolesAllowed("USER")
|
||||||
@Operation(description = "Get all user advancement", tags = "GLOBAL")
|
@Operation(description = "Get all user advancement", tags = "GLOBAL")
|
||||||
public List<UserMediaAdvancement> gets(@Context final SecurityContext sc) throws Exception {
|
public List<UserMediaAdvancement> gets(@Context final SecurityContext sc) throws Exception {
|
||||||
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
return DataAccess.getsWhere(UserMediaAdvancement.class, new Condition(new QueryCondition("userId", "=", gc.userByToken.id)));
|
return DataAccess.getsWhere(UserMediaAdvancement.class,
|
||||||
|
new Condition(new QueryCondition("userId", "=", gc.userByToken.id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================================= Modification SECTION: ============================================================================= */
|
/* ============================================================================= Modification SECTION: ============================================================================= */
|
||||||
|
|
||||||
public record MediaInformations(int time, float percent, int count) {
|
public record MediaInformations(
|
||||||
}
|
int time,
|
||||||
|
float percent,
|
||||||
|
int count) {}
|
||||||
|
|
||||||
// @POST
|
// @POST
|
||||||
// @Path("{id}")
|
// @Path("{id}")
|
||||||
// @RolesAllowed("USER")
|
// @RolesAllowed("USER")
|
||||||
// @Consumes(MediaType.APPLICATION_JSON)
|
// @Consumes(MediaType.APPLICATION_JSON)
|
||||||
public UserMediaAdvancement post(@Context final SecurityContext sc, @PathParam("id") final Long id, final MediaInformations data) throws Exception {
|
public UserMediaAdvancement post(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@PathParam("id") final Long id,
|
||||||
|
final MediaInformations data) throws Exception {
|
||||||
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
|
||||||
final UserMediaAdvancement elem = new UserMediaAdvancement();
|
final UserMediaAdvancement elem = new UserMediaAdvancement();
|
||||||
elem.userId = gc.userByToken.id;
|
elem.userId = gc.userByToken.id;
|
||||||
@ -66,16 +75,21 @@ public class UserMediaAdvancementResource {
|
|||||||
elem.count = data.count;
|
elem.count = data.count;
|
||||||
return DataAccess.insert(elem);
|
return DataAccess.insert(elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record MediaInformationsDelta(int time, float percent, boolean addCount) {
|
public record MediaInformationsDelta(
|
||||||
}
|
int time,
|
||||||
|
float percent,
|
||||||
|
boolean addCount) {}
|
||||||
|
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed("USER")
|
@RolesAllowed("USER")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@Operation(description = "Modify a user advancement", tags = "GLOBAL")
|
@Operation(description = "Modify a user advancement", tags = "GLOBAL")
|
||||||
public UserMediaAdvancement patch(@Context final SecurityContext sc, @PathParam("id") final Long id, @Valid final MediaInformationsDelta data) throws Exception {
|
public UserMediaAdvancement patch(
|
||||||
|
@Context final SecurityContext sc,
|
||||||
|
@PathParam("id") final Long id,
|
||||||
|
@Valid final MediaInformationsDelta data) throws Exception {
|
||||||
final UserMediaAdvancement elem = get(sc, id);
|
final UserMediaAdvancement elem = get(sc, id);
|
||||||
if (elem == null) {
|
if (elem == null) {
|
||||||
// insert element
|
// insert element
|
||||||
@ -91,10 +105,10 @@ public class UserMediaAdvancementResource {
|
|||||||
elem.count++;
|
elem.count++;
|
||||||
}
|
}
|
||||||
LOGGER.info("{},{},{}", elem.time, elem.percent, elem.count);
|
LOGGER.info("{},{},{}", elem.time, elem.percent, elem.count);
|
||||||
final long nbAfected = DataAccess.update(elem, elem.id, List.of("time", "percent", "count"));
|
DataAccess.update(elem, elem.id, List.of("time", "percent", "count"));
|
||||||
return DataAccess.get(UserMediaAdvancement.class, elem.id);
|
return DataAccess.get(UserMediaAdvancement.class, elem.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("{id}")
|
@Path("{id}")
|
||||||
@RolesAllowed("USER")
|
@RolesAllowed("USER")
|
||||||
@ -103,5 +117,5 @@ public class UserMediaAdvancementResource {
|
|||||||
final UserMediaAdvancement elem = get(sc, id);
|
final UserMediaAdvancement elem = get(sc, id);
|
||||||
DataAccess.delete(UserMediaAdvancement.class, elem.id);
|
DataAccess.delete(UserMediaAdvancement.class, elem.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
package test.atriasoft.karideo;
|
package test.atriasoft.karideo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.atriasoft.archidata.dataAccess.DBAccess;
|
import org.atriasoft.archidata.dataAccess.DBAccess;
|
||||||
import org.atriasoft.archidata.db.DbConfig;
|
import org.atriasoft.archidata.db.DbConfig;
|
||||||
import org.atriasoft.archidata.db.DbIoFactory;
|
import org.atriasoft.archidata.db.DbIoFactory;
|
||||||
import org.atriasoft.archidata.exception.DataAccessException;
|
import org.atriasoft.archidata.exception.DataAccessException;
|
||||||
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
||||||
import org.atriasoft.karideo.model.Media;
|
|
||||||
import org.atriasoft.karideo.model.Season;
|
|
||||||
import org.atriasoft.karideo.model.Series;
|
|
||||||
import org.atriasoft.karideo.model.Type;
|
|
||||||
import org.atriasoft.karideo.model.UserKarideo;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -22,7 +16,7 @@ public class ConfigureDb {
|
|||||||
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
|
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
|
||||||
final static private String modeTestForced = null;// "MONGO";
|
final static private String modeTestForced = null;// "MONGO";
|
||||||
public static DBAccess da = null;
|
public static DBAccess da = null;
|
||||||
|
|
||||||
public static void configure() throws IOException, InternalServerErrorException, DataAccessException {
|
public static void configure() throws IOException, InternalServerErrorException, DataAccessException {
|
||||||
String modeTest = System.getenv("TEST_E2E_MODE");
|
String modeTest = System.getenv("TEST_E2E_MODE");
|
||||||
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
||||||
@ -38,13 +32,6 @@ public class ConfigureDb {
|
|||||||
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12342/test/api/";
|
ConfigBaseVariable.apiAdress = "http://127.0.0.1:12342/test/api/";
|
||||||
// Enable the test mode permit to access to the test token (never use it in production).
|
// Enable the test mode permit to access to the test token (never use it in production).
|
||||||
ConfigBaseVariable.testMode = "true";
|
ConfigBaseVariable.testMode = "true";
|
||||||
final List<Class<?>> listObject = List.of( //
|
|
||||||
Media.class, //
|
|
||||||
Season.class, //
|
|
||||||
Series.class, //
|
|
||||||
Type.class, //
|
|
||||||
UserKarideo.class //
|
|
||||||
);
|
|
||||||
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
|
if ("SQLITE-MEMORY".equalsIgnoreCase(modeTest)) {
|
||||||
ConfigBaseVariable.dbType = "sqlite";
|
ConfigBaseVariable.dbType = "sqlite";
|
||||||
ConfigBaseVariable.bdDatabase = null;
|
ConfigBaseVariable.bdDatabase = null;
|
||||||
@ -73,7 +60,7 @@ public class ConfigureDb {
|
|||||||
// Connect the dataBase...
|
// Connect the dataBase...
|
||||||
da = DBAccess.createInterface();
|
da = DBAccess.createInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void removeDB() {
|
public static void removeDB() {
|
||||||
String modeTest = System.getenv("TEST_E2E_MODE");
|
String modeTest = System.getenv("TEST_E2E_MODE");
|
||||||
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
if (modeTest == null || modeTest.isEmpty() || "false".equalsIgnoreCase(modeTest)) {
|
||||||
@ -116,7 +103,7 @@ public class ConfigureDb {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clear() throws IOException {
|
public static void clear() throws IOException {
|
||||||
LOGGER.info("Remove the test db");
|
LOGGER.info("Remove the test db");
|
||||||
removeDB();
|
removeDB();
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package test.atriasoft.karideo;
|
package test.atriasoft.karideo;
|
||||||
|
|
||||||
|
import org.atriasoft.archidata.exception.RESTErrorResponseException;
|
||||||
|
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
||||||
|
import org.atriasoft.archidata.tools.RESTApi;
|
||||||
|
import org.atriasoft.karideo.api.HealthCheck.HealthResult;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
@ -8,10 +12,6 @@ import org.junit.jupiter.api.Order;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.atriasoft.archidata.exception.RESTErrorResponseException;
|
|
||||||
import org.atriasoft.archidata.tools.ConfigBaseVariable;
|
|
||||||
import org.atriasoft.archidata.tools.RESTApi;
|
|
||||||
import org.atriasoft.karideo.api.HealthCheck.HealthResult;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -47,14 +47,15 @@ public class TestHealthCheck {
|
|||||||
@Test
|
@Test
|
||||||
// @RepeatedTest(10)
|
// @RepeatedTest(10)
|
||||||
public void checkHealthCheck() throws Exception {
|
public void checkHealthCheck() throws Exception {
|
||||||
final HealthResult result = api.get(HealthResult.class, "health_check");
|
final HealthResult result = api.request("health_check").get().fetch(HealthResult.class);
|
||||||
Assertions.assertEquals(result.value(), "alive and kicking");
|
Assertions.assertEquals(result.value(), "alive and kicking");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Order(2)
|
@Order(2)
|
||||||
@Test
|
@Test
|
||||||
public void checkHealthCheckWrongAPI() throws Exception {
|
public void checkHealthCheckWrongAPI() throws Exception {
|
||||||
Assertions.assertThrows(RESTErrorResponseException.class, () -> api.get(HealthResult.class, "health_checks"));
|
Assertions.assertThrows(RESTErrorResponseException.class,
|
||||||
|
() -> api.request("health_check_kaboom").get().fetch());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,8 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
}
|
}
|
||||||
}, [isPlaying, videoRef]);
|
}, [isPlaying, videoRef]);
|
||||||
|
|
||||||
const onAudioEnded = () => {
|
const onEnded = () => {
|
||||||
|
console.log('ended ...');
|
||||||
if (playMediaList.length === 0 || isNullOrUndefined(MediaOffset)) {
|
if (playMediaList.length === 0 || isNullOrUndefined(MediaOffset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -193,6 +194,7 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
videoRef.current.currentTime = newValue;
|
videoRef.current.currentTime = newValue;
|
||||||
};
|
};
|
||||||
const onPlay = () => {
|
const onPlay = () => {
|
||||||
|
console.log(`onPlay ...`);
|
||||||
if (!videoRef || !videoRef.current) {
|
if (!videoRef || !videoRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,6 +205,7 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onStop = () => {
|
const onStop = () => {
|
||||||
|
console.log(`onStop ...`);
|
||||||
if (!videoRef || !videoRef.current) {
|
if (!videoRef || !videoRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -310,6 +313,36 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const onError = (event) => {
|
||||||
|
const errorDetails: MediaError = event.target.error;
|
||||||
|
let errorMessage = 'An error occurred while trying to play the video.';
|
||||||
|
|
||||||
|
if (errorDetails) {
|
||||||
|
if (errorDetails.code === 1) {
|
||||||
|
errorMessage =
|
||||||
|
'The video cannot be played because the source is invalid or the format is unsupported.';
|
||||||
|
} else if (errorDetails.code === 2) {
|
||||||
|
errorMessage =
|
||||||
|
'The video cannot be played because the network error occurred.';
|
||||||
|
} else if (errorDetails.code === 3) {
|
||||||
|
errorMessage =
|
||||||
|
'The video cannot be played because of a decoding error.';
|
||||||
|
} else if (errorDetails.code === 4) {
|
||||||
|
errorMessage =
|
||||||
|
'The video cannot be played because of an unknown error.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//setError(errorMessage); // Mettre à jour l'état de l'erreur
|
||||||
|
console.error(
|
||||||
|
'Error code:',
|
||||||
|
errorDetails.code,
|
||||||
|
' ==> ',
|
||||||
|
errorMessage,
|
||||||
|
' // ',
|
||||||
|
errorDetails.message
|
||||||
|
);
|
||||||
|
};
|
||||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFullScreenChange = () => {
|
const handleFullScreenChange = () => {
|
||||||
@ -351,10 +384,6 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
height={isFullScreen ? '100%' : undefined}
|
height={isFullScreen ? '100%' : undefined}
|
||||||
width={isFullScreen ? '100%' : undefined}
|
width={isFullScreen ? '100%' : undefined}
|
||||||
marginX="auto"
|
marginX="auto"
|
||||||
// left={0}
|
|
||||||
// right={0}
|
|
||||||
// top={0}
|
|
||||||
// bottom={0}
|
|
||||||
>
|
>
|
||||||
<video
|
<video
|
||||||
height={isFullScreen ? '100%' : undefined}
|
height={isFullScreen ? '100%' : undefined}
|
||||||
@ -366,8 +395,11 @@ export const VideoPlayer = ({}: AudioPlayerProps) => {
|
|||||||
onTimeUpdate={onTimeUpdate}
|
onTimeUpdate={onTimeUpdate}
|
||||||
onDurationChange={onDurationChange}
|
onDurationChange={onDurationChange}
|
||||||
onLoadedMetadata={onChangeMetadata}
|
onLoadedMetadata={onChangeMetadata}
|
||||||
|
onError={onError}
|
||||||
autoPlay={true}
|
autoPlay={true}
|
||||||
onEnded={onAudioEnded}
|
onEnded={onEnded}
|
||||||
|
muted={true}
|
||||||
|
//preload="none"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
{(!isFullScreen || !isMobile || isRunning) && (
|
{(!isFullScreen || !isMobile || isRunning) && (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user